From 73ce9c68881cdd81e8d040ea60b43c2775a4cc47 Mon Sep 17 00:00:00 2001 From: dalisoft Date: Fri, 28 May 2021 20:21:02 +0500 Subject: [PATCH] feat(@nanoexpress/middleware-graphql): added `AOT Jit` optimization and performance got up to 25-times on different cases fix(@nanoexpress/middleware-graphql): fixes example to use `subscription` on both **schema** mode fix(@nanoexpress/middleware-graphql): somehow `subscription` itself worked --- packages/graphql/examples/graphql-schema.js | 7 +-- packages/graphql/examples/hello.js | 2 +- packages/graphql/examples/package.json | 2 +- packages/graphql/graphql.es.js | 57 +++++++++++++-------- packages/graphql/package.json | 3 +- yarn.lock | 50 +++++++++++++++++- 6 files changed, 92 insertions(+), 29 deletions(-) diff --git a/packages/graphql/examples/graphql-schema.js b/packages/graphql/examples/graphql-schema.js index b36f951..8e17b6b 100644 --- a/packages/graphql/examples/graphql-schema.js +++ b/packages/graphql/examples/graphql-schema.js @@ -10,12 +10,13 @@ const hello = { const greetings = { type: graphqlExports.GraphQLString, - resolve: async function* sayHiIn5Languages() { + subscribe: async function* sayHiIn5Languages() { // eslint-disable-next-line no-restricted-syntax for (const hi of ['Hi', 'Bonjour', 'Hola', 'Ciao', 'Zdravo']) { - yield { greetings: hi }; + yield hi; } - } + }, + resolve: (payload) => payload }; const schema = new graphqlExports.GraphQLSchema({ diff --git a/packages/graphql/examples/hello.js b/packages/graphql/examples/hello.js index 7bd82fa..9fbe5fd 100644 --- a/packages/graphql/examples/hello.js +++ b/packages/graphql/examples/hello.js @@ -10,6 +10,6 @@ const app = nanoexpress(); app.use(bodyParser()); app.post('/graphql', graphql(graphqlSchema)); -app.ws('/graphql', makeBehavior(graphqlSchema)); +app.ws('/graphql', makeBehavior({ schema: graphqlSchema })); app.listen(4000); diff --git a/packages/graphql/examples/package.json b/packages/graphql/examples/package.json index d94e457..01b5952 100644 --- a/packages/graphql/examples/package.json +++ b/packages/graphql/examples/package.json @@ -2,7 +2,7 @@ "type": "module", "dependencies": { "@nanoexpress/middleware-body-parser": "^1.2.1", - "@nanoexpress/middleware-graphql": "^1.0.2", + "@nanoexpress/middleware-graphql": "file:..", "graphql": "^15.5.0", "graphql-ws": "^4.5.1", "nanoexpress": "^5.0.4" diff --git a/packages/graphql/graphql.es.js b/packages/graphql/graphql.es.js index 7ebe885..a6a2869 100644 --- a/packages/graphql/graphql.es.js +++ b/packages/graphql/graphql.es.js @@ -1,4 +1,5 @@ import graphqlExports from 'graphql'; +import { compileQuery } from 'graphql-jit'; /** * @param schema - Please see [here](https://graphql.org/graphql-js/type/#graphqlschema) @@ -12,6 +13,9 @@ import graphqlExports from 'graphql'; */ export default function graphql(schema, root) { + const cache = {}; + // eslint-disable-next-line @typescript-eslint/naming-convention + const jitOptions = { customJSONSerializer: true }; return async function graphqlHandler(req, res) { const { headers, body } = req; @@ -19,36 +23,45 @@ export default function graphql(schema, root) { const contentType = headers['content-type']; res.writeHeader('content-type', 'application/json'); - let graphqlQuery = ''; + let query; let variables; let operationName; - if (typeof body === 'object' && body) { - graphqlQuery = body.query || body.mutation; + let document; + let compiled; + + if ( + contentType === 'application/graphql' || + contentType === 'text/graphql' + ) { + query = body; + } else { + query = body.query || body.mutation; variables = body.variables; operationName = body.operationName; - } else if (typeof body === 'string') { - if ( - contentType === 'application/graphql' || - contentType === 'text/graphql' - ) { - graphqlQuery = body; - } else { - return res.end( - '{"status":"error","message":"Please, make sure, your provided data are correct"}' - ); - } + } + + if (!query) { + return res + .status(400) + .send({ status: 'error', message: 'Invalid query' }); + } + + compiled = cache[query]; + + if (!compiled) { + document = graphqlExports.parse(query); + cache[query] = compileQuery( + schema, + document, + operationName, + jitOptions + ); + compiled = cache[query]; } const context = { req, res }; - const response = await graphqlExports.graphql( - schema, - graphqlQuery, - root, - context, - variables, - operationName - ); + const response = compiled.query(root, context, variables); if (response) { return res.send(response); diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 56721e5..0e9d8d0 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -31,6 +31,7 @@ }, "homepage": "https://github.com/nanoexpress/middlewares#readme", "dependencies": { - "graphql": "^15.5.0" + "graphql": "^15.5.0", + "graphql-jit": "^0.5.1" } } diff --git a/yarn.lock b/yarn.lock index aedbd86..05714e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3165,6 +3165,15 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stringify@^1.13.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-1.21.0.tgz#51bc8c6d77d8c7b2cc7e5fa754f7f909f9e1262f" + integrity sha512-xY6gyjmHN3AK1Y15BCbMpeO9+dea5ePVsp3BouHCdukcx0hOHbXwFhRodhcI0NpZIgDChSeAKkHW9YjKvhwKBA== + dependencies: + ajv "^6.11.0" + deepmerge "^4.2.2" + string-similarity "^4.0.1" + fast-json-stringify@^2.7.5: version "2.7.5" resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.7.5.tgz#fdd348d204ba8fc81b3ab4bb947d10a50d953e48" @@ -3390,6 +3399,13 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + gensync@^1.0.0-beta.1: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3667,6 +3683,18 @@ graceful-fs@^4.2.3: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graphql-jit@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/graphql-jit/-/graphql-jit-0.5.1.tgz#738cb2c772a65e9682f04f20dc8edac4365023da" + integrity sha512-gzgnlu9/R/SnMnPe7MGQWh6sIpK7Tva2mT2ulhUenD0GZD1izbakevGGRT6ApTMenCOiClSVcugDNziXC5KKYg== + dependencies: + fast-json-stringify "^1.13.0" + generate-function "^2.3.1" + json-schema "^0.2.3" + lodash.memoize "^4.1.2" + lodash.merge "4.6.2" + lodash.mergewith "4.6.2" + graphql@^15.5.0: version "15.5.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" @@ -4184,6 +4212,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= + is-regex@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" @@ -4352,6 +4385,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@^0.2.3: + version "0.2.5" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.5.tgz#97997f50972dd0500214e208c407efa4b5d7063b" + integrity sha512-gWJOWYFrhQ8j7pVm0EM8Slr+EPVq1Phf6lvzvD/WCeqkrx/f2xBI0xOsRRS9xCn3I4vKtP519dvs3TP09r24wQ== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -4637,11 +4675,21 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= -lodash.merge@^4.6.2: +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.merge@4.6.2, lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.mergewith@4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.omit@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60"