I want to pass the express req and res object to my context because i want to use express-sessions to do session-based-auth because the default requests from the context does not know about sessions. Here is what I have tried
app.use("/graphql", (req, res) => {
return createYoga({
context: ({ params }) => {
return {
req,
params,
res,
prisma,
redis: redisClient,
};
},
graphiql: true,
landingPage: false,
cors: false,
schema,
});
});
But if i try this it seems like the request is not going through.
According to the docs, while running in node.js and express runtime, the context will automatically have the request and response.
Server Context
When creating the server instance, GraphQL Yoga accepts an additional
object from your base server framework or library that will be merged
with the default context. Node.js (standalone, express and Next.js
etc.)
If you are using GraphQL Yoga as a standalone server with createServer
from the http(s) module or exposing it as a middleware as we show in
the express or Next.js integration recipes.
req - Node.js IncomingMessage object
res - Node.js ServerResponse object
The req and res objects are added to the initial context object.
const serverContext = { ...defaultContext, req, res }
Thus, when using #graphql-yoga/node, it is possible to access
context.req and context.res within the GraphQL resolvers or the user
context factory function.
However, we recommend avoiding using context.req and context.res
wherever possible and instead favor context.request, as it is more
future-proof and platform independent (as Node.js HTTP servers adopt
the Fetch Response API).
I did try this out and the context does have the req and res objects.
Following apollo documentation, I'm unable to get data from memcached with telnet commands. It seems like the data is cached because when I resend the same request the datasource doesn't call the APIs.
const server = new ApolloServer({
typeDefs,
resolvers,
cache: new MemcachedCache(
'127.0.0.1:11211',
{ retries: 10, retry: 10000 }, // Options
),
dataSources: () => ({
moviesAPI: new MoviesAPI(),
}),
});
I tried a manual implementation of memcached inside a dummy project and I was able to set/get data in the telnet interface.
Is the memcached queried by graphql or do we need to do it manually in the resolvers ?
Any help is welcome
The option to pass a cache into ApolloServer's initialization is optionl. With that being said , the cache highly downsized the speed of your database fetches.You have to pass the given cache in the initialization of any dataSource class, like this:
class DogCustomDataSource extends CustomDataSource{
constructor(models){
....
}
initialize({context,cache}){
this.context=context
this.cache=cache
}
}
Yes, you are right , subsequently, you have to set and delete objects to the cache in the methods of your dataSource, like this:
async get(id,{ttlInSeconds}={}){
.....
await this.cache.set("yourKey","yourValue",{ttl:ttlInSeconds})
//or
await this.cache.del("yourKey")
.....
}
I am implementing a query which should serve some fields in the response depending on user login status.
To be specific, I want to get "pointRate" field only if $authenticationToken is passed & would want to avoid passing $authenticated in below query. The reason I want to avoid sending $authenticated is client can do mistake by sending $authenticated = true but $authenticationToken = null.
query ToDoQuery($authenticationToken: String, $authenticated: Boolean!) {
pointRate(accessToken: $authenticationToken) #include(if: $authenticated) {
status
}
}
So, Actually you want to do that
i) if $authenticationToken is passed, you want to get "pointRate".
ii) and you also want to avoid passing $authenticated in subsequent
queries. Because you are concern about your clients who can make some
mistake like sending authenticated is true where authentication token
was null.
So in generally I want to answer that if you want to handle authentication by yourself using GraphQL, at first you have to create a token, then you have to pass the token in every request or with subsequent requests. Otherwise it is not possible. Because sensitive data's will not be provided without authentication.
On the other hand, you can use session auth. You can access every data until session is closed.
If it is not satisfactory, You can read the following brief description with a scenerio like yours. I also tried to accumulate some related sample solutions for better understanding, it may clarify you more.
As GraphQL API is completely public, you can make authentication by two ways.
Let the web server (e.g. express or nginx) take care of authentication.
Handle authentication in GraphQL itself.
If you do authentication in the web server, you can use a standard auth package (e.g. passport.js for express) and many existing authentication methods will work out of the box. You can also add and remove methods at your liking without modifying the GraphQL schema.
If you’re implementing authentication yourself, do the followings
Make sure to never store passwords in clear text or a MD5 or SHA-256
hash
Use something like bcrypt
Make sure to not store your session tokens as-is on the server, you
should hash them first
You can write a login method, which sets the context. Since mutations
are executed one after the other and not in parallel, you can be sure
the context is set after the login mutation:
mutation {
loginWithToken(token: "6e37a03e-9ee4-42fd-912d-3f67d2d0d852"),
do_stuff(greeting: "Hello", name: "Tom"),
do_more_stuff(submarine_color: "Yellow")
}
Instead of passing in the token via header or query parameter (like JWT, OAuth, etc), we make it part of the GraphQL query. Your schema code can parse the token directly using the JWT library itself or another tool.
Remember to always use HTTPS when passing sensitive information :)
As parallel execution is an important for performance. and mutation and queries are executed serially, in the order given.
So in most cases It is preferred to handle authentication in the web server. It’s not only more generic, but also more flexible.
Scenerio:
First go through the followings
import jwt from'express-jwt';
import graphqlHTTP from'express-graphql';
import express from'express';
import schema from'./mySchema';
const app = express();
app.use('/graphql', jwt({
secret: 'shhhhhhared-secret',
requestProperty: 'auth',
credentialsRequired: false,
}));
app.use('/graphql', function(req, res, done) {
const user = db.User.get(req.auth.sub);
req.context = {
user: user,
}
done();
});
app.use('/graphql', graphqlHTTP(req => ({
schema: schema,
context: req.context,
})
));
If you check in the above section, you will get that API is not secure at all. It might try to verify the JWT but if the JWT doesn’t exist or is invalid, the request will still pass through (see credentialsRequired: false). Why? We have to allow the request to pass through because if we blocked it we would block the entire API. That means, our users wouldn’t even be able to call a loginUser mutation to get a token to authenticate themselves.
Solution#1:
Barebone example using Authenticate resolvers, not endpoints.
import { GraphQLSchema } from 'graphql';
import { Registry } from 'graphql-helpers';
// The registry wraps graphql-js and is more concise
const registry = new Registry();
registry.createType(`
type User {
id: ID!
username: String!
}
`;
registry.createType(`
type Query {
me: User
}
`, {
me: (parent, args, context, info) => {
if (context.user) {
return context.user;
}
throw new Error('User is not logged in (or authenticated).');
},
};
const schema = new GraphQLSchema({
query: registry.getType('Query'),
});
By the time the request gets to our Query.me resolver, the server middleware has already tried to authenticate the user and fetch the user object from the database. In our resolver, we can then check the graphql context for the user (we set the context in our server.js file) and if one exists then return it else throw an error.
Note: you could just as easily return null instead of throwing an error and I would actually recommend it.
Solution#2:
Use functional Composition(middleware based) of express-graphql
import { GraphQLSchema } from 'graphql';
import { Registry } from 'graphql-helpers';
// See an implementation of compose https://gist.github.com/mlp5ab/f5cdee0fe7d5ed4e6a2be348b81eac12
import { compose } from './compose';
const registry = new Registry();
/**
* The authenticated function checks for a user and calls the next function in the composition if
* one exists. If no user exists in the context then an error is thrown.
*/
const authenticated =
(fn: GraphQLFieldResolver) =>
(parent, args, context, info) => {
if (context.user) {
return fn(parent, args, context, info);
}
throw new Error('User is not authenticated');
};
/*
* getLoggedInUser returns the logged in user from the context.
*/
const getLoggedInUser = (parent, args, context, info) => context.user;
registry.createType(`
type User {
id: ID!
username: String!
}
`;
registry.createType(`
type Query {
me: User
}
`, {
me: compose(authenticated)(getLoggedInUser)
};
const schema = new GraphQLSchema({
query: registry.getType('Query'),
});
The above code will work exactly the same as the first snippet. Instead of checking for the user in our main resolver function, we have created a highly reusable and testable middleware function that achieves the same thing. The immediate impact of this design may not be apparent yet but think about what would happen if we wanted to add another protected route as well as log our resolver running times. With our new design its as simple as:
const traceResolve =
(fn: GraphQLFieldResolver) =>
async (obj: any, args: any, context: any, info: any) => {
const start = new Date().getTime();
const result = await fn(obj, args, context, info);
const end = new Date().getTime();
console.log(`Resolver took ${end - start} ms`);
return result;
};
registry.createType(`
type Query {
me: User
otherSecretData: SecretData
}
`, {
me: compose(traceResolve, authenticated)(getLoggedInUser)
otherSecretData: compose(traceResolve, authenticated)(getSecretData)
};
Using this technique will help you build more robust GraphQL APIs. Function composition is a great solution for authentication tasks but you can also use it for logging resolvers, cleaning input, massaging output, and much more.
Solution#3:
A decent solution is to factor out data fetching into a separate layer and do the authorization check there.
Below is an example that follows the principles outlined above. It’s for a query that fetches all todo lists that a user can see.
For the following query,
{
allLists {
name
}
}
Don’t do this:
//in schema.js (just the essential bits)
allLists: {
resolve: (root, _, ctx) => {
return sql.raw("SELECT * FROM lists WHERE owner_id is NULL or owner_id = %s", ctx.user_id);
}
}
Instead, I suggest you do this:
// in schema.js (just the essential bits)
allLists: {
resolve: (root, _, ctx) => {
//factor out data fetching
return DB.Lists.all(ctx.user_id)
.then( lists => {
//enforce auth on each node
return lists.map(auth.List.enforce_read_perm(ctx.user_id) );
});
}
}
//in DB.js
export const DB = {
Lists: {
all: (user_id) => {
return sql.raw("SELECT id FROM lists WHERE owner_id is NULL or owner_id = %s, user_id);
}
}
}
//in auth.js
export const auth = {
List: {
enforce_read_perm: (user_id) => {
return (list) => {
if(list.owner_id !== null && list.owner_id !== user_id){
throw new Error("User not authorized to read list");
} else {
return list;
}
}
}
}
You may think that the DB.Lists.all function is already enforcing permissions, but the way I see it it’s just trying not to fetch too much data, the permissions themselves are enforced not on each node separately. That way you have the auth checks in one place and can be sure that they will be applied consistently, even if you fetch data in many different places.
Solution#4:
Auth flow can be done in many different ways.
i) basic auth,
ii) session auth, or
iii) token auth.
As your issue is according to token auth, I would like to meet you with Scaphold which one uses token authentication. Everything we do, whether it be logging a user into Scaphold or logging your user into your app, we use tokens to manage a user's auth status. The auth flow works like this:
a) User logs in with username and password.
b) The GraphQL server verifies the user in the database against his / her hashed password.
c) If successful, the server returns a JSON Web Token (JWT) that is a Base64 encoded token with an expiration date. This is the authentication token.
d) To use the authentication token, your future requests should include the authentication token in the header as
{ Authorization: 'Bearer' + [Auth_Token] }
Now, each time the server (perhaps Node Express) sees the token in the header, it will parse out the token, verify it, and in the GraphQL world, save the identified user in the context for use in the rest of the application. The user is now logged in.
For more, you can learn more about #include in this tutorial: https://github.com/mugli/learning-graphql/blob/master/4.%20Querying%20with%20Directives.md#include
For learning step by step graphql authentication, you can go through this tutorial: GraphQL Authentication
Resource Link:
Authentication with
GraphQL
A guide to authentication in
GraphQL
Best practices for GraphQL
security
I don't think this is possible since you cannot convert an (empty) String to a Boolean in GraphQL.
Also, some advice from the official GraphQL docs:
Delegate authorization logic to the business logic layer
#include
GraphQL queries are a powerful way to declare data in your application.
The include directive, allows us to include fields based on some condition.
query myAwesomeQuery($isAwesome: Boolean) {
awesomeField #include(if: $isAwesome)
}
Note. #skip always has higher precedence than #include.
I'm using Inversify.JS in a project with Express. I would like to create a connection to a Neo4J Database, and this process has two objets:
The driver object - Could be shared across the application and created one time only
The session object - Each HTTP request should create a session against the driver, whose lifecyle is the same as the http request lifecycle (as long as the request ends, the connection is destroyed)
Without Insersify.JS, this problem is solved using a simple algorithm:
exports.getSession = function (context) { // 'context' is the http request
if(context.neo4jSession) {
return context.neo4jSession;
}
else {
context.neo4jSession = driver.session();
return context.neo4jSession;
}
};
(example: https://github.com/neo4j-examples/neo4j-movies-template/blob/master/api/neo4j/dbUtils.js#L13-L21)
To create a static dependency for the driver, I can inject a constant:
container.bind<DbDriver>("DbDriver").toConstantValue(new Neo4JDbDriver());
How can I create a dependency instantiated only once per request and retrieve them from the container?
I suspect I must invoke the container on a middleware like this:
this._express.use((request, response, next) => {
// get the container and create an instance of the Neo4JSession for the request lifecycle
next();
});
Thanks in advance.
I see two solutions to your problem.
use inRequestScope() for DbDriver dependency. (available since 4.5.0 version). It will work if you use single composition root for one http request. In other words you call container.get() only once per http request.
create child container, attach it to response.locals._container and register DbDriver as singleton.
let appContainer = new Container()
appContainer.bind(SomeDependencySymbol).to(SomeDependencyImpl);
function injectContainerMiddleware(request, response, next) {
let requestContainer = appContainer.createChildContainer();
requestContainer.bind<DbDriver>("DbDriver").toConstantValue(new Neo4JDbDriver());
response.locals._container = requestContainer;
next();
}
express.use(injectContainerMiddleware); //insert injectContainerMiddleware before any other request handler functions
In this example you can retrieve DbDriver from response.locals._container in any request handler/middleware function registered after injectContainerMiddleware and you will get the same instance of DbDriver
This will work, but I'm not sure how performant it is. Additionally I guess that you may need to somehow dispose requestContainer (unbind all dependencies and remove reference to parent container) after http request is done.
I am trying to create a route which will perform some CRUD operations on DynamoDB.
At high level , it can be understood as :
The node js server application is running .(i.e. command 'node server.js' is being triggered)
The user uses POSTMAN of chrome browser to do route requests.
The user does a GET request for 'http://localhost:8080/listtablesofdynamodb'.
The specific route connected with this url gets hit which should do dynamodb specific activity. (like connecting to dynamodb ,fetching table names and showing it in callback method.)
the reason I am asking this question is because I could not find any relevant tutorial of how to do dynamodb activity by using express js of node. All I could find is console applications on aws website which seems not useful for me.
Any kind of help is highly appreciated.
Access key required
All you need to d is make a DynamoDB object to connect too
var ddb = require('dynamodb').ddb({ accessKeyId: '< your_access_key_id >', secretAccessKey: '< your_secret_access_key >' });
put this under your require statements, turn on your server. Then you can just fill out the routes to do the CRUD operations you need.
To test it use
ddb.listTables({}, function(err, res) {console.log(res);});
This will list all the tables in your db.
for full source check here
Best of luck
fortunately, I could manage to use aws-sdk in my route. the solution have two stages:
Run the code in your aws account's EC2 instance and attach an IAM role which allows the ec2 instance to talk to dynamodb. (in this way you don't need to hard-code access key in your code) see this article.
can take reference of the below code for initial code scaffolding.
`
var express = require('express');
var router = express.Router();
var AWS = require("aws-sdk");
AWS.config.update({
region: "us-west-2",
endpoint: "dynamodb endpoint specific to your aws account"
});
var dynamodb = new AWS.DynamoDB();
var params = {
ExclusiveStartTableName: "stringvalue",
Limit: 10
};
/* GET users listing. */
router.get('/', function (req, res) {
console.log("entered into dynadb route");
dynamodb.listTables(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else {
res.send(data);
}
});
});
module.exports = router;
`