{"id":46897498,"url":"https://github.com/tot-ra/graphql-query-cost","last_synced_at":"2026-03-10T23:34:22.219Z","repository":{"id":39801778,"uuid":"322350546","full_name":"tot-ra/graphql-query-cost","owner":"tot-ra","description":"Graphql query cost calculation","archived":false,"fork":false,"pushed_at":"2025-07-22T08:58:13.000Z","size":382,"stargazers_count":12,"open_issues_count":2,"forks_count":2,"subscribers_count":55,"default_branch":"master","last_synced_at":"2025-08-08T21:03:33.493Z","etag":null,"topics":["graphql","security"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@pipedrive/graphql-query-cost","language":"JavaScript","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/tot-ra.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null}},"created_at":"2020-12-17T16:25:58.000Z","updated_at":"2025-04-18T15:00:56.000Z","dependencies_parsed_at":"2023-07-18T18:56:13.694Z","dependency_job_id":null,"html_url":"https://github.com/tot-ra/graphql-query-cost","commit_stats":null,"previous_names":["pipedrive/graphql-query-cost"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/tot-ra/graphql-query-cost","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tot-ra%2Fgraphql-query-cost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tot-ra%2Fgraphql-query-cost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tot-ra%2Fgraphql-query-cost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tot-ra%2Fgraphql-query-cost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tot-ra","download_url":"https://codeload.github.com/tot-ra/graphql-query-cost/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tot-ra%2Fgraphql-query-cost/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30362120,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"ssl_error","status_checked_at":"2026-03-10T21:40:59.357Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["graphql","security"],"created_at":"2026-03-10T23:34:21.561Z","updated_at":"2026-03-10T23:34:22.207Z","avatar_url":"https://github.com/tot-ra.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @pipedrive/graphql-query-cost\n\nGraphql cost analysis, mainly to limit query execution in graphql service before resolvers are executed.\n\n![](https://img.shields.io/travis/pipedrive/graphql-query-cost/master?logo=travis)\n![](https://img.shields.io/github/v/release/pipedrive/graphql-query-cost?sort=semver)\n![](https://img.shields.io/coveralls/github/pipedrive/graphql-query-cost/master?logo=coveralls)\n[![Known Vulnerabilities](https://snyk.io/test/npm/@pipedrive/graphql-query-cost/badge.svg)](https://snyk.io/test/npm/@pipedrive/graphql-query-cost)\n\n# Features\n\n- Query cost calculation based on schema and cost mappings\n- Cost directive for the schema\n- Cost directive extraction from the schema\n- Cost directive value extraction from the schema as cost mappings\n\n# API\n\nIn the API documentation, @pipedrive/graphql-query-cost is referred to as `queryCost`, as if it was imported with:\n\n```js\nconst queryCost = require('@pipedrive/graphql-query-cost');\n```\n\n## `queryCost.costDirective`\n\nA custom graphql directive for defining expenses directly in the schema.\n\n```js\nconst schema = `\n  ${queryCost.costDirective}\n  type Greeting {\n    id: ID\n    name: String\n  }\n  type Query {\n    hello(limit: Int!): Greeting @cost(\n      complexity: 10,\n      network: 1,\n      db: 1,\n      useMultiplers: true,\n      multipliers: [\"limit\"]\n      provides: [\"id\"]\n    )\n  }\n`;\n```\n\n### Directive arguments\n\n| Parameter      | Type     | Description                                                                                                                 | Complexity |   Tokens |\n| :------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------- | ---------: | -------: |\n| complexity     | Int      | Abstract value                                                                                                              |     n \\* 1 |          |\n| network        | Int      | Amount of requests required to resolve the field                                                                            |            | n \\* 100 |\n| db             | Int      | Amount of db requests or query complexity                                                                                   |            | n \\* 100 |\n| mutlipliers    | [String] | Field arguments for multipling the complexity                                                                               |            |          |\n| useMultipliers | Boolean  | When defined, field complexity will not be multiplied.\u003cbr/\u003eDefaults to **true** unless the directive is **not** defined.    |            |          |\n| provides       | [String] | Specify which fields are available for the child on the parent type.\u003cbr/\u003eIf only those are requested, cost will be ignored. |            |          |\n\n## `queryCost.extractCost`\n\nReturns [cost directive arguments](#directive-arguments) per type definition.\n\n```js\nconst schema = `\n  ${queryCost.costDirective}\n  type Greeting {\n    id: ID\n    name: String @cost(db: 10)\n  }\n  type Query {\n    hello: Greeting @cost(complexity: 10)\n  }\n`;\nconst { costMap } = queryCost.extractCost(schema);\n// {\n//   Query: {\n//     hello: {\n//       complexity: 10,\n//     },\n//   },\n//   Greeting: {\n//     hello: {\n//       tokens: 1000, // n * 100\n//     },\n//   },\n// };\n```\n\n## `queryCost.calculateCost`\n\nCalculates cost of the current query based on cost mappings and schema.\n\n```js\nconst schema = `\n  type Query {\n    hello(limit: Int!): string\n    world: string\n  }\n`\nconst query = `\n  query makeQuery($limit)  {\n    hello(limit: $limit)\n    world\n  }\n`\nconst costMap = {\n  Query: {\n    hello: {\n      complexity: 5\n      multipliers: ['limit']\n    }\n  }\n}\nconst cost = queryCost.calculateCost(\n  query, schema, {\n    costMap,\n    defaultCost: 1,\n    variables: {\n      $limit: 5\n    }\n  }\n)\n// (5 * 5) + 1 = 11\n```\n\n# Cost calculation\n\n- Definition of cost for entity/resolver/property should be pessimistic (considered for worst case scenario, cold cache)\n\n## Complexity vs tokens(db, network)\n\n- Complexity argument is multiplied by its own multipliers and every parent mutliplier.\n  ```\n  deals(limit: 100) @cost(complexity: 2) = 200\n  ```\n- Tokens define how many **resources** are need to resolve a field. When it's recursive it is multiplied.\n- Tokens are only multiplied only by its parents multipliers. Only parent multipliers are used because it takes \"parent\" times **resources** to execute the query. \u003cbr\u003e\n  **Example query**\n  ```\n  deals(limit: 100)\n    @cost(complexity: 2, db: 1, network: 1)\n  ```\n  **Example token cost**\n  ```\n  complexity(2) * limit(100) + tokens(200) = 400\n  ```\n  **If tokens were multiplied by own multipler**\n  ```\n  complexity(200) + tokens(200) * limit(100) = 20200\n  ```\n\n## Flat complexity\n\n```graphql\n# Schema\ntype Query {\n  field: String @cost(complexity: 3)\n  default: String\n}\n\n# Query\nquery {\n  field\n  default\n}\n```\n\nTotal cost is 4:\n\n- **field** cost is 3\n- **default** cost is 1\n\n## Multipliers\n\n- Multipliers are recursive\n- Undefined(`Parent.name`) complexity is not multiplied\n\n```graphql\n# Schema\ntype Query {\n  parents(limit: Int!, names: [String]): [Parent]\n  @cost(complexity: 3, multipliers: [\"limit\", \"names\"])\n}\n\ntype Parent {\n  name: String\n  children(limit: Int): [Child] @cost(complexity: 5)\n}\n\ntype Child {\n  name: String\n}\n\n# Query\n{\n  parents(limit: 2, names: [\"elon\", \"foo\"]) {\n    name\n    children(limit: 4) {\n      name\n    }\n  }\n}\n```\n\n| Field path            | Description                                                           |                           Result |\n| :-------------------- | :-------------------------------------------------------------------- | -------------------------------: |\n| parents               | limit _ names.length _ complexity                                     |                   2 _ 2 _ 3 = 12 |\n| parents.name          | default                                                               |                previous + 1 = 13 |\n| parents.children      | (parents.limit _ parents.names.length) _ children.limit \\* complexity | previous + (2 _ 2 _ 4 \\* 5) = 93 |\n| parents.children.name | default                                                               |                previous + 1 = 94 |\n\n## Ignoring multipliers\n\n### `useMultipliers`\n\n- Definining `useMultipliers: false` ignores the multipliers\n\n```graphql\n# Schema\ntype Query {\n  parents(limit: Int): [Parent] @cost(complexity: 2, multipliers: [\"limit\"])\n}\n\ntype Parent {\n  name: String @cost(complexity: 8, useMultipliers: false)\n}\n# Query\n{\n  parents(limit: 5) {\n    name\n  }\n}\n```\n\n| Field path   | Description                      |                Result |\n| :----------- | :------------------------------- | --------------------: |\n| parents      | limit \\* complexity              | c(2) \\* limit(5) = 10 |\n| parents.name | useMultipliers: false is defined |  previous + c(8) = 18 |\n\n### `provides`\n\n- If all the queried fields are in the list of `provides` argument, then the complexity of the field is ignored and the default cost is applied\n\n```graphql\n# Schema\ntype Query {\n  parents(limit: Int): [Parent] @cost(complexity: 3, multipliers: [\"limit\"], provides: [\"id\"])\n}\n\ntype Parent {\n  id: ID @cost(complexity: 1)\n  name: String\n}\n\n# Query\n{\n  parents(limit: 5) {\n    id\n  }\n}\n```\n\n| Field path | Description                                                                                    |                Result |\n| :--------- | :--------------------------------------------------------------------------------------------- | --------------------: |\n| parents    | limit \\* complexity                                                                            | c(3) \\* limit(5) = 15 |\n| parents.id | no multipliers used, default cost applied - field `parents` already `provides` fields `[\"id\"]` |  previous + c(1) = 16 |\n\n### Recursive queries\n\n```graphql\n# Schema\ntype Query {\n  pipelines: [Pipeline]\n}\n\ntype Pipeline {\n  id: ID\n  deals: [Deal]\n}\n\ntype Deal {\n  id: ID\n  pipeline: Pipeline\n}\n\n# Query\n{\n  pipelines {\n    deals {\n      pipeline {\n        deals {\n          pipeline {\n            id\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n| Field path | Description                                                                            |                                                                Result |\n| :--------- | :------------------------------------------------------------------------------------- | --------------------------------------------------------------------: |\n| pipelines  | Default cost                                                                           |                                                                     1 |\n| deals      | Default cost                                                                           |                                                      previous + 1 = 2 |\n| pipeline   | Default cost                                                                           |                                                      previous + 1 = 3 |\n| deals      | `deals.pipeline` reverse of `pipeline.deals` already appeared \u003cbr /\u003e Recursion level 1 |                               previous \\* 100(recursion x1) + 1 = 301 |\n| pipeline   | Recursion level increased to 2                                                         | previous _ (100 _ 100)(recursion x2) + 1 = 301 \\* 10000 + 1 = 3010001 |\n| id         | Default cost                                                                           |                                              (previous + 1) = 3010002 |\n\n#### Recursion multiplier\n\n`recursionMultiplier` default value is `100`. If recursion is detected, thats how much cost gets affected.\n`recursionMultiplier` value of `1` will mean that detected recursion doesn't affect cost.\n`recursionMultiplier` value gets inherited to deeper levels of the graph, so you can have different values depending on the schema\n\nFor cases where schema and resolver can have deeply nested recursive structures (trees or graphs) fetched once into memory,\nyou can override default behaviour by setting `recursionMultiplier` to lower values.\n\n##### Usage\n\nWith maximum cost of `5000` per request, you can see different values of `recursionMultiplier` affects graphq and schema below:\n\n| recursionMultiplier | Cost          |\n| :------------------ | :------------ |\n| 1                   | 6             |\n| 3                   | 1490          |\n| 3.65                | 4783          |\n| 100 (default)       | 2000001000102 |\n\n- It is recommended that you _don't_ set recursion to `1`, but still leave some recursion scaling limits, such that your query still fits into max cost limit.\n\n```graphql\n# Schema\ntype Query {\n  myTree: [TreeLeaf]\n}\n\ntype TreeLeaf {\n  id: ID\n  leafs: [TreeLeaf] @cost(recursionMultiplier: 1)\n}\n\n# Query\n{\n  myTree {\n    leafs {\n      leafs {\n        leafs {\n          leafs {\n            id\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n- With great `recursionMultiplier` override power comes great responsibility that you don't reset it to `1` and leave holes in graph cost map.\n\n```graphql\ntype Query {\n  myTree: [TreeLeaf]\n}\n\ntype TreeLeaf {\n  id: ID\n\n  # reset recursionMultiplier back to 100, as its part of the recursive graph\n  branch: Branch @cost(recursionMultiplier: 100)\n  leafs: [TreeLeaf] @cost(recursionMultiplier: 1)\n}\n\ntype Branch {\n  id: ID\n  name: String @cost(network: 1)\n  leafs: [TreeLeaf]\n}\n```\n\n## Development\n\n### Cost debugging\n\nTo ease understanding how cost is calculated, you can use `debug:true` param, which will console.log node visiting along with how price is added.\n(Maybe it could be a product feature later if further )\n\n```\n\tcalculateCost(`query { a { b { c { b {c { id }}}}}}`, typeDefs, { costMap, debug: true });\n```\n\nDebug result:\n\n```\n       undefined (\u003cOperationDefinition\u003e)\n        a \u003cField\u003e\n         a (\u003cField\u003e)\n          b \u003cField\u003e\n           b (\u003cField\u003e)\n            c \u003cField\u003e\n             c (\u003cField\u003e)\n              b \u003cField\u003e\n               b (\u003cField\u003e)\n                c \u003cField\u003e\n                recursion detected b=\u003ec for 1 times\n                recursion detected c=\u003ebfor 1 times\n                * recursion multiplier = (10000)\n                 c (\u003cField\u003e)\n                  id \u003cField\u003e\n                  = 1\n                 == 1\n                = 20000\n               == 20000\n              = 20001\n             == 20001\n            = 20002\n           == 20002\n          = 20003\n         == 20003\n        = 20004\n       == 20004\n```\n\n# Contribution\n\n- Before making PR, make sure to run `npm run version` \u0026 fill CHANGELOG\n- `npm-version-\u003cversion\u003e` – should be set when creating a pull request. It’s good to set it as soon as possible, so reviewer can validate that the proposed version bump is correct\n- `npm-ready-for-publish` – add this label after PR has been approved, it will publish the package to NPM and merge changes to master\n\n## Authors and acknowledgment\n\nOriginal author - @ErikSchults.\nCurrent maintainers - @tot-ra, @Wolg. Mention in PR, if it is stuck\n\nSee [blog post](https://medium.com/pipedrive-engineering/journey-to-a-federated-graphql-cost-of-the-queries-a892f9939f9a)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftot-ra%2Fgraphql-query-cost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftot-ra%2Fgraphql-query-cost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftot-ra%2Fgraphql-query-cost/lists"}