{"id":20253559,"url":"https://github.com/graphile/gqlcheck","last_synced_at":"2025-09-10T03:43:52.424Z","repository":{"id":245086571,"uuid":"814789277","full_name":"graphile/gqlcheck","owner":"graphile","description":"Quickly check operations before you deploy them to production to ensure they're not too deep, missing pagination, or anything else","archived":false,"fork":false,"pushed_at":"2024-11-13T11:50:50.000Z","size":239,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T23:43:38.623Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/graphile.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-13T17:54:05.000Z","updated_at":"2024-12-22T23:56:45.000Z","dependencies_parsed_at":"2024-10-29T13:42:33.029Z","dependency_job_id":null,"html_url":"https://github.com/graphile/gqlcheck","commit_stats":null,"previous_names":["graphile/gqlcheck"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgqlcheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgqlcheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgqlcheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgqlcheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/graphile","download_url":"https://codeload.github.com/graphile/gqlcheck/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248317726,"owners_count":21083527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T10:25:41.442Z","updated_at":"2025-04-10T23:43:44.072Z","avatar_url":"https://github.com/graphile.png","language":"TypeScript","funding_links":["https://github.com/sponsors/benjie"],"categories":[],"sub_categories":[],"readme":"# gqlcheck\n\nThis tool is designed to perform checks (see [Rules](#rules)) against your\nGraphQL documents (which contain one or more query, mutation or subscription\noperations and any associated fragments) before you ship them to production, to\nhelp ensure the safety of your servers. You should use it alongside other\ntooling such as\n[trusted documents](https://benjie.dev/graphql/trusted-documents) and\n[GraphQL-ESLint](https://the-guild.dev/graphql/eslint/docs) to ensure you're\nshipping the best GraphQL you can.\n\nThis tools is designed to be **safe to incorporate into your existing\nprojects** - it enables you to capture (and allowlist) the current state of your\nexisting operations, called the \"baseline\", whilst enforcing more rigorous rules\nagainst future operations (and against new fields added to existing operations).\nYou can thus safely adopt this tool with strict rules enabled, even if you've\nbeen lenient with your GraphQL operations up until this point, without breaking\nexisting operations. And you can change the configuration options at any point\nand simply reset the baseline to avoid having to adjust existing operations to\nmatch your new rules.\n\n`gqlcheck` by default uses all cores available on your processor to complete the\nchecks as fast as possible; to adjust this see [Configuration](#configuration).\n\n## TL;DR\n\nRun a check passing your schema and a path to your operations (either `.graphql`\nfile(s), directory containing the same, or a\n[KJSONL file](https://github.com/benjie/kjsonl) containing operations):\n\n```bash\nnpx gqlcheck -s path/to/schema.graphqls path/to/operations\n```\n\nOutput:\n\n```\npath/to/operations/doc2.graphql:\n- [17:3] Self-reference limit for field 'User.friends' exceeded: 3 \u003e 2\n  Problematic paths:\n  - FoFoF:query\u003ecurrentUser\u003eF1:User.friends\u003eF2:User.friends\u003eF3:User.friends\n\npath/to/operations/doc3.graphql:\n- [7:13] Maximum list nesting depth limit exceeded: 6 \u003e 4\n  Problematic paths:\n  - FoFoFoFoFoF:query\u003ecurrentUser\u003efriends\u003efriends\u003efriends\u003efriends\u003efriends\n  - FoFoFoFoFoF:query\u003ecurrentUser\u003efriends\u003efriends\u003efriends\u003efriends\u003efriends\u003efriends\n- [5:9] Self-reference limit for field 'User.friends' exceeded: 6 \u003e 2\n  Problematic paths:\n  - FoFoFoFoFoF:query\u003ecurrentUser\u003efriends\u003efriends\u003efriends\n  - FoFoFoFoFoF:query\u003ecurrentUser\u003efriends\u003efriends\u003efriends\u003efriends\n  - FoFoFoFoFoF:query\u003ecurrentUser\u003efriends\u003efriends\u003efriends\u003efriends\u003efriends\n  - FoFoFoFoFoF:query\u003ecurrentUser\u003efriends\u003efriends\u003efriends\u003efriends\u003efriends\u003efriends\n\nScanned 3 documents consisting of 3 operations (and 3 fragments). Visited 16\nfields, 0 arguments, 3 named fragment spreads and 0 inline fragment spreads.\n\nErrors: 0\nInfractions: 3\n```\n\nThis reveals all our documents are valid (no errors), but they don't comply with\nour rules (3 infractions). But these are existing operations; we only want to\ndefend against newly introduced issues, all existing operations should be\nallowed. To achieve this, we create a baseline using the `-b` and `-u` flags:\n\n```bash\nnpx gqlcheck -s path/to/schema.graphqls path/to/operations -b baseline.json5 -u\n```\n\n```\nNew baseline written to baseline.json5\n\nScanned 3 documents consisting of 3 operations (and 3 fragments). Visited 16\nfields, 0 arguments, 3 named fragment spreads and 0 inline fragment spreads.\n\nErrors: 0\nInfractions: 0 (ignored: 3)\n```\n\nPassing all these flags is a chore; instead, let's create a configuration file\n`graphile.config.mjs`:\n\n```ts\nexport default {\n  gqlcheck: {\n    schemaSdlPath: \"path/to/schema.graphqls\",\n    baselinePath: \"baseline.json5\",\n  },\n};\n```\n\nThen in CI we can check no new issues are introduced:\n\n```bash\nnpx gqlcheck path/to/operations\n```\n\n```\nScanned 3 documents consisting of 3 operations (and 3 fragments). Visited 16\nfields, 0 arguments, 3 named fragment spreads and 0 inline fragment spreads.\n\nErrors: 0\nInfractions: 0 (ignored: 3)\n```\n\n## Exit status\n\nThe `gqlcheck` command exits with a status code indicating success or failure:\n\nExit status 0: no issues found.  \nExit status 1: errors (including validation errors) found.  \nExit status 2: infractions found.\n\n## Works with any GraphQL system (in any language)\n\nThis tool takes the SDL describing your GraphQL schema and your GraphQL\ndocuments as inputs, and as output gives you a pass or a fail (with details). So\nlong as you can represent your GraphQL documents as a string using the GraphQL\nlanguage, and you can introspect your schema to produce an SDL file, you can use\nthis tool to check your documents. (Supports the latest draft of the GraphQL\nspecification.)\n\n## Fast\n\nThis tool uses a worker pool to distribute validation work across all the cores\nof your CPU (memory allowing). We don't want to slow down your CI or deploy\nprocess any more than we have to! We're also very careful to write the rules in\na performant manner, leaning into the visitor pattern exposed by GraphQL.js and\navoiding manual AST traversal where possible.\n\n## Designed to work with persisted operations\n\nPersisted queries, persisted operations, stored operations, trusted documents...\nWhatever you call them, this system is designed to perform checks on your new\ndocuments before you persist them. This means that your server will not have to\nrun expensive validation rules (or know about your overrides) in production -\nonly the operations that you have persisted (and have checked with this tool)\nshould be allowed. Read more about\n[trusted documents](https://benjie.dev/graphql/trusted-documents); a pattern\nthat is usable with any GraphQL server in any language.\n\n## Rules\n\nOther than the specified GraphQL validation rules, this tool also checks a few\nother things. Each check can be configured or disabled, and can be overridden to\nallow existing operations to pass.\n\n### Field depth\n\nSetting: `maxDepth` (normal) and `maxIntrospectionDepth` (for introspection\nqueries).\n\nCheck that your operations aren't too deep.\n\n(Leaf nodes are ignored.)\n\n### List depth\n\nSetting: `maxListDepth` (normal) and `maxIntrospectionListDepth` (for\nintrospection queries).\n\nChecks that lists aren't being nested too many times, leading to potential\nresponse amplification attacks; e.g.\n`{ user { friends { friends { friends { friends { friends { name } } } } } } }`.\n\n(Leaf nodes are ignored, even if they are lists.)\n\n### Self-referential depth\n\nSetting: `maxSelfReferentialDepth` and `maxDepthByFieldCoordinates[coords]`\n\nAttackers often look to exploit cycles in your schema; in general it's unlikely\nyou'd want to visit the same field multiple times, so this rule only allows a\nfield to be referenced once inside itself (i.e. it allows \"friends of friends\"\nbut not \"friends of friends of friends\").\n\nShould you need to, you can override this on a per-field basis by specifying a\nhigher limit using the field's\n[schema coordinate](https://github.com/graphql/graphql-wg/blob/main/rfcs/SchemaCoordinates.md)\n(i.e. `TypeName.fieldName`). For example, to allow \"friends of friends of\nfriends\" you might configure with:\n\n```ts\n    maxSelfReferentialDepth: 2,\n    maxDepthByFieldCoordinates: {\n      \"User.friends\": 3,\n    },\n```\n\n## Baselines\n\nWhen you get started with `gqlcheck` you'll want to capture the current state of\nyour GraphQL operations (you can do this with the `-u` CLI flag). This will\nensure that all existing operations continue to function (i.e. are allowed)\nwhilst trying to avoid any issues getting worse. For example, if you set a\ndefault `maxDepth` limit of `8` but one of your existing queries has a depth of\n`12`, you'd still want that existing query to continue working.\n\n`gqlcheck` is intelligent - it doesn't just capture the required settings values\nfor the operation as a whole, it captures the\n[operation expression path](https://github.com/graphql/graphql-wg/blob/main/rfcs/OperationExpressions.md)\nthat describes where the issue occurred, this allows it to bless that particular\npath whilst still preventing depth creep in other areas of that same named\noperation.\n\nThe baseline is only intended to be captured when you first start running the\nsystem, or when you do a software update that introduces new rules (and you're\nalready in a \"clean\" state), since it will hide \"known\" issues from being\nreported.\n\n## Configuration\n\nIf the CLI flags are not enough, configuration is performed via a\n`graphile.config.mjs` file. Global settings are stored under the\n`gqlcheck.config` path, and named-operation overrides under\n`gqlcheck.operationOverrides[operationName]`. In future, `plugins` may be added\nto the configuration to allow supporting additional rules.\n\n### Example configuration\n\nThe below `graphile.config.mjs` file demonstrates the key settings you are\nlikely to want to change. For full configuration options, use TypeScript.\n\n```ts\n// @ts-check\n\n// The following comment requires TypeScript 5.5+ to work\n/** @import {} from 'gqlcheck' */\n\n/** @type {GraphileConfig.Preset} */\nconst preset = {\n  gqlcheck: {\n    // How many workers should we spawn in the background? Defaults to the\n    // number of CPUs on this machine (or less if insufficient RAM is\n    // available) since walking ASTs is single threaded.\n    // workerCount: 4,\n\n    // Update this to be the path to your GraphQL schema in SDL format. We\n    // currently only support loading an SDL from the file system.\n    schemaSdlPath: \"schema.graphqls\",\n\n    // Enable this setting so that a baseline may be used to hide issues that\n    // were present before you adopted `gqlcheck`.\n    baselinePath: \"baseline.json5\",\n\n    config: {\n      // How many fields deep can you go?\n      maxDepth: 12,\n\n      // How many lists deep can you go?\n      maxListDepth: 4,\n\n      // How many layers deep can a field reference itself\n      maxSelfReferentialDepth: 2,\n\n      // If certain of your coordinates need to be deeply nested (or must never\n      // be nested), list them here with their maximum depth values (1+).\n      maxDepthByFieldCoordinates: {\n        // \"User.friends\": 1,\n        // \"Comment.comments\": 5,\n      },\n    },\n  },\n};\n\nexport default preset;\n```\n\nIf you aren't using TypeScript and want a shorter config, you can omit all\ncomments and export directly:\n\n```js\nexport default {\n  gqlcheck: {\n    schemaSdlPath: \"schema.graphqls\",\n    baselinePath: \"baseline.json5\",\n    config: {\n      maxDepth: 12,\n      maxListDepth: 4,\n      maxSelfReferentialDepth: 2,\n      maxDepthByFieldCoordinates: {},\n    },\n  },\n};\n```\n\n### Per-operation overrides\n\nOverrides in `gqlcheck` operate on a per-operation-name basis; this is because\nyour operations will likely evolve over time, so overrides need to apply not\nonly to the current version of the operation but also all past and future\nversions too, even if they're not in the same file name. Every setting can be\noverridden on a per-operation basis,\n\n```ts\nconst preset = {\n  gqlcheck: {\n    config: {\n      maxDepth: 12,\n      maxListDepth: 4,\n      maxSelfReferentialDepth: 2,\n    },\n\n    operationOverrides: {\n      // Override the above global settings for the 'MyLegacyQuery' operation\n      MyLegacyQuery: {\n        maxDepth: 32,\n        maxListDepth: 10,\n        maxDepthByFieldCoordinates: {\n          \"User.friends\": 5,\n        },\n      },\n    },\n  },\n};\n\nexport default preset;\n```\n\n## Installation\n\n```\nnpm install gqlcheck\n```\n\n## Usage\n\nIf you have configured the `schemaSdlPath` then you can run `gqlcheck` passing\nthe paths to files to check:\n\n```\ngqlcheck query1.graphql query2.graphql query3.graphql\n```\n\nOtherwise, pass the `-s path/to/schema.graphqls` option to specify where your\nschema SDL is.\n\nYou can also pass `-b baseline.json5` to identify your baseline file; and use\n`-u` to update the baseline such that all current documents are allowed.\n\n### Full usage\n\n```\nUsage:\n\n  gqlcheck [-s schema.graphqls] [-b baseline.json5] [-u] [-l] [-e] doc1.graphql doc2.graphql\n\nFlags:\n\n--help\n-h\n\n    Output available CLI flags\n\n--version\n-v\n\n    Output the version\n\n--config \u003cconfigPath\u003e\n-C \u003cconfigPath\u003e\n\n    The path to the config file\n\n--schema \u003csdlPath\u003e\n-s \u003csdlPath\u003e\n\n    Path to the GraphQL schema SDL file\n\n--baseline \u003cjsonPath\u003e\n-b \u003cjsonPath\u003e\n\n    Path to the baseline file (.json or .json5)\n\n--update-baseline\n-u\n\n    Update the baseline.json file to allow all passed documents even if they break the rules.\n\n--list\n-l\n\n    Don't output any details, just list the affected source names.\n\n--only-errors\n-e\n\n    Only output details about errors (not infractions); combine with `-l` to list only the files with errors.\n```\n\n## FAQ\n\n### Do I have to use the `.graphqls` extension for my schema?\n\nNo. It just helps to differentiate it from the executable documents.\n\n### Why should I trust you?\n\nI'm a\n[member of the GraphQL TSC](https://github.com/graphql/graphql-wg/blob/main/GraphQL-TSC.md#tsc-members)\nand one of the\n[top contributors to the GraphQL spec](https://github.com/graphql/graphql-spec/graphs/contributors).\n\n### This is awesome, how can I support you?\n\nI'm a community-funded open source maintainer, which means that sponsorship from\nfolks like you helps me to take time off from client work to work on open\nsource. This project was initially sponsored by Steelhead Technologies (thanks\nSteelhead! ❤️) but over time there will almost certainly be feature requests,\nbug reports, and maintenance work required. Not to mention that I have a lot of\nother open source projects too!\n\n💖 Please sponsor me: https://github.com/sponsors/benjie 🙏\n\n## See also\n\n- [GraphQL-ESLint](https://the-guild.dev/graphql/eslint/docs) for general\n  GraphQL linting\n- [Trusted documents](https://benjie.dev/graphql/trusted-documents) to protect\n  your server from untrusted queries\n- [@graphile/depth-limit](https://github.com/graphile/depth-limit) to help\n  reduce query amplification attacks\n\n## Thanks\n\nThe initial work on this project was funded by Steelhead Technologies - thanks\nSteelhead!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphile%2Fgqlcheck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgraphile%2Fgqlcheck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphile%2Fgqlcheck/lists"}