GraphQL middleware
This is a small library that makes it possible to apply middleware for your resolvers using specific rules. It can be used for logging, collecting metrics, to cache, to modify the parameters or response, for authorization and access control.
It's built to be used with express-graphql but it can also be used with any other library, as well as stand-alone.
Installation
npm i graphql-wrapper --save
Basic usage
// ...const allowOnly cache logs csrfValidation = const wrapper = /*** * @param* @param* @param * @param * @param * @param * @param* @param* @param* @return [any]*/ { console return // it is not required to pass arguments to next(), } // if there are no arguments passed, original args will be used const app = app app
Contribution
If you found this library useful, please feel free to contribute or make a feature request.
Examples
You can find all examples in /examples folder
To launch example, you need to execute the following command:
node ./examples/example-<name>.js
It will launch test graphql-express server at port 4000 with enabled graphiql playground. Check "Docs" sidebar to see all available fields.
Logging example
This example shows how you can add logging to your resolvers without modifying them directly
{ const info = args let path = infopathkey forlet current = infopathprev; current; current = currentprev path = `.` const startTs = Date const value = await console return value} app app
Query:
{ posts { id } todos { id } user(id: 1) { albums { id photos { id } } }}
Command line output:
Filed: "Query.user"; path:"user"; execution time: 101msFiled: "User.albums"; path:"user.albums"; execution time: 71msFiled: "Query.todos"; path:"todos"; execution time: 195msFiled: "Query.posts"; path:"posts"; execution time: 239msFiled: "Album.photos"; path:"user.albums.2.photos"; execution time: 42msFiled: "Album.photos"; path:"user.albums.0.photos"; execution time: 45msFiled: "Album.photos"; path:"user.albums.1.photos"; execution time: 45msFiled: "Album.photos"; path:"user.albums.3.photos"; execution time: 44msFiled: "Album.photos"; path:"user.albums.7.photos"; execution time: 44msFiled: "Album.photos"; path:"user.albums.4.photos"; execution time: 48msFiled: "Album.photos"; path:"user.albums.9.photos"; execution time: 44msFiled: "Album.photos"; path:"user.albums.5.photos"; execution time: 50msFiled: "Album.photos"; path:"user.albums.8.photos"; execution time: 49msFiled: "Album.photos"; path:"user.albums.6.photos"; execution time: 53ms
Profiling example
/examples/example-profiling.js
Example of how you can profile execution of GraphQL request. This middleware will print chart that describes order, execution time, and sequence of every resolver that was involved in particular request. It also shows which requests were resolved concurrently and which ones — sequentially.
const wrapper metricsMiddleware express: chartMiddleware = const schema = const app = appapp app
Query:
{ posts { id } todos { id } user(id: 1) { albums { id photos { id } } }}
Command line output:
posts [----------------- ]ts: 76mstodos [---------------- ]ts: 50msuser [--------------- ]ts: 44ms └user.albums [ ------------------- ]ts: 57ms ├user.albums.0.photos[ ----------------]ts: 52ms ├user.albums.1.photos[ --------------- ]ts: 45ms ├user.albums.2.photos[ --------- ]ts: 30ms ├user.albums.3.photos[ -------- ]ts: 26ms ├user.albums.4.photos[ ---------- ]ts: 30ms ├user.albums.5.photos[ ----------- ]ts: 34ms ├user.albums.7.photos[ ---------- ]ts: 30ms ├user.albums.6.photos[ ----------- ]ts: 35ms ├user.albums.8.photos[ ------------- ]ts: 38ms └user.albums.9.photos[ -------------]ts: 41ms Total execution time: 154ms
Access control example
/examples/example-hide-fields.js
This example shows how you can control access to specific fields in your GraphQL schema
const MY_USER_ID = 1const IS_ADMIN = false // hide field based on arguments { const id = args return id === MY_USER_ID ? : null} // hide field based on app inner state { return IS_ADMIN? : null} const app = app app
Query:
{ me: user(id: 1) { id username address { city zipcode } } otherUser: user(id: 2) { id username address { city zipcode } } otherUserPost: post(id: 20) { id title user { id username address { city zipcode } } } albums { id title }}
Here you can see that address
field is hidden from all users who are not "me".
You can also see that albums
field is restricted
Cache example
Basic example of how you can add cache for specific fields. In this example I have added cache middleware
for Album.photos
field.
const TTL = 5000const app = app app /*functions below created with sole purpose of the demo, do not use them in actual applications */ { const hash = crypto hash return hash} { const dictionary = return async { const hash = const ts = Date if dictionary && ts - dictionaryts < ttl return dictionaryvalue const value = await dictionary return value }}
{ user(id: 1) { name albums { title photos { id } } }}
I will use profiling tool from previous examples to show the difference between non-cached and cached request.
non-cached request
user [-------------- ]ts: 23ms └user.albums [ --------- ]ts: 15ms ├user.albums.0.photos[ ----------------- ]ts: 28ms ├user.albums.1.photos[ ------------------ ]ts: 30ms ├user.albums.2.photos[ ------------------ ]ts: 31ms ├user.albums.3.photos[ -------------------------]ts: 41ms ├user.albums.5.photos[ ------------------ ]ts: 31ms ├user.albums.4.photos[ --------------------- ]ts: 35ms ├user.albums.6.photos[ ------------------- ]ts: 31ms ├user.albums.7.photos[ ------------------- ]ts: 31ms ├user.albums.9.photos[ ----------------------]ts: 37ms └user.albums.8.photos[ ----------------------]ts: 38ms Total execution time: 83ms
cached request
user [-------------------------- ]ts: 17ms └user.albums [ ------------------------]ts: 16ms ├user.albums.0.photos[ ]ts: 0ms ├user.albums.1.photos[ ]ts: 0ms ├user.albums.2.photos[ ]ts: 0ms ├user.albums.3.photos[ ]ts: 0ms ├user.albums.4.photos[ ]ts: 0ms ├user.albums.5.photos[ ]ts: 0ms ├user.albums.6.photos[ ]ts: 0ms ├user.albums.7.photos[ ]ts: 0ms ├user.albums.8.photos[ ]ts: 0ms └user.albums.9.photos[ ]ts: 0ms Total execution time: 33ms
Todo
- Tests
- Better documentation