diff --git a/index.js b/index.js index f692f32f..61bbe968 100644 --- a/index.js +++ b/index.js @@ -342,11 +342,30 @@ const mercurius = fp(async function (app, opts) { } } - function serialize (query) { - if (query.info) { - return stringify({ obj: query.obj, params: query.params }) + /** + * Custom serialization function for batching GraphQL loaders. + * + * This ensures that similar queries (e.g. same obj.id and language) can be grouped + * into a single batched call. + * + * We use `obj.id` when available because: + * - It's a common convention (used in most GraphQL schemas) + * - It's stable and unique across objects + * - It's much faster than serializing the entire object + * + * If `obj.id` is not available, we fall back to a full serialization of both + * `obj` and `params` to ensure uniqueness (at the cost of performance). + * + * This improves performance for large GraphQL queries that call the same + * field resolver many times (e.g. N+1 problems), by allowing batching to work properly. + */ + function serialize ({ obj, params }) { + if (obj?.id !== undefined) { + const lang = params?.lang ?? null + return JSON.stringify([obj.id, lang]) } - return query + + return stringify({ obj, params }) } const resolvers = {} diff --git a/package.json b/package.json index 0bed71fe..4bfb571d 100644 --- a/package.json +++ b/package.json @@ -28,48 +28,48 @@ "graphql": "^16.0.0" }, "devDependencies": { - "@graphql-tools/merge": "^9.0.0", - "@graphql-tools/schema": "^10.0.0", - "@graphql-tools/utils": "^10.0.0", + "@graphql-tools/merge": "^9.0.24", + "@graphql-tools/schema": "^10.0.23", + "@graphql-tools/utils": "^10.8.6", "@sinonjs/fake-timers": "^14.0.0", - "@types/isomorphic-form-data": "^2.0.0", - "@types/node": "^22.0.0", - "@types/ws": "^8.2.0", + "@types/isomorphic-form-data": "^2.0.4", + "@types/node": "^22.14.0", + "@types/ws": "^8.18.1", "autocannon": "^8.0.0", - "concurrently": "^9.0.0", - "docsify-cli": "^4.4.3", - "eslint": "^9.9.1", - "fastify": "^5.0.0", - "graphql": "^16.0.0", + "concurrently": "^9.1.2", + "docsify-cli": "^4.4.4", + "eslint": "^9.24.0", + "fastify": "^5.2.2", + "graphql": "^16.10.0", "graphql-tag": "^2.12.6", - "graphql-ws": "^6.0.1", - "neostandard": "^0.12.0", + "graphql-ws": "^6.0.4", + "neostandard": "^0.12.1", "pre-commit": "^1.2.2", "proxyquire": "^2.1.3", - "semver": "^7.5.0", + "semver": "^7.7.1", "sinon": "^20.0.0", - "split2": "^4.0.0", - "tap": "^21.0.0", - "tsd": "^0.31.0", - "typescript": "^5.0.2", - "undici": "^7.0.0", - "wait-on": "^8.0.0" + "split2": "^4.2.0", + "tap": "^21.1.0", + "tsd": "^0.32.0", + "typescript": "^5.8.3", + "undici": "^7.7.0", + "wait-on": "^8.0.3" }, "dependencies": { - "@fastify/error": "^4.0.0", - "@fastify/static": "^8.0.0", - "@fastify/websocket": "^11.0.0", - "fastify-plugin": "^5.0.0", + "@fastify/error": "^4.1.0", + "@fastify/static": "^8.1.1", + "@fastify/websocket": "^11.0.2", + "fastify-plugin": "^5.0.1", "graphql-jit": "0.8.7", - "mqemitter": "^6.0.0", - "p-map": "^4.0.0", - "quick-lru": "^7.0.0", - "readable-stream": "^4.0.0", - "safe-stable-stringify": "^2.3.0", - "secure-json-parse": "^3.0.0", - "single-user-cache": "^1.0.0", - "tiny-lru": "^11.0.0", - "ws": "^8.2.2" + "mqemitter": "^6.0.2", + "p-map": "^7.0.3", + "quick-lru": "^7.0.1", + "readable-stream": "^4.7.0", + "safe-stable-stringify": "^2.5.0", + "secure-json-parse": "^4.0.0", + "single-user-cache": "^1.0.1", + "tiny-lru": "^11.2.11", + "ws": "^8.18.1" }, "tsd": { "directory": "test/types" diff --git a/static/main.js b/static/main.js index 090f5147..dd4e0a73 100644 --- a/static/main.js +++ b/static/main.js @@ -157,7 +157,7 @@ function render () { function importDependencies () { const link = document.createElement('link') - link.href = 'https://unpkg.com/graphiql@3.7.1/graphiql.min.css' + link.href = 'https://unpkg.com/graphiql@3.8.3/graphiql.min.css' link.type = 'text/css' link.rel = 'stylesheet' link.media = 'screen,print' @@ -167,7 +167,7 @@ function importDependencies () { return importer.urls([ 'https://unpkg.com/react@18.3.1/umd/react.production.min.js', 'https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js', - 'https://unpkg.com/graphiql@3.7.1/graphiql.min.js' + 'https://unpkg.com/graphiql@3.8.3/graphiql.min.js' ]).then(function () { const pluginUrls = window.GRAPHIQL_PLUGIN_LIST .map(plugin => window[`GRAPIHQL_PLUGIN_${plugin.toUpperCase()}`].umdUrl) diff --git a/static/sw.js b/static/sw.js index f53dc7a6..6af2bf55 100644 --- a/static/sw.js +++ b/static/sw.js @@ -2,13 +2,13 @@ self.addEventListener('install', function (e) { e.waitUntil( - caches.open('graphiql-v3.7.1').then(function (cache) { + caches.open('graphiql-v3.8.3').then(function (cache) { return cache.addAll([ './main.js', - 'https://unpkg.com/graphiql@3.7.1/graphiql.css', + 'https://unpkg.com/graphiql@3.8.3/graphiql.css', 'https://unpkg.com/react@18.3.1/umd/react.production.min.js', 'https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js', - 'https://unpkg.com/graphiql@3.7.1/graphiql.min.js' + 'https://unpkg.com/graphiql@3.8.3/graphiql.min.js' ]) }) )