{"id":25919702,"url":"https://github.com/saewoohan/graphql-depth-guard","last_synced_at":"2025-03-03T15:15:52.131Z","repository":{"id":274193593,"uuid":"922190679","full_name":"saewoohan/graphql-depth-guard","owner":"saewoohan","description":"A GraphQL directive to enforce query depth limits with optional caching support.","archived":false,"fork":false,"pushed_at":"2025-01-28T00:33:40.000Z","size":160,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-27T00:43:47.330Z","etag":null,"topics":["depth","graphql","graphql-depth","graphql-rate-limit","guard","limit"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/graphql-depth-guard","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saewoohan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2025-01-25T15:06:01.000Z","updated_at":"2025-01-28T01:51:09.000Z","dependencies_parsed_at":"2025-01-25T16:29:25.574Z","dependency_job_id":null,"html_url":"https://github.com/saewoohan/graphql-depth-guard","commit_stats":null,"previous_names":["saewoohan/graphql-depth-guard"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saewoohan%2Fgraphql-depth-guard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saewoohan%2Fgraphql-depth-guard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saewoohan%2Fgraphql-depth-guard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saewoohan%2Fgraphql-depth-guard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saewoohan","download_url":"https://codeload.github.com/saewoohan/graphql-depth-guard/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241686830,"owners_count":20003112,"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":["depth","graphql","graphql-depth","graphql-rate-limit","guard","limit"],"created_at":"2025-03-03T15:15:51.453Z","updated_at":"2025-03-03T15:15:52.125Z","avatar_url":"https://github.com/saewoohan.png","language":"TypeScript","readme":"# GraphQL Depth Guard\n\nA GraphQL directive to enforce query depth limits with optional caching support (using Redis or in-memory cache).\n\n## Features\n\n- **Depth Limiting**: Prevent overly deep GraphQL queries.\n- **Caching**: Cache depth calculations for performance optimization.\n- **Customizable Storage**: Supports Redis, in-memory cache.\n- **Flexible Limits**: Apply limits globally or on a per-query basis.\n\n---\n\n## Why GraphQL Depth Guard?\n\nComplexity-based limiters often struggle to precisely identify thresholds for various use cases. This library was created to provide an intuitive mechanism for limiting query depths. By restricting response depth, it aims to:\n\n1. Prevent excessive and repetitive database queries.\n2. Simplify configuration compared to complexity-based approaches.\n3. Support both global and query-specific limits for better control.\n\n---\n\n## Depth Definition\n\nQuery depth is determined by the structure of the response fields, including nested fields and fragments. Here's how depth is defined:\n\n### Example Queries with Depth Calculation\n\n#### Depth: 0\n\n```graphql\nquery {\n  hello\n}\n```\n\n#### Depth: 1\n\n```graphql\nquery {\n  userDetails {\n    name\n  }\n}\n```\n\n#### Depth: 2 (with nested fields)\n\n```graphql\nquery {\n  userDetails {\n    name\n    posts {\n      title\n    }\n  }\n}\n```\n\n#### Depth: 2 (with fragments)\n\n```graphql\nfragment postInfo on Post {\n  title\n  comments {\n    content\n    author\n  }\n}\n\nquery {\n  userDetails {\n    posts {\n      ...postInfo\n    }\n  }\n}\n```\n\n---\n\n### Fragment Behavior\n\n- **Inline Fragments** and **Named Fragments** are fully traversed during depth calculation.\n- Fragments do not reset or reduce the depth; they are evaluated as part of the query structure.\n\n#### Example with Nested Fragments\n\n```graphql\nfragment commentInfo on Comment {\n  content\n  author\n}\n\nfragment postInfo on Post {\n  title\n  comments {\n    ...commentInfo\n  }\n}\n\nquery {\n  viewer {\n    users {\n      posts {\n        ...postInfo\n      }\n    }\n  }\n}\n```\n\n**Depth Calculation:**\n\n- `viewer` (depth 0)\n- `users` (depth 1)\n- `posts` (depth 2)\n- `postInfo` (depth 3 for `title` and `comments`)\n- `commentInfo` (depth 4 for `content` and `author`)\n\n**Total Depth:** 4\n\n---\n\n## Installation\n\n### NPM\n\n```bash\nnpm install graphql-depth-guard graphql @graphql-tools/utils\n```\n\n### Yarn\n\n```bash\nyarn add graphql-depth-guard graphql @graphql-tools/utils\n```\n\n---\n\n## Usage\n\n### Basic Setup\n\n1. **Import the directive and apply it to your schema:**\n\n```ts\nimport { makeExecutableSchema } from '@graphql-tools/schema';\nimport depthLimitDirective from 'depth-limit-directive';\n\nconst typeDefs = `\n  type Query {\n    hello: String @depthLimit(limit: 3)\n    nestedField: NestedType @depthLimit(limit: 2)\n  }\n\n  type NestedType {\n    name: String\n    child: NestedType\n  }\n`;\n\nconst resolvers = {\n  Query: {\n    hello: () =\u003e 'Hello, world!',\n    nestedField: () =\u003e ({ name: 'Level 1', child: { name: 'Level 2' } }),\n  },\n};\n\nconst depthDirective = depthLimitDirective({\n  globalLimit: 5, // Optional global limit\n});\n\nconst schema = depthDirective.transformer(\n  makeExecutableSchema({\n    typeDefs: [depthDirective.typeDefs, typeDefs],\n    resolvers,\n  }),\n);\n```\n\n---\n\n### Caching Support\n\n#### 1. **Using In-Memory Cache**\n\nThe library uses an in-memory cache (`MemoryCache`) by default, which stores cached depths for 60 seconds.\n\n```ts\nimport depthLimitDirective, { MemoryCache } from 'graphql-depth-guard';\n\nconst depthDirective = depthLimitDirective({\n  globalLimit: 5,\n  store: new MemoryCache(60 * 1000),\n});\n```\n\n#### 2. **Using redis Cache**\n\nTo use `redis` for caching:\n\n```ts\nimport { createClient } from 'redis';\nimport depthLimitDirective, { RedisCache } from 'graphql-depth-guard';\n\nconst redisClient: RedisClientType = createClient({\n  url: 'redis://localhost:6379',\n});\nawait redisClient.connect();\n\nconst redisCache = new RedisCache(redisClient, 60 * 1000); // TTL: 60 seconds\n\nconst depthDirective = depthLimitDirective({\n  store: redisCache, // Pass Redis cache instance\n  globalLimit: 5,\n});\n```\n\n#### 3. **Using ioredis Cache**\n\nTo use `ioredis` for caching:\n\n```ts\nimport Redis from 'ioredis';\nimport depthLimitDirective, { RedisCache } from 'graphql-depth-guard';\n\nconst ioredisClient = new Redis('redis://localhost:6379');\n\nconst redisCache = new RedisCache(ioredisClient, 60 * 1000); // TTL: 60 seconds\n\nconst depthDirective = depthLimitDirective({\n  store: redisCache, // Pass Redis cache instance\n  globalLimit: 5,\n});\n```\n\n#### 4. **No Cache**\n\nIf no store is provided in the options, caching is disabled, and the directive calculates the depth for every query without caching it.\n\n```ts\nconst depthDirective = depthLimitDirective({\n  globalLimit: 5, // Global limit without caching\n});\n```\n\n---\n\n### Custom Error Handling\n\nYou can provide a custom `errorHandler` to control how errors are reported:\n\n```ts\nconst depthDirective = depthLimitDirective({\n  globalLimit: 5,\n  errorHandler: ({ depth, limit, message, isGlobalLimit }) =\u003e {\n    return new Error(\n      `Custom Error: Depth of ${depth} exceeds limit of ${limit}${\n        isGlobalLimit ? ' (global limit)' : ''\n      }`,\n    );\n  },\n});\n```\n\n---\n\n## API\n\n### `depthLimitDirective(options?: DepthLimitDirectiveOptions)`\n\n| Option         | Type                  | Description                                                                |\n| -------------- | --------------------- | -------------------------------------------------------------------------- |\n| `globalLimit`  | `number` (optional)   | The global depth limit for queries.                                        |\n| `errorHandler` | `function` (optional) | Custom function to handle errors when the depth limit is exceeded.         |\n| `store`        | `ICache` (optional)   | Cache store implementation (`MemoryCache`, `RedisCache`, or custom store). |\n\n---\n\n## Example Schema with Depth Directive\n\n```graphql\ndirective @depthLimit(limit: Int!, message: String) on FIELD_DEFINITION\n\ntype Query {\n  hello: String @depthLimit(limit: 3)\n  nestedField: NestedType @depthLimit(limit: 2)\n}\n\ntype NestedType {\n  name: String\n  child: NestedType\n}\n```\n\n---\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaewoohan%2Fgraphql-depth-guard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaewoohan%2Fgraphql-depth-guard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaewoohan%2Fgraphql-depth-guard/lists"}