Last active
December 8, 2021 17:13
-
-
Save shammelburg/4ab6d7318157fedf3dbab015fda71ade to your computer and use it in GitHub Desktop.
express-graphql-api
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const jwt = require('jsonwebtoken'); | |
const authMiddleware = (req, res, next) => { | |
const authHeader = req.get('Authorization') | |
if (!authHeader) { | |
req.error = "No authentication header found." | |
req.isAuth = false | |
return next() | |
} | |
let decoded | |
try { | |
const token = authHeader.split(' ')[1] | |
decoded = jwt.verify(token, process.env.JWT_KEY) | |
} catch (error) { | |
req.isAuth = false | |
req.error = error.message | |
return next() | |
} | |
if (!decoded) { | |
req.isAuth = false | |
req.error = "Unable to decode jwt" | |
return next() | |
} | |
req.isAuth = true | |
req.user = decoded | |
req.error = null | |
next() | |
} | |
module.exports = authMiddleware |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const DataLoader = require('dataloader') | |
const students = [ | |
{ id: 1, studentName: 'Sander' }, | |
{ id: 2, studentName: 'Mark' }, | |
{ id: 3, studentName: 'Greg' }, | |
{ id: 4, studentName: 'Chris' }, | |
] | |
const classes = [ | |
{ id: 1, className: 'a0', studentId: 1 }, | |
{ id: 2, className: 'a1', studentId: 1 }, | |
{ id: 3, className: 'a2', studentId: 2 }, | |
{ id: 4, className: 'a3', studentId: 1 }, | |
{ id: 5, className: 'a4', studentId: 3 }, | |
{ id: 6, className: 'a5', studentId: 4 }, | |
{ id: 7, className: 'a6', studentId: 1 }, | |
{ id: 8, className: 'a7', studentId: 3 }, | |
{ id: 9, className: 'a8', studentId: 3 }, | |
] | |
const studentRepo = () => { | |
return { | |
getClassesByStudentId: ids => { | |
return classes.filter(c => ids.includes(c.studentId)) | |
} | |
} | |
} | |
const batchStudentIds = async ids => { | |
// Logging batched student ids | |
console.log(ids) | |
// Get all classes by student id | |
const classes = studentRepo().getClassesByStudentId(ids) | |
// Now group the classes by ids as they're passed into the batch function | |
const groupedById = ids.map(id => classes.filter(c => c.studentId === id)) | |
return Promise.resolve(groupedById) | |
} | |
const dataLoader = new DataLoader(batchStudentIds); | |
const result = Promise.all(students.map(s => dataLoader.load(s.id))) | |
result.then(console.log) | |
// Result | |
// [ | |
// [ | |
// { id: 1, className: 'a0', studentId: 1 }, | |
// { id: 2, className: 'a1', studentId: 1 }, | |
// { id: 4, className: 'a3', studentId: 1 }, | |
// { id: 7, className: 'a6', studentId: 1 } | |
// ], | |
// [{ id: 3, className: 'a2', studentId: 2 }], | |
// [ | |
// { id: 5, className: 'a4', studentId: 3 }, | |
// { id: 8, className: 'a7', studentId: 3 } | |
// ], | |
// [{ id: 6, className: 'a5', studentId: 4 }] | |
// ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express') | |
const { graphqlHTTP } = require('express-graphql') | |
// Subscriptions | |
const ws = require('ws') | |
const { useServer } = require('graphql-ws/lib/use/ws'); | |
const { execute, subscribe } = require('graphql'); | |
const app = express() | |
const schema = require('./src/schema') | |
app.get('/', (req, res) => res.send('GraphQL Server is running')) | |
app.use('/graphql', graphqlHTTP(req => ({ | |
schema, | |
graphiql: { | |
headerEditorEnabled: true | |
}, | |
// ... | |
}))) | |
const server = app.listen(4000, () => { | |
console.log(`GraphQL Server running on http://localhost:4000/graphql`) | |
// create and use the websocket server | |
const wsServer = new ws.Server({ | |
server, | |
path | |
}); | |
useServer( | |
{ | |
schema, | |
execute, | |
subscribe, | |
onConnect: (ctx) => { | |
console.log('Connect'); | |
}, | |
onSubscribe: (ctx, msg) => { | |
console.log('Subscribe'); | |
}, | |
onNext: (ctx, msg, args, result) => { | |
console.debug('Next'); | |
}, | |
onError: (ctx, msg, errors) => { | |
console.error('Error'); | |
}, | |
onComplete: (ctx, msg) => { | |
console.log('Complete'); | |
}, | |
}, | |
wsServer | |
); | |
console.log(`WebSockets listening on ws://localhost:4000/subscriptions`) | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { makeExecutableSchema } = require('graphql-tools') | |
const merge = require('lodash.merge'); | |
const userSchema = require('./user') | |
const roleSchema = require('./role') | |
// Multiple files to keep your project modularised | |
const schema = makeExecutableSchema({ | |
typeDefs: [ | |
userSchema.typeDefs, // First defines the type Query | |
roleSchema.typeDefs, // Others extends type Query | |
], | |
resolvers: merge( | |
userSchema.resolvers, | |
roleSchema.resolvers, | |
) | |
}) | |
module.exports = schema |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const users = require('../data/users.json') | |
module.exports = (context, fieldName) => { | |
// These fields require the requester to be authenticated | |
const fieldsThatRequireAuth = [ | |
'createUser' | |
] | |
if (fieldsThatRequireAuth.includes(fieldName) && !context.isAuth) { | |
// if not authenticated | |
} | |
// Authorization for user with "Admin" role only | |
const hasAdminRole = context.user && context.user.roles.includes('Admin') | |
return { | |
// users query | |
getUsers: () => users, | |
// createUser mutation | |
insertUser: user => { | |
if (!hasAdminRole) return new Error(`Access denied to "${fieldName}" field.`) | |
// insert... | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const pubsub = require('../../helpers/pubsub') | |
const NEW_USER_EVENT = 'NEW_USER_EVENT' | |
const resolvers = { | |
Query: { | |
// ... | |
}, | |
Mutation: { | |
createUser: (root, { input }, context, info) => { | |
const id = userRepo(context, info.fieldName).insertUser(input) | |
pubsub.publish(NEW_USER_EVENT, { newUser: { ...input, id } }) | |
return { ...input, id } | |
}, | |
}, | |
Subscription: { | |
newUser: { | |
subscribe: (parent, args, context) => pubsub.asyncIterator(NEW_USER_EVENT) | |
} | |
} | |
} | |
module.exports = resolvers |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment