Skip to content

Instantly share code, notes, and snippets.

@jmcneese
Last active April 5, 2018 03:16
Show Gist options
  • Save jmcneese/b646066ea260a833d38f744373247981 to your computer and use it in GitHub Desktop.
Save jmcneese/b646066ea260a833d38f744373247981 to your computer and use it in GitHub Desktop.
Relay/GraphQL signIn/signOut mutation
relay.commitUpdate(
new SignInMutation({ username, password, viewer }), {
onSuccess: ({ signIn: { viewer: { token } } }) => {
// do something with the returned token, set it in localStorage or cookies, or whatever
},
onFailure: (transaction) => {
// handle the failed mutation
},
}
);
import cors from 'cors';
import express from 'express';
import expressGraphql from 'express-graphql';
import schema from 'schema';
const handler = expressGraphql((req) => {
let token = null;
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
// grab the token out of the headers, if present
token = req.headers.authorization.split(' ')[1];
} else if (req.cookies && req.cookies.token) {
// use the session cookie, for returning users
token = req.cookies.token;
}
// before you set the token you could decode and verify the JWT here, or do db lookups, or whatever before setting the context
return {
schema,
context: {
token,
},
};
));
const graphqlRouter = express.Router();
graphqlRouter.use(cors());
graphqlRouter.use('/', handler);
export default graphqlRouter;
import { GraphQLNonNull, GraphQLString } from 'graphql';
import { mutationWithClientMutationId } from 'graphql-relay';
import ViewerType from 'schema/types/viewer';
export default mutationWithClientMutationId({
name: 'SignIn',
inputFields: () => ({
username: {
type: new GraphQLNonNull(GraphQLString),
description: 'The username of the viewer',
},
password: {
type: new GraphQLNonNull(GraphQLString),
description: 'The password of the viewer',
},
}),
outputFields: () => ({
viewer: {
type: ViewerType,
description: 'The viewer who is logging in',
},
}),
mutateAndGetPayload: ({ username, password }, context) => {
// do whatever with the input to authenticate the user and generate a JWT, and set the token to context for use in other resolvers
const token = 'token here';
context.token = token;
return {
viewer: {
loggedIn: true,
token,
},
};
},
});
import Relay from 'react-relay/classic';
// the SignOut mutation looks almost the same as this
export default class SignInMutation extends Relay.Mutation {
static fragments = {
viewer: () => Relay.QL`
fragment on Viewer {
id
}
`,
};
getMutation = () => Relay.QL`
mutation { signIn }
`;
getVariables = () => ({
username: this.props.username,
password: this.props.password,
});
getFatQuery = () => Relay.QL`
fragment on SignInPayload {
viewer {
loggedIn
token
# other stuff that might change when the viewer logs in
}
}
`;
getConfigs = () => [
{
// just to be sure the token comes back in the fat query
type: 'REQUIRED_CHILDREN',
children: [
Relay.QL`
fragment on SignInPayload {
viewer {
token
}
}
`,
],
},
{
type: 'FIELDS_CHANGE',
fieldIDs: {
viewer: this.props.viewer.id,
},
},
];
}
import { mutationWithClientMutationId } from 'graphql-relay';
import ViewerType from 'schema/types/viewer';
export default mutationWithClientMutationId({
name: 'SignOut',
outputFields: () => ({
viewer: {
type: ViewerType,
description: 'The viewer who is signing out',
},
}),
mutateAndGetPayload: (args, context) => {
context.token = null;
return {
viewer: {
loggedIn: false,
token: null,
},
};
},
});
import { GraphQLBoolean, GraphQLObjectType, GraphQLNonNull, GraphQLString } from 'graphql';
import jwt from 'jsonwebtoken';
import { nodeInterface } from 'schema/lib/node';
import PersonType from 'schema/types/person';
/**
* Viewer type
*/
export default new GraphQLObjectType({
name: 'Viewer',
description: 'The viewer of the application',
interfaces: () => [nodeInterface],
fields: () => ({
id: globalIdField('viewers'),
loggedIn: {
type: new GraphQLNonNull(GraphQLBoolean),
description: 'Whether the viewer is logged in',
resolve: (root, args, { token }) => {
// no token, no person
if (!token) {
return Promise.resolve(false);
}
try {
// check token validity, and decode if valid
const decoded = jwt.verify(token, process.env.SECRET);
return Promise.resolve(true);
} catch (e) {
return Promise.resolve(false);
}
},
},
person: {
type: PersonType,
description: 'The person record of the viewer',
resolve: (root, args, { db, token }) => {
// no token, no person
if (!token) {
return Promise.resolve({});
}
try {
// check token validity, and decode if valid
const decoded = jwt.verify(token, process.env.SECRET);
// get person from db
return db.getPersonById(decoded.sub);
} catch (e) {
// invalid token, no person and destroy token
context.token = null;
return Promise.resolve({});
}
},
},
}),
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment