{"id":16297498,"url":"https://github.com/graphile/graphql-toe","last_synced_at":"2025-04-10T23:43:28.515Z","repository":{"id":257029925,"uuid":"857138525","full_name":"graphile/graphql-toe","owner":"graphile","description":"GraphQL Throw-On-Error","archived":false,"fork":false,"pushed_at":"2025-03-24T14:34:47.000Z","size":37,"stargazers_count":18,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T23:43:20.341Z","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-09-13T22:11:29.000Z","updated_at":"2025-04-06T11:58:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"f90efb74-c5a0-4ac3-859c-05542124c6c2","html_url":"https://github.com/graphile/graphql-toe","commit_stats":null,"previous_names":["graphile/graphql-toe"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgraphql-toe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgraphql-toe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgraphql-toe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graphile%2Fgraphql-toe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/graphile","download_url":"https://codeload.github.com/graphile/graphql-toe/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-10-10T20:27:53.079Z","updated_at":"2025-04-10T23:43:28.507Z","avatar_url":"https://github.com/graphile.png","language":"TypeScript","funding_links":[],"categories":["Standalone Tools"],"sub_categories":[],"readme":"# GraphQL TOE (Throw On Error)\n\n\u003e Like bumping your **toe** on something... I usually **throw** things!  \n\u003e -- Pascal Senn, ChilliCream\n\n**GraphQL gives you `null`... Was that a real `null`, or an error?**\n\nTOE makes GraphQL errors into real JavaScript errors, so you can stop writing\ncode that second-guesses your data!\n\nWorks seamlessly with `try`/`catch`, or your framework's error handling such as\n`\u003cErrorBoundary /\u003e` in React or SolidJS. And, with semantic nullability, reduce\nthe need for null checks in your client code!\n\n## Example\n\n```ts\nimport { toe } from \"graphql-toe\";\n\n// Imagine the second user threw an error in your GraphQL request:\nconst result = await request(\"/graphql\", \"{ users(first: 2) { id } }\");\n\n// Take the GraphQL response map and convert it into a TOE object:\nconst data = toe(result);\n\ndata.users[0]; // { id: 1 }\ndata.users[1]; // Throws \"Loading user 2 failed!\"\n```\n\n## How?\n\nReturns a copy of your GraphQL result data that uses\n[getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)\nto throw an error when you read from an errored GraphQL field. And it's\nefficient: only the parts of the response that are impacted by errors are copied\n(if there are no errors, the underlying data is returned directly).\n\n## Why?\n\nGraphQL replaces errored fields with `null`, so you can't trust a `null` to mean\n\"nothing\"; you must always check to see if a `null` actually represents an error\nfrom the \"errors\" list.\n\n`toe()` fixes this. It reintroduces errors into your data using getters that\nthrow when accessed.\n\nThat means:\n\n- `try`/`catch` just works\n- `\u003cErrorBoundary /\u003e` components can catch data-layer errors\n- Your GraphQL types’ [_semantic_ nullability](#semantic-nullability) matters\n  again\n\n## Installation\n\n```bash\nyarn add graphql-toe\n# OR: npm install --save graphql-toe\n# OR: pnpm install --save graphql-toe\n```\n\n## Usage\n\n```ts\nimport { toe } from \"graphql-toe\";\n\nconst result = await fetch(/* ... */).then((res) =\u003e res.json());\nconst data = toe(result);\n```\n\nIf `result.data` is `null` or not present, `toe(result)` will throw immediately.\nOtherwise, `data` is a derivative of `result.data` where errored fields are\nreplaced with throwing getters.\n\n## Framework examples\n\nHow to get `result` and feed it to `toe(result)` will depend on the client\nyou're using. Here are some examples:\n\n### Apollo Client (React)\n\n```ts\nimport { useQuery } from \"@apollo/client\";\nimport { toe } from \"graphql-toe\";\nimport { useMemo } from \"react\";\n\nfunction useQueryTOE(document, options) {\n  const rawResult = useQuery(document, { ...options, errorPolicy: \"all\" });\n  return useMemo(\n    () =\u003e toe({ data: rawResult.data, errors: rawResult.error?.graphQLErrors }),\n    [rawResult.data, rawResult.error],\n  );\n}\n```\n\nNote: apply similar changes to mutations and subscriptions.\n\n### URQL\n\nUse\n[@urql/exchange-throw-on-error](https://github.com/urql-graphql/urql/tree/main/exchanges/throw-on-error):\n\n```ts\nimport { Client, fetchExchange } from \"urql\";\nimport { throwOnErrorExchange } from \"@urql/exchange-throw-on-error\";\n\nconst client = new Client({\n  url: \"/graphql\",\n  exchanges: [fetchExchange, throwOnErrorExchange()],\n});\n```\n\n### graffle\n\n```ts\nimport { request } from \"graffle\";\n\nconst result = await request(\"https://api.spacex.land/graphql/\", document);\nconst data = toe(result);\n```\n\n### fetch()\n\n```ts\nimport { toe } from \"graphql-toe\";\n\nconst response = await fetch(\"/graphql\", {\n  headers: {\n    Accept: \"application/graphql-response+json, application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({ query: \"{ __schema { queryType { name } } }\" }),\n});\nif (!response.ok) throw new Error(\"Uh-oh!\");\nconst result = await response.json();\nconst data = toe(result);\n```\n\n### Relay\n\nRelay has native support for error handling via the\n[@throwOnFieldError](https://relay.dev/docs/guides/throw-on-field-error-directive/)\nand [@catch](https://relay.dev/docs/guides/catch-directive/) directives.\n\n## Zero dependencies\n\n**Just 512 bytes** gzipped\n([v1.0.0-rc.0 on bundlephobia](https://bundlephobia.com/package/graphql-toe@1.0.0-rc.0))\n\nWorks with _any_ GraphQL client that returns `{ data, errors }`.\n\nErrors are thrown as-is; you can pre-process them to wrap in `Error` or\n`GraphQLError` if needed:\n\n```ts\nimport { GraphQLError } from \"graphql\";\nimport { toe } from \"graphql-toe\";\n\nconst mappedResult = {\n  ...result,\n  errors: result.errors?.map(\n    (e) =\u003e\n      new GraphQLError(e.message, {\n        positions: e.positions,\n        path: e.path,\n        originalError: e,\n        extensions: e.extensions,\n      }),\n  ),\n};\nconst data = toe(mappedResult);\n```\n\n## Semantic nullability\n\nThe\n[@semanticNonNull](https://specs.apollo.dev/nullability/v0.4/#@semanticNonNull)\ndirective lets schema designers mark fields where `null` is **never a valid\nvalue**; so if you see `null`, it means an error occurred.\n\nNormally this intent is lost and clients still need to check for `null`, but\nwith `toe()` you can treat these fields as non-nullable: a `null` here will\nthrow.\n\nIn TypeScript, use\n[semanticToStrict from graphql-sock](https://github.com/graphile/graphql-sock?tab=readme-ov-file#semantic-to-strict)\nto rewrite semantic-non-null to traditional non-null for type generation.\n\nTogether, this combination gives you:\n\n- More accurate codegen types\n- Improved DX with fewer null checks\n- Safer, cleaner client code\n\n## Motivation\n\nOn the server side, GraphQL captures errors, replaces them in the returned\n`data` with a `null`, and adds them to the `errors` array. Clients typically\nthen have to look at `data` and `errors` in combination to determine if a `null`\nis a \"true null\" (just a `null` value) or an \"error null\" (a `null` with a\nmatching error in the `errors` list). This is unwieldy.\n\nI see the future of GraphQL as errors being handled on the client side, and\nerror propagation being disabled on the server. Over time, I hope all major\nGraphQL clients will integrate error handling deep into their architecture, but\nin the mean time this project can add support for this future behavior to almost\nany GraphQL client by re-introducing thrown errors into your data. Handle errors\nthe way your programming language or framework is designed to — no need for\nGraphQL-specific logic.\n\n## Deeper example\n\n```ts\nimport { toe } from \"graphql-toe\";\n\n// Example data from GraphQL\nconst result = {\n  data: {\n    deep: {\n      withList: [\n        { int: 1 },\n        {\n          /* `null` because an error occurred */\n          int: null,\n        },\n        { int: 3 },\n      ],\n    },\n  },\n  errors: [\n    {\n      message: \"Two!\",\n      // When you read from this path, an error will be thrown\n      path: [\"deep\", \"withList\", 1, \"int\"],\n    },\n  ],\n};\n\n// TOE'd data:\nconst data = toe(result);\n\n// Returns `3`:\ndata.deep.withList[2].int;\n\n// Returns an object with the key `int`\ndata.deep.withList[1];\n\n// Throws the error `Two!`\ndata.deep.withList[1].int;\n```\n\n## TODO\n\n- [ ] Add support for incremental delivery\n\n## History\n\nVersion 0.1.0 of this module was released from the San Francisco Centre the day\nafter GraphQLConf 2024, following many fruitful discussions around nullability.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphile%2Fgraphql-toe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgraphile%2Fgraphql-toe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraphile%2Fgraphql-toe/lists"}