Showing preview only (2,713K chars total). Download the full file or copy to clipboard to get everything.
Repository: urql-graphql/urql
Branch: main
Commit: 71f049c6abbb
Files: 671
Total size: 2.5 MB
Directory structure:
gitextract_87_w7_87/
├── .changeset/
│ ├── README.md
│ ├── config.json
│ ├── late-boats-listen.md
│ └── shiny-pets-give.md
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODEOWNERS
│ ├── ISSUE_TEMPLATE/
│ │ ├── RFC.md
│ │ ├── bug_report.yaml
│ │ └── config.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── actions/
│ │ ├── discord-message/
│ │ │ ├── action.mjs
│ │ │ └── action.yml
│ │ └── pnpm-run/
│ │ ├── action.mjs
│ │ └── action.yml
│ └── workflows/
│ ├── ci.yml
│ ├── mirror.yml
│ └── release.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs/
│ ├── README.md
│ ├── advanced/
│ │ ├── README.md
│ │ ├── authentication.md
│ │ ├── authoring-exchanges.md
│ │ ├── auto-populate-mutations.md
│ │ ├── debugging.md
│ │ ├── persistence-and-uploads.md
│ │ ├── retry-operations.md
│ │ ├── server-side-rendering.md
│ │ ├── subscriptions.md
│ │ └── testing.md
│ ├── api/
│ │ ├── README.md
│ │ ├── auth-exchange.md
│ │ ├── core.md
│ │ ├── execute-exchange.md
│ │ ├── graphcache.md
│ │ ├── preact.md
│ │ ├── refocus-exchange.md
│ │ ├── request-policy-exchange.md
│ │ ├── retry-exchange.md
│ │ ├── svelte.md
│ │ ├── urql.md
│ │ └── vue.md
│ ├── architecture.md
│ ├── basics/
│ │ ├── README.md
│ │ ├── core.md
│ │ ├── document-caching.md
│ │ ├── errors.md
│ │ ├── react-preact.md
│ │ ├── solid-start.md
│ │ ├── solid.md
│ │ ├── svelte.md
│ │ ├── typescript-integration.md
│ │ ├── ui-patterns.md
│ │ └── vue.md
│ ├── comparison.md
│ ├── graphcache/
│ │ ├── README.md
│ │ ├── cache-updates.md
│ │ ├── errors.md
│ │ ├── local-directives.md
│ │ ├── local-resolvers.md
│ │ ├── normalized-caching.md
│ │ ├── offline.md
│ │ └── schema-awareness.md
│ └── showcase.md
├── examples/
│ ├── README.md
│ ├── pnpm-workspace.yaml
│ ├── with-apq/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── LocationsList.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-defer-stream-directives/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── server/
│ │ │ ├── apollo-server.js
│ │ │ ├── graphql-yoga.js
│ │ │ └── schema.js
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── Songs.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-graphcache-pagination/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── PaginatedNpmSearch.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-graphcache-updates/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── client.js
│ │ │ ├── index.jsx
│ │ │ └── pages/
│ │ │ ├── Links.jsx
│ │ │ └── LoginForm.jsx
│ │ └── vite.config.js
│ ├── with-infinite-pagination/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── SearchResults.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-multipart/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── FileUpload.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-next/
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── layout.tsx
│ │ │ ├── non-rsc/
│ │ │ │ ├── layout.tsx
│ │ │ │ └── page.tsx
│ │ │ └── page.tsx
│ │ ├── next-env.d.ts
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── with-pagination/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── PaginatedNpmSearch.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-react/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── PokemonList.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-react-native/
│ │ ├── App.js
│ │ ├── README.md
│ │ ├── app.json
│ │ ├── index.js
│ │ ├── package.json
│ │ └── src/
│ │ └── screens/
│ │ └── PokemonList.js
│ ├── with-refresh-auth/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── authStore.js
│ │ │ ├── client.js
│ │ │ ├── index.jsx
│ │ │ └── pages/
│ │ │ ├── LoginForm.jsx
│ │ │ └── Profile.jsx
│ │ └── vite.config.js
│ ├── with-retry/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── Color.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-solid/
│ │ ├── .eslintrc.js
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── PokemonList.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-solid-start/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app.config.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.tsx
│ │ │ ├── entry-client.tsx
│ │ │ ├── entry-server.tsx
│ │ │ └── routes/
│ │ │ └── index.tsx
│ │ └── tsconfig.json
│ ├── with-subscriptions-via-fetch/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── server/
│ │ │ ├── graphql-yoga.js
│ │ │ └── schema.js
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── Songs.jsx
│ │ │ └── index.jsx
│ │ └── vite.config.js
│ ├── with-svelte/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.svelte
│ │ │ ├── PokemonList.svelte
│ │ │ └── main.js
│ │ └── vite.config.mjs
│ └── with-vue3/
│ ├── .gitignore
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── App.vue
│ │ ├── PokemonList.vue
│ │ └── main.js
│ └── vite.config.js
├── exchanges/
│ ├── auth/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── authExchange.test.ts
│ │ │ ├── authExchange.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── context/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── context.test.ts
│ │ │ ├── context.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── execute/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── execute.test.ts
│ │ │ ├── execute.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── graphcache/
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── benchmarks/
│ │ │ ├── 10000Reads.html
│ │ │ ├── 10000ReadsComplex.html
│ │ │ ├── 10000Writes.html
│ │ │ ├── 10000WritesComplex.html
│ │ │ ├── 1000Reads.html
│ │ │ ├── 1000ReadsComplex.html
│ │ │ ├── 1000Writes.html
│ │ │ ├── 1000WritesComplex.html
│ │ │ ├── 100Reads.html
│ │ │ ├── 100ReadsComplex.html
│ │ │ ├── 100Writes.html
│ │ │ ├── 100WritesComplex.html
│ │ │ ├── 50000Reads.html
│ │ │ ├── 50000Writes.html
│ │ │ ├── 5000Reads.html
│ │ │ ├── 5000Writes.html
│ │ │ ├── 500Reads.html
│ │ │ ├── 500Writes.html
│ │ │ ├── addTodo.html
│ │ │ ├── benchmarks.js
│ │ │ ├── entities.js
│ │ │ ├── makeEntries.js
│ │ │ ├── operations.js
│ │ │ ├── package.json
│ │ │ ├── readMe.md
│ │ │ ├── updateTodo.html
│ │ │ └── urqlClient.js
│ │ ├── cypress/
│ │ │ ├── fixtures/
│ │ │ │ └── example.json
│ │ │ ├── plugins/
│ │ │ │ └── index.js
│ │ │ └── support/
│ │ │ ├── component-index.html
│ │ │ └── component.js
│ │ ├── cypress.config.js
│ │ ├── e2e-tests/
│ │ │ ├── query.spec.tsx
│ │ │ └── updates.spec.tsx
│ │ ├── help.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── ast/
│ │ │ │ ├── graphql.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── node.ts
│ │ │ │ ├── schema.ts
│ │ │ │ ├── schemaPredicates.test.ts
│ │ │ │ ├── schemaPredicates.ts
│ │ │ │ ├── traversal.test.ts
│ │ │ │ ├── traversal.ts
│ │ │ │ ├── variables.test.ts
│ │ │ │ └── variables.ts
│ │ │ ├── cacheExchange-types.test.ts
│ │ │ ├── cacheExchange.test.ts
│ │ │ ├── cacheExchange.ts
│ │ │ ├── default-storage/
│ │ │ │ └── index.ts
│ │ │ ├── extras/
│ │ │ │ ├── index.ts
│ │ │ │ ├── relayPagination.test.ts
│ │ │ │ ├── relayPagination.ts
│ │ │ │ ├── simplePagination.test.ts
│ │ │ │ └── simplePagination.ts
│ │ │ ├── helpers/
│ │ │ │ ├── help.ts
│ │ │ │ └── operation.ts
│ │ │ ├── index.ts
│ │ │ ├── offlineExchange.test.ts
│ │ │ ├── offlineExchange.ts
│ │ │ ├── operations/
│ │ │ │ ├── invalidate.ts
│ │ │ │ ├── query.test.ts
│ │ │ │ ├── query.ts
│ │ │ │ ├── shared.test.ts
│ │ │ │ ├── shared.ts
│ │ │ │ ├── write.test.ts
│ │ │ │ └── write.ts
│ │ │ ├── store/
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ └── store.test.ts.snap
│ │ │ │ ├── data.test.ts
│ │ │ │ ├── data.ts
│ │ │ │ ├── keys.ts
│ │ │ │ ├── store.test.ts
│ │ │ │ └── store.ts
│ │ │ ├── test-utils/
│ │ │ │ ├── altered_root_schema.json
│ │ │ │ ├── examples-1.test.ts
│ │ │ │ ├── examples-2.test.ts
│ │ │ │ ├── examples-3.test.ts
│ │ │ │ ├── relayPagination_schema.json
│ │ │ │ ├── simple_schema.json
│ │ │ │ ├── suite.test.ts
│ │ │ │ └── utils.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── persisted/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── persistedExchange.test.ts
│ │ │ ├── persistedExchange.ts
│ │ │ ├── sha256.ts
│ │ │ └── test-utils.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── populate/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── helpers/
│ │ │ │ ├── help.ts
│ │ │ │ ├── node.ts
│ │ │ │ └── traverse.ts
│ │ │ ├── index.ts
│ │ │ ├── populateExchange.test.ts
│ │ │ └── populateExchange.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── refocus/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── refocusExchange.test.ts
│ │ │ └── refocusExchange.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── request-policy/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── requestPolicyExchange.test.ts
│ │ │ └── requestPolicyExchange.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── retry/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── retryExchange.test.ts
│ │ │ └── retryExchange.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ └── throw-on-error/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── jsr.json
│ ├── package.json
│ ├── src/
│ │ ├── index.ts
│ │ ├── throwOnErrorExchange.test.ts
│ │ └── throwOnErrorExchange.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── package.json
├── packages/
│ ├── core/
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── __snapshots__/
│ │ │ │ └── client.test.ts.snap
│ │ │ ├── client.test.ts
│ │ │ ├── client.ts
│ │ │ ├── exchanges/
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ ├── fetch.test.ts.snap
│ │ │ │ │ └── subscription.test.ts.snap
│ │ │ │ ├── cache.test.ts
│ │ │ │ ├── cache.ts
│ │ │ │ ├── compose.test.ts
│ │ │ │ ├── compose.ts
│ │ │ │ ├── debug.test.ts
│ │ │ │ ├── debug.ts
│ │ │ │ ├── fallback.test.ts
│ │ │ │ ├── fallback.ts
│ │ │ │ ├── fetch.test.ts
│ │ │ │ ├── fetch.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── map.test.ts
│ │ │ │ ├── map.ts
│ │ │ │ ├── ssr.test.ts
│ │ │ │ ├── ssr.ts
│ │ │ │ ├── subscription.test.ts
│ │ │ │ └── subscription.ts
│ │ │ ├── gql.test.ts
│ │ │ ├── gql.ts
│ │ │ ├── index.ts
│ │ │ ├── internal/
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ └── fetchSource.test.ts.snap
│ │ │ │ ├── fetchOptions.test.ts
│ │ │ │ ├── fetchOptions.ts
│ │ │ │ ├── fetchSource.test.ts
│ │ │ │ ├── fetchSource.ts
│ │ │ │ └── index.ts
│ │ │ ├── test-utils/
│ │ │ │ ├── index.ts
│ │ │ │ └── samples.ts
│ │ │ ├── types.ts
│ │ │ └── utils/
│ │ │ ├── __snapshots__/
│ │ │ │ └── error.test.ts.snap
│ │ │ ├── collectTypenames.test.ts
│ │ │ ├── collectTypenames.ts
│ │ │ ├── error.test.ts
│ │ │ ├── error.ts
│ │ │ ├── formatDocument.test.ts
│ │ │ ├── formatDocument.ts
│ │ │ ├── graphql.ts
│ │ │ ├── hash.test.ts
│ │ │ ├── hash.ts
│ │ │ ├── index.ts
│ │ │ ├── operation.ts
│ │ │ ├── request.test.ts
│ │ │ ├── request.ts
│ │ │ ├── result.test.ts
│ │ │ ├── result.ts
│ │ │ ├── streamUtils.ts
│ │ │ ├── variables.test.ts
│ │ │ └── variables.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── introspection/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── getIntrospectedSchema.ts
│ │ │ ├── index.ts
│ │ │ └── minifyIntrospectionQuery.ts
│ │ └── tsconfig.json
│ ├── next-urql/
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── DataHydrationContext.ts
│ │ │ ├── Provider.ts
│ │ │ ├── htmlescape.ts
│ │ │ ├── index.ts
│ │ │ ├── rsc.ts
│ │ │ ├── useQuery.ts
│ │ │ └── useUrqlValue.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── preact-urql/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Mutation.test.tsx
│ │ │ │ ├── Mutation.ts
│ │ │ │ ├── Query.test.tsx
│ │ │ │ ├── Query.ts
│ │ │ │ ├── Subscription.test.tsx
│ │ │ │ ├── Subscription.ts
│ │ │ │ └── index.ts
│ │ │ ├── context.ts
│ │ │ ├── hooks/
│ │ │ │ ├── constants.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── useMutation.test.tsx
│ │ │ │ ├── useMutation.ts
│ │ │ │ ├── useQuery.test.tsx
│ │ │ │ ├── useQuery.ts
│ │ │ │ ├── useRequest.ts
│ │ │ │ ├── useSource.ts
│ │ │ │ ├── useSubscription.test.tsx
│ │ │ │ └── useSubscription.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── react-urql/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── core/
│ │ │ ├── index.d.ts
│ │ │ ├── index.esm.js
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── cypress/
│ │ │ ├── fixtures/
│ │ │ │ └── example.json
│ │ │ └── support/
│ │ │ ├── component-index.html
│ │ │ └── component.js
│ │ ├── cypress.config.js
│ │ ├── e2e-tests/
│ │ │ └── useQuery.spec.tsx
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Mutation.test.tsx
│ │ │ │ ├── Mutation.ts
│ │ │ │ ├── Query.test.tsx
│ │ │ │ ├── Query.ts
│ │ │ │ ├── Subscription.ts
│ │ │ │ └── index.ts
│ │ │ ├── context.ts
│ │ │ ├── hooks/
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ ├── useMutation.test.tsx.snap
│ │ │ │ │ ├── useQuery.test.tsx.snap
│ │ │ │ │ └── useSubscription.test.tsx.snap
│ │ │ │ ├── cache.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── state.ts
│ │ │ │ ├── useMutation.test.tsx
│ │ │ │ ├── useMutation.ts
│ │ │ │ ├── useQuery.spec.ts
│ │ │ │ ├── useQuery.test.tsx
│ │ │ │ ├── useQuery.ts
│ │ │ │ ├── useRequest.test.ts
│ │ │ │ ├── useRequest.ts
│ │ │ │ ├── useSubscription.test.tsx
│ │ │ │ └── useSubscription.ts
│ │ │ ├── index.ts
│ │ │ └── test-utils/
│ │ │ └── ssr.test.tsx
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── site/
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── plugins/
│ │ │ ├── assets-fix/
│ │ │ │ └── node.api.js
│ │ │ ├── monorepo-fix/
│ │ │ │ └── node.api.js
│ │ │ ├── preact/
│ │ │ │ └── node.api.js
│ │ │ └── react-router/
│ │ │ └── browser.api.js
│ │ ├── public/
│ │ │ ├── browserconfig.xml
│ │ │ └── site.webmanifest
│ │ ├── src/
│ │ │ ├── analytics.js
│ │ │ ├── app.js
│ │ │ ├── assets/
│ │ │ │ ├── anchor.js
│ │ │ │ └── chevron.js
│ │ │ ├── components/
│ │ │ │ ├── body-copy.js
│ │ │ │ ├── button.js
│ │ │ │ ├── footer.js
│ │ │ │ ├── header.js
│ │ │ │ ├── link.js
│ │ │ │ ├── loading.js
│ │ │ │ ├── markdown.js
│ │ │ │ ├── mdx.js
│ │ │ │ ├── navigation.js
│ │ │ │ ├── panel.js
│ │ │ │ ├── scroll-to-top.js
│ │ │ │ ├── secondary-title.js
│ │ │ │ ├── section-title.js
│ │ │ │ ├── sidebar-search-input.js
│ │ │ │ ├── sidebar.js
│ │ │ │ └── wrapper.js
│ │ │ ├── constants.js
│ │ │ ├── google-analytics.js
│ │ │ ├── google-tag-manager.js
│ │ │ ├── html.js
│ │ │ ├── index.js
│ │ │ ├── screens/
│ │ │ │ ├── 404/
│ │ │ │ │ ├── 404.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── docs/
│ │ │ │ │ ├── article.js
│ │ │ │ │ ├── header.js
│ │ │ │ │ └── index.js
│ │ │ │ └── home/
│ │ │ │ ├── _content.js
│ │ │ │ ├── features.js
│ │ │ │ ├── get-started.js
│ │ │ │ ├── hero.js
│ │ │ │ ├── index.js
│ │ │ │ └── more-oss.js
│ │ │ └── styles/
│ │ │ ├── global.js
│ │ │ └── theme.js
│ │ ├── static.config.js
│ │ └── vercel.json
│ ├── solid-start-urql/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── context.test.tsx
│ │ │ ├── context.ts
│ │ │ ├── createMutation.test.ts
│ │ │ ├── createMutation.ts
│ │ │ ├── createQuery.test.tsx
│ │ │ ├── createQuery.ts
│ │ │ ├── createSubscription.test.ts
│ │ │ ├── createSubscription.ts
│ │ │ ├── index.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── solid-urql/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── context.ts
│ │ │ ├── createMutation.test.ts
│ │ │ ├── createMutation.ts
│ │ │ ├── createQuery.test.tsx
│ │ │ ├── createQuery.ts
│ │ │ ├── createSubscription.test.ts
│ │ │ ├── createSubscription.ts
│ │ │ ├── index.ts
│ │ │ ├── suspense.test.tsx
│ │ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── storage-rn/
│ │ ├── CHANGELOG.md
│ │ ├── LICENCE
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── makeAsyncStorage.test.ts
│ │ │ └── makeAsyncStorage.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── svelte-urql/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── jsr.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── common.ts
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ ├── mutationStore.test.ts
│ │ │ ├── mutationStore.ts
│ │ │ ├── queryStore.test.ts
│ │ │ ├── queryStore.ts
│ │ │ ├── subscriptionStore.test.ts
│ │ │ └── subscriptionStore.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ └── vue-urql/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── jsr.json
│ ├── package.json
│ ├── src/
│ │ ├── index.ts
│ │ ├── useClient.test.ts
│ │ ├── useClient.ts
│ │ ├── useClientHandle.ts
│ │ ├── useMutation.test.ts
│ │ ├── useMutation.ts
│ │ ├── useQuery.test.ts
│ │ ├── useQuery.ts
│ │ ├── useSubscription.test.ts
│ │ ├── useSubscription.ts
│ │ └── utils.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── pnpm-workspace.yaml
├── scripts/
│ ├── actions/
│ │ ├── build-all.mjs
│ │ ├── lib/
│ │ │ ├── commands.mjs
│ │ │ ├── constants.mjs
│ │ │ ├── github.mjs
│ │ │ └── packages.mjs
│ │ └── pack-all.mjs
│ ├── babel/
│ │ ├── transform-debug-target.mjs
│ │ ├── transform-invariant-warning.mjs
│ │ └── transform-pipe.mjs
│ ├── changesets/
│ │ ├── changelog.js
│ │ ├── jsr.mjs
│ │ └── version.mjs
│ ├── eslint/
│ │ └── preset.js
│ ├── prepare/
│ │ ├── index.js
│ │ └── postinstall.js
│ ├── rollup/
│ │ ├── cleanup-plugin.mjs
│ │ ├── config.mjs
│ │ ├── plugins.mjs
│ │ └── settings.mjs
│ └── vitest/
│ └── setup.js
├── tsconfig.json
├── vercel.json
└── vitest.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .changeset/README.md
================================================
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md)
================================================
FILE: .changeset/config.json
================================================
{
"$schema": "https://unpkg.com/@changesets/config@0.3.0/schema.json",
"changelog": "../scripts/changesets/changelog.js",
"commit": false,
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "minor",
"snapshot": {
"prereleaseTemplate": "{tag}-{commit}",
"useCalculatedVersion": true
},
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true,
"updateInternalDependents": "out-of-range"
}
}
================================================
FILE: .changeset/late-boats-listen.md
================================================
---
'@urql/solid-start': minor
---
Fix SSR runtime failures caused by importing SolidStart's `action` API at module load time by reading `action` from `Provider` context instead.
================================================
FILE: .changeset/shiny-pets-give.md
================================================
---
'@urql/solid-start': patch
---
Fix `createSubscription` to use `@urql/solid-start` context instead of re-exporting the Solid-only implementation from `@urql/solid`.
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_size = 2
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = 100
trim_trailing_whitespace = false
[COMMIT_EDITMSG]
max_line_length = 0
================================================
FILE: .gitattributes
================================================
* text=auto
================================================
FILE: .github/CODEOWNERS
================================================
/.github/ @urql-graphql/core
/.changeset/config.json @urql-graphql/core
/scripts/actions/* @urql-graphql/core
/scripts/prepare/* @urql-graphql/core
/scripts/rollup/* @urql-graphql/core
/scripts/changesets/* @urql-graphql/core
================================================
FILE: .github/ISSUE_TEMPLATE/RFC.md
================================================
---
name: 'RFC'
about: Propose an enhancement / feature and start a discussion
title: 'RFC: Your Proposal'
labels: "future \U0001F52E"
---
<!--
🚨 RFCs are for proposed changes (not bugs or questions)
Specifically they are whenever you'd like to see new features
being added to urql, or enable new use-cases.
Please open a Bug Report for issues/bugs, and use GitHub Discussions
or the Discord channel for questions instead.
-->
## Summary
<!--
Describe in a couple of words *what* you're proposing.
If relevant, include *why* this should be addressed now.
The problem should be clearly stated and the solution
should be summarised.
-->
## Proposed Solution
<!--
Explain the solution you're proposing in detail.
*How* will this change be implemented, and how does it work?
-->
## Requirements
<!--
This section is *optional*.
But if your proposed solution has multiple ways
of being implemented, you don't want to state how
it may be implemented, or you don't know yet how
it will be implemented, then:
*List* what the implementation needs to achieve to fulfil this RFC;
-->
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yaml
================================================
name: "\U0001F41E Bug report"
description: Report an issue with urql
labels: []
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: Please describe your bug clearly and concisely.
placeholder: Bug description
validations:
required: true
- type: input
id: reproduction
attributes:
label: Reproduction
description: Please provide a link to a reproduction. Templates can be found in the [examples folder](https://github.com/urql-graphql/urql/tree/main/examples). A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required. If a report is vague (e.g. just a generic error message) and if no reproduction is provided the issue will be auto-closed.
placeholder: Reproduction
validations:
required: true
- type: textarea
id: urql-version
attributes:
label: Urql version
description: The versions of the relevant urql packages you are using
placeholder: urql v2.0.0
validations:
required: true
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: I can confirm that this is a bug report, and not a feature request, RFC, question, or discussion, for which [GitHub Discussions](https://github.com/urql-graphql/urql/discussions) should be used
required: true
- label: Read the [docs](https://formidable.com/open-source/urql/docs/).
required: true
- label: Follow our [Code of Conduct](https://github.com/urql-graphql/urql/blob/main/CODE_OF_CONDUCT.md)
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: Ask a question
url: https://github.com/urql-graphql/urql/discussions
about: Ask questions and discuss with other community members
- name: Join the Discord
url: https://urql.dev/discord
about: Chat with maintainers and other community members
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thanks for opening a pull request! We appreciate your dedication and help!
Before submitting your pull request, please make sure to read our CONTRIBUTING guide.
The best contribution is always a PR, but please make sure to open an issue or discuss
your changes first, if you’re looking to submit a larger PR.
If this PR is already related to an issue, please reference it like so:
Resolves #123
-->
## Summary
<!-- What's the motivation of this change? What does it solve? -->
## Set of changes
<!--
Roughly list the changes you've made and which packages are affected.
Leave some notes on what may be noteworthy files you've changed.
And lastly, please let us know if you think this is a breaking change.
-->
================================================
FILE: .github/actions/discord-message/action.mjs
================================================
import * as core from '@actions/core';
import * as github from '@actions/github';
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL;
const octokit = github.getOctokit(GITHUB_TOKEN);
const formatBody = input => {
const titleRe = /(?:^|\n)#+[^\n]+/g;
const updatedDepsRe = /\n-\s*Updated dependencies[\s\S]+\n(\n\s+-[\s\S]+)*/gi;
const markdownLinkRe = /\[([^\]]+)\]\(([^\)]+)\)/g;
const creditRe = new RegExp(
`Submitted by (?:undefined|${markdownLinkRe.source})`,
'ig'
);
const repeatedNewlineRe = /(?:\n[ ]*)*(\n[ ]*)/g;
return input
.replace(titleRe, '')
.replace(updatedDepsRe, '')
.replace(creditRe, (_match, text, url) => {
if (!text || /@kitten|@JoviDeCroock/i.test(text)) return '';
return `Submitted by [${text}](${url})`;
})
.replace(markdownLinkRe, (_match, text, url) => `[${text}](<${url}>)`)
.replace(repeatedNewlineRe, (_match, text) => (text ? ` ${text}` : '\n'))
.trim();
};
async function getReleaseBody(name, version) {
const tag = `${name}@${version}`;
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
const result = await octokit.rest.repos.getReleaseByTag({ owner, repo, tag });
const release = result.status === 200 ? result.data : undefined;
if (!release || !release.body) return;
const title = `:package: [${tag}](<${release.html_url}>)`;
const body = formatBody(release.body);
if (!body) return;
return `${title}\n${body}`;
}
async function main() {
const inputPackages = core.getInput('publishedPackages');
let packages;
try {
packages = JSON.parse(inputPackages);
} catch (e) {
console.error('invalid JSON in publishedPackages input.');
return;
}
// Get releases
const releasePromises = packages.map(entry => {
return getReleaseBody(entry.name, entry.version);
});
const content = (await Promise.allSettled(releasePromises))
.map(x => x.status === 'fulfilled' && x.value)
.filter(Boolean)
.join('\n\n');
// Send message through a discord webhook or bot
const response = await fetch(WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ content }),
});
if (!response.ok) {
console.error(
'Something went wrong while sending the discord webhook.',
response.status
);
console.error(await response.text());
}
}
main().then().catch(console.error);
================================================
FILE: .github/actions/discord-message/action.yml
================================================
name: 'Send a discord message'
description: 'Send a discord message as a result of an urql publish.'
inputs:
publishedPackages:
description: >
A JSON array to present the published packages. The format is `[{"name": "@xx/xx", "version": "1.2.0"}, {"name": "@xx/xy", "version": "0.8.9"}]`
runs:
using: 'node20'
main: 'action.mjs'
================================================
FILE: .github/actions/pnpm-run/action.mjs
================================================
import { execa } from 'execa';
const run = execa('pnpm', ['run', process.env.INPUT_COMMAND], {
cwd: process.cwd(),
});
run.stdout.pipe(process.stdout);
run.stderr.pipe(process.stderr);
run
.then(result => process.exit(result.exitCode))
.catch(error => process.exit(error.exitCode || -1));
================================================
FILE: .github/actions/pnpm-run/action.yml
================================================
name: 'Run a pnpm command'
description: 'Locally run a forked pnpm command as an action.'
inputs:
command:
description: 'Command'
default: 'help'
runs:
using: 'node20'
main: 'action.mjs'
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
pull_request_review:
types: [submitted, edited]
branches: changeset-release/main
jobs:
check:
name: Checks
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
run_install: false
- name: Get pnpm store directory
id: pnpm-store
run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Use pnpm store
uses: actions/cache@v4
id: pnpm-cache
with:
path: |
~/.cache/Cypress
${{ steps.pnpm-store.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: TypeScript
run: pnpm run check
- name: Linting
run: pnpm run lint
- name: Unit Tests
run: pnpm run test
- name: Check for slow types
run: pnpm jsr:dryrun
react-e2e:
name: React E2E
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
run_install: false
- name: Get pnpm store directory
id: pnpm-store
run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Use pnpm store
uses: actions/cache@v4
id: pnpm-cache
with:
path: |
~/.cache/Cypress
${{ steps.pnpm-store.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Build
run: pnpm -F @urql/core build && pnpm -F urql build
- name: e2e tests 🧪
uses: cypress-io/github-action@v6
with:
install: false
command: pnpm cypress run --component
working-directory: packages/react-urql
graphcache-e2e:
name: Graphcache E2E
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
run_install: false
- name: Get pnpm store directory
id: pnpm-store
run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Use pnpm store
uses: actions/cache@v4
id: pnpm-cache
with:
path: |
~/.cache/Cypress
${{ steps.pnpm-store.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Build
run: pnpm -F "@urql/core" -F urql -F "@urql/exchange-execute" build
- name: e2e tests 🧪
uses: cypress-io/github-action@v6
with:
install: false
command: pnpm cypress run --component
working-directory: exchanges/graphcache
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
matrix:
node: [0, 1, 2]
env:
NODE_TOTAL: 3
NODE_INDEX: ${{matrix.node}}
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
run_install: false
- name: Get pnpm store directory
id: pnpm-store
run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Use pnpm store
uses: actions/cache@v4
id: pnpm-cache
with:
path: |
~/.cache/Cypress
${{ steps.pnpm-store.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Build
run: pnpm build
- name: Pack
uses: ./.github/actions/pnpm-run
with:
command: pack
================================================
FILE: .github/workflows/mirror.yml
================================================
# Mirrors to https://tangled.sh/@kitten.sh (knot.kitten.sh)
name: Mirror (Git Backup)
on:
push:
branches:
- main
jobs:
mirror:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Mirror
env:
MIRROR_SSH_KEY: ${{ secrets.MIRROR_SSH_KEY }}
GIT_SSH_COMMAND: 'ssh -o StrictHostKeyChecking=yes'
run: |
mkdir -p ~/.ssh
echo "$MIRROR_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H knot.kitten.sh >> ~/.ssh/known_hosts
git remote add mirror "git@knot.kitten.sh:kitten.sh/${GITHUB_REPOSITORY#*/}"
git push --mirror mirror
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: write
id-token: write
issues: write
repository-projects: write
deployments: write
packages: write
pull-requests: write
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: "https://registry.npmjs.org"
- name: Update npm
run: npm install -g npm@latest
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
run_install: false
- name: Get pnpm store directory
id: pnpm-store
run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Use pnpm store
uses: actions/cache@v4
id: pnpm-cache
with:
path: |
~/.cache/Cypress
${{ steps.pnpm-store.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install
- name: PR or Publish
id: changesets
uses: changesets/action@v1.5.3
with:
version: pnpm changeset:version
publish: pnpm changeset:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify discord
id: discord-msg
if: steps.changesets.outputs.published == 'true'
uses: ./.github/actions/discord-message
with:
publishedPackages: ${{ steps.changesets.outputs.publishedPackages }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
- name: Publish Prerelease
if: steps.changesets.outputs.published != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git reset --hard origin/main
pnpm changeset version --no-git-tag --snapshot canary
pnpm changeset publish --no-git-tag --snapshot canary --tag canary
================================================
FILE: .gitignore
================================================
/.idea
/.vscode
**/node_modules
*.log
.rts2_cache*
.husky
dist/
build/
coverage/
package-lock.json
.DS_Store
.next
packages/*/LICENSE
exchanges/*/LICENSE
# TODO: Figure out how to remove these:
tmp/
dist/
examples/yarn.lock
examples/pnpm-lock.yaml
examples/package-lock.json
examples/*/public
examples/*/yarn.lock
examples/*/pnpm-lock.yaml
examples/*/package-lock.json
examples/*/ios/
examples/*/android/
examples/*/.watchmanconfig
examples/*/metro.config.js
examples/*/babel.config.js
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team:
- phil@0no.co
- grant.sander@formidable.com
- jovi@preact.dev
All complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Development
Thanks for contributing! We want to ensure that `urql` evolves and fulfills
its idea of extensibility and flexibility by seeing continuous improvements
and enhancements, no matter how small or big they might be.
If you're about to add a new exchange, please consider publishing it as
a separate package.
## How to contribute?
We follow fairly standard but lenient rules around pull requests and issues.
Please pick a title that describes your change briefly, optionally in the imperative
mood if possible.
If you have an idea for a feature or want to fix a bug, consider opening an issue
first. We're also happy to discuss and help you open a PR and get your changes
in!
- If you have a question, try [creating a GitHub Discussions thread.](https://github.com/urql-graphql/urql/discussions/new)
- If you think you've found a bug, [open a new issue.](https://github.com/urql-graphql/urql/issues/new/choose)
- or, if you found a bug you'd like to fix, [open a PR.](https://github.com/urql-graphql/urql/compare)
- If you'd like to propose a change [open an RFC issue.](https://github.com/urql-graphql/urql/issues/new?labels=future+%F0%9F%94%AE&template=RFC.md&title=RFC%3A+Your+Proposal) You can read more about the RFC process [below](#how-do-i-propose-changes).
### What are the issue conventions?
There are **no strict conventions**, but we do have two templates in place that will fit most
issues, since questions and other discussion start on GitHub Discussions. The bug template is fairly
standard and the rule of thumb is to try to explain **what you expected** and **what you got
instead.** Following this makes it very clear whether it's a known behavior, an unexpected issue,
or an undocumented quirk.
We do ask that issues _aren’t_ created for questions, or where a bug is likely to be either caused
by misusage or misconfiguration. In short, if you can’t provide a reproduction of the issue, then
it may be the case that you’ve got a question instead.
If you need a template for creating a reproduction, all of our examples can be opened in isolated
sandboxes or modified as you see fit: https://github.com/urql-graphql/urql/tree/main/examples
### How do I propose changes?
We follow an **RFC proposal process**. This allows anyone to propose a new feature or a change, and
allows us to communicate our current planned features or changes, so any technical discussion,
progress, or upcoming changes are always **documented transparently.** You can [find the RFC
template](https://github.com/urql-graphql/urql/issues/new/choose) in our issue creator.
All RFCs are added to the [RFC Lifecycle board.](https://github.com/urql-graphql/urql/projects/3)
This board tracks where an RFC stands and who's working on it until it's completed. Bugs and PRs may
end up on there too if no corresponding RFC exists or was necessary. RFCs are typically first added
to "In Discussion" until we believe they're ready to be worked on. This step may either be short,
skipped, or rather long, if no plan is in place for a change yet. So if you see a way to help,
please leave some suggestions.
### What are the PR conventions?
This also comes with **no strict conventions**. We only ask you to follow the PR template we have
in place more strictly here than the templates for issues, since it asks you to list a summary
(maybe even with a short explanation) and a list of technical changes.
If you're **resolving** an issue please don't forget to add `Resolve #123` to the description so that
it's automatically linked, so that there's no ambiguity and which issue is being addressed (if any)
You'll find that a comment by the "Changeset" bot may pop up. If you don't know what a **changeset**
is and why it's asking you to document your changes, read on at ["How do I document a change for the
changelog"](#how-do-i-document-a-change-for-the-changelog)
We also typically **name** our PRs with a slightly descriptive title, e.g. `(shortcode) - Title`,
where shortcode is either the name of a package, e.g. `(core)` and the title is an imperative mood
description, e.g. "Update X" or "Refactor Y."
## How do I set up the project?
Luckily it's not hard to get started. You can install dependencies
[using `pnpm`](https://pnpm.io/installation#using-corepack).
Please don't use `npm` or `yarn` to respect the lockfile.
```sh
pnpm install
```
There are multiple commands you can run in the root folder to test your changes:
```sh
# TypeScript checks:
pnpm run check
# Linting (prettier & eslint):
pnpm run lint
# Unit Tests (for all packages):
pnpm run test
# Builds (for all packages):
pnpm run build
```
You can find the main packages in `packages/*` and the addon exchanges in `exchanges/*`.
Each package also has its own scripts that are common and shared between all packages.
```sh
# Unit Tests for the current package:
pnpm run test
# Linting (prettier & eslint):
pnpm run lint
# Build the current package:
pnpm run build
# TypeScript checks for the current package:
pnpm run check
```
While you can run `build` globally in the interest of time it's advisable to only run it
on the packages you're working on. Note that TypeScript checks don't require any packages
to be built.
## How do I test my changes?
It's always good practice to run the tests when making changes. If you're unsure which packages
may be affected by your new tests or changes you may run `pnpm test` in the root of
the repository.
If your editor is not set up with type checks you may also want to run `pnpm run check` on your
changes.
Additionally you can head to any example in the `examples/` folder
and run them. There you'll also need to install their dependencies as they're isolated projects,
without a lockfile and without linking to packages in the monorepos.
All examples are started using the `package.json`'s `start` script.
## How do I lint my code?
We ensure consistency in `urql`'s codebase using `eslint` and `prettier`.
They are run on a `precommit` hook, so if something's off they'll try
to automatically fix up your code, or display an error.
If you have them set up in your editor, even better!
## How do I document a change for the changelog?
This project uses [changesets](https://github.com/atlassian/changesets). This means that for
every PR there must be documentation for what has been changed and which package is affected.
You can document a change by running `changeset`, which will ask you which packages
have changed and whether the change is major/minor/patch. It will then ask you to write
a change entry as markdown.
```sh
# In the root of the urql repository call:
pnpm changeset
```
This will create a new "changeset file" in the `.changeset` folder, which you should commit and
push, so that it's added to your PR.
This will eventually end up in the package's `CHANGELOG.md` file when we do a release.
You won't need to add a changeset if you're simply making "non-visible" changes to the docs or other
files that aren't published to the npm registry.
[Read more about adding a `changeset` here.](https://github.com/atlassian/changesets/blob/master/docs/adding-a-changeset.md#i-am-in-a-multi-package-repository-a-mono-repo)
## How do I release new versions of our packages?
Hold up, that's **automated**! Since we use `changeset` to document our changes, which determines what
goes into the changelog and what kind of version bump a change should make, you can also use the
tool to check what's currently posed to change after a release batch using: `pnpm changeset status`.
We have a [GitHub Actions workflow](./.github/workflow/release.yml) which is triggered whenever new
changes are merged. It will always open a **"Version Packages" PR** which is kept up-to-date. This PR
documents all changes that are made and will show in its description what all new changelogs are
going to contain for their new entries.
Once a "Version Packages" PR is approved by a contributor and merged, the action will automatically
take care of creating the release, publishing all updated packages to the npm registry, and creating
appropriate tags on GitHub too.
This process is automated, but the changelog should be checked for errors.
As to **when** to merge the automated PR and publish? Maybe not after every change. Typically there
are two release batches: hotfixes and release batches. We expect that a hotfix for a single package
should go out as quickly as possible if it negatively affects users. For **release batches**
however, it's common to assume that if one change is made to a package that more will follow in the
same week. So waiting for **a day or two** when other changes are expected will make sense to keep the
fatigue as low as possible for downstream maintainers.
## How do I upgrade all dependencies?
It may be a good idea to keep all dependencies on the `urql` repository **up-to-date** every now and
then. Typically we do this by running `pnpm update --interactive --latest` and checking one-by-one
which dependencies will need to be bumped. In case of any security issues it may make sense to
just run `pnpm update [package]`.
While this is rare with `pnpm`, upgrading some transitive dependencies may accidentally duplicate
them if two packages depend on different compatible version ranges. This can be fixed by running:
```sh
npx pnpm-deduplicate
pnpm install
```
It's common to then **create a PR** (with a changeset documenting the packages that need to reflect
new changes if any `dependencies` have changed) with the name of
"(chore) - Upgrade direct and transitive dependencies" or something similar.
## How do I add a new package?
First of all we need to know **where** to put the package.
- Exchanges should be added to `exchanges/` and the folder should be the plain
name of the exchange. Since the `package.json:name` is following the convention
of `@urql/exchange-*` the folder should just be without this conventional prefix.
- All other packages should be added to `packages/`. Typically all packages should
be named `@urql/*` and their folders should be named exactly this without the
prefix or `*-urql`. Optionally if the package will be named `*-urql` then the folder
can take on the same name.
When adding a new package, start by **copying** a `package.json` file from another project.
You may want to alter the following fields first:
- `name`
- `version` (either start at `0.1.0` or `1.0.0`)
- `description`
- `repository.directory`
- `keywords`
Make sure to also alter the `devDependencies`, `peerDependencies`, and `dependencies` to match
the new package's needs.
**The `main` and `module` fields follow a convention:**
All output bundles will always be output in the `./dist` folder by `rollup`, which is set up in
the `build` script. Their filenames are a "kebab case" (dash-cased) version of the `name` field with
an appropriate extension (`.esm.js` for `module` and `.cjs.js` for `main`).
If your entrypoint won't be at `src/index.ts` you may alter it. But the `types` field has to match
the same file relative to the `dist/types` folder, where `rollup` will output the TypeScript
declaration files.
When setting up your package make sure to create a `src/index.ts` file
(or any other file which you've pointed `package.json:source` to). Also don't forget to
copy over the `tsconfig.json` from another package (You won't need to change it).
The `scripts.prepare` task is set up to check your new `package.json` file for correctness. So in
case you get anything wrong, you'll get a short error when running `pnpm` after setting your new
project up. Just in case! 😄
Afterwards you can check whether everything is working correctly by running:
```sh
pnpm install
pnpm run check
```
At this point, **don't publish** the package or a prerelease yourself if you can avoid it. If you can't
or have already, we'll need to get the **rights** fixed by adding the package to the `@urql` scope.
Typically what we do is:
```sh
npm access grant read-write urql:developers [package]
```
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018–2020 Formidable,
Copyright (c) urql GraphQL Team and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<img alt="urql" width="250" src="packages/site/src/assets/sidebar-badge.svg" />
<br />
<br />
<strong>
A highly customisable and versatile GraphQL client
</strong>
<br />
<br />
<a href="https://github.com/urql-graphql/urql/actions/workflows/ci.yml">
<img alt="CI Status" src="https://github.com/urql-graphql/urql/actions/workflows/ci.yml/badge.svg?branch=main" />
</a>
<a href="https://www.npmjs.com/package/@urql/core">
<img alt="Weekly downloads" src="https://badgen.net/npm/dw/@urql/core?color=blue" />
</a>
<a href="https://urql.dev/discord">
<img alt="Discord" src="https://img.shields.io/discord/1082378892523864074?color=7389D8&label&logo=discord&logoColor=ffffff" />
</a>
<br />
<br />
</div>
## ✨ Features
- 📦 **One package** to get a working GraphQL client in React, Preact, Vue, Solid and Svelte
- ⚙️ Fully **customisable** behaviour [via "exchanges"](https://formidable.com/open-source/urql/docs/advanced/authoring-exchanges/)
- 🗂 Logical but simple default behaviour and document caching
- 🌱 Normalized caching via [`@urql/exchange-graphcache`](https://formidable.com/open-source/urql/docs/graphcache)
- 🔬 Easy debugging with the [`urql` devtools browser extensions](https://formidable.com/open-source/urql/docs/advanced/debugging/)
`urql` is a GraphQL client that exposes a set of helpers for several frameworks. It's built to be highly customisable and versatile so
you can take it from getting started with your first GraphQL project all the way to building complex apps and experimenting with GraphQL clients.
**📃 For more information, [check out the docs](https://formidable.com/open-source/urql/docs/).**
## 💙 [Sponsors](https://github.com/sponsors/urql-graphql)
<table>
<tr>
<td align="center"><a href="https://bigcommerce.com/"><img src="https://avatars.githubusercontent.com/u/186342?s=200&v=4" width="150" alt="BigCommerce"/><br />BigCommerce</a></td>
<td align="center"><a href="https://wundergraph.com/"><img src="https://avatars.githubusercontent.com/u/64281914?s=200&v=4" width="150" alt="WunderGraph"/><br />WunderGraph</a></td>
<td align="center"><a href="https://the-guild.dev/"><img src="https://avatars.githubusercontent.com/u/42573040?s=200&v=4" width="150" alt="The Guild "/><br />The Guild</a></td>
</tr>
</table>
<table>
<tr>
<td align="center"><a href="https://beatgig.com/"><img src="https://avatars.githubusercontent.com/u/51333382?s=200&v=4" width="100" alt="BeatGig"/><br />BeatGig</a></td>
</tr>
</table>
## 🙌 Contributing
**The urql project was founded by [Formidable](https://formidable.com/) and is actively developed
by the urql GraphQL team.**
If you'd like to get involved, [check out our Contributor's guide.](https://github.com/urql-graphql/urql/blob/main/CONTRIBUTING.md)
## 📦 [Releases](https://github.com/urql-graphql/urql/releases)
All new releases and updates are listed on GitHub with full changelogs. Each package in this
repository further contains an independent `CHANGELOG.md` file with the historical changelog, for
instance, [here’s `@urql/core`’s
changelog](https://github.com/urql-graphql/urql/blob/main/packages/core/CHANGELOG.md).
If you’re upgrading to v4, [check out our migration guide, posted as an
issue.](https://github.com/urql-graphql/urql/issues/3114)
New releases are prepared using
[changesets](https://github.com/urql-graphql/urql/blob/main/CONTRIBUTING.md#how-do-i-document-a-change-for-the-changelog),
which are changelog entries added to each PR, and we have “Version Packages” PRs that once merged
will release new versions of `urql` packages. You can use `@canary` releases from `npm` if you’d
like to get a preview of the merged changes.
## 📃 [Documentation](https://urql.dev/goto/docs)
The documentation contains everything you need to know about `urql`, and contains several sections in order of importance
when you first get started:
- **[Basics](https://formidable.com/open-source/urql/docs/basics/)** — contains the ["Getting Started" guide](https://formidable.com/open-source/urql/docs/#where-to-start) and all you need to know when first using `urql`.
- **[Architecture](https://formidable.com/open-source/urql/docs/architecture/)** — explains how `urql` functions and is built.
- **[Advanced](https://formidable.com/open-source/urql/docs/advanced/)** — covers more uncommon use-cases and things you don't immediately need when getting started.
- **[Graphcache](https://formidable.com/open-source/urql/docs/graphcache/)** — documents ["Normalized Caching" support](https://formidable.com/open-source/urql/docs/graphcache/normalized-caching/) which enables more complex apps and use-cases.
- **[API](https://formidable.com/open-source/urql/docs/api/)** — the API documentation for each individual package.
Furthermore, all APIs and packages are self-documented using TSDocs. If you’re using a language
server for TypeScript, the documentation for each API should pop up in your editor when hovering
`urql`’s code and APIs.
_You can find the raw markdown files inside this repository's `docs` folder._
<img width="100%" src="docs/assets/urql-spoiler.png" />
================================================
FILE: docs/README.md
================================================
---
title: Overview
order: 1
---
# Overview
`urql` is a highly customizable and versatile GraphQL client with which you add on features like
normalized caching as you grow. It's built to be both easy to use for newcomers to
GraphQL, and extensible, to grow to support dynamic single-app applications and highly
customized GraphQL infrastructure. In short, `urql` prioritizes usability and adaptability.
As you're adopting GraphQL, `urql` becomes your primary data layer and can handle content-heavy
pages through ["Document Caching"](./basics/document-caching.md) as well as dynamic and data-heavy
apps through ["Normalized Caching"](./graphcache/normalized-caching.md).
`urql` can be understood as a collection of connected parts and packages.
When we only need to install a single package for our framework of choice. We're then able to
declaratively send GraphQL requests to our API. All framework packages — like `urql` (for React),
`@urql/preact`, `@urql/svelte`, `@urql/solid`/`@urql/solid-start` and `@urql/vue` — wrap the [core package,
`@urql/core`](./basics/core.md), which we can imagine as the brain
of `urql` with most of its logic. As we progress with implementing `urql` into our application,
we're later able to extend it by adding ["addon packages", which we call
_Exchanges_](./advanced/authoring-exchanges.md)
If at this point you're still unsure of whether to use `urql`, [have a look at the **Comparison**
page](./comparison.md) and check whether `urql` supports all features you're looking for.
## Where to start
We have **Getting Started** guides for:
- [**React/Preact**](./basics/react-preact.md) covers how to work with the bindings for React/Preact.
- [**Vue**](./basics/vue.md) covers how to work with the bindings for Vue 3.
- [**Svelte**](./basics/svelte.md) covers how to work with the bindings for Svelte.
- [**Solid**](./basics/solid.md) covers how to work with the bindings for Solid.
- [**SolidStart**](./basics/solid-start.md) covers how to work with the bindings for SolidStart.
- [**Core Package**](./basics/core.md) covers the shared "core APIs" and how we can use them directly
in Node.js or imperatively.
Each of these sections will walk you through the specific instructions for the framework bindings,
including how to install and set them up, how to write queries, and how to send mutations.
## Following the Documentation
This documentation is split into groups or sections that cover different levels of usage or areas of
interest.
- **Basics** is the section where we'll want to start learning about `urql` as it contains "Getting
Started" guides for our framework of choice.
- **Architecture** then explains more about how `urql` functions, what it's made up of, and covers
the main aspects of the `Client` and exchanges.
- **Advanced** covers all more uncommon use-cases and contains guides that we won't need immediately
when we get started with `urql`.
- **Graphcache** documents one of the most important addons to `urql`, which adds ["Normalized
Caching" support](./graphcache/normalized-caching.md) to the `Client` and enables more complex
use-cases, smarter caching, and more dynamic apps to function.
- **Showcase** aims to list users of `urql`, third-party packages, and other helpful resources,
like tutorials and guides.
- **API** contains a detailed documentation on each package's APIs. The documentation links to each
of these as appropriate, but if we're unsure of how to use a utility or package, we can go here
directly to look up how to use a specific API.
We hope you grow to love `urql`!
================================================
FILE: docs/advanced/README.md
================================================
---
title: Advanced
order: 4
---
# Advanced
In this chapter we'll dive into various topics of "advanced" `urql` usage. This is admittedly a
catch-all chapter of various use-cases that can only be covered after [the "Architecture"
chapter.](../architecture.md)
- [**Subscriptions**](./subscriptions.md) covers how to use `useSubscription` and how to set up GraphQL subscriptions with
`urql`.
- [**Persistence & Uploads**](./persistence-and-uploads.md) teaches us how to set up Automatic
Persisted Queries and File Uploads using the two respective packages.
- [**Server-side Rendering**](./server-side-rendering.md) guides us through how to set up server-side rendering and rehydration.
- [**Debugging**](./debugging.md) shows us the [`urql`
devtools](https://github.com/urql-graphql/urql-devtools/) and how to add our own debug events
for its event view.
- [**Retrying operations**](./retry-operations.md) shows the `retryExchange` which allows you to retry operations when they've failed.
- [**Authentication**](./authentication.md) describes how to implement authentication using the `authExchange`
- [**Testing**](./testing.md) covers how to test components that use `urql` particularly in React.
- [**Authoring Exchanges**](./authoring-exchanges.md) describes how to implement exchanges from
scratch and how they work internally. This is a good basis to understanding how some
features in this section function.
- [**Auto-populate Mutations**](./auto-populate-mutations.md) presents the `populateExchange` addon, which can make it easier to
update normalized data after mutations.
================================================
FILE: docs/advanced/authentication.md
================================================
---
title: Authentication
order: 6
---
# Authentication
Most APIs include some type of authentication, usually in the form of an auth token that is sent with each request header.
The purpose of the [`authExchange`](../api/auth-exchange.md) is to provide a flexible API that facilitates the typical
JWT-based authentication flow.
> **Note:** [You can find a code example for `@urql/exchange-auth` in an example in the `urql` repository.](https://github.com/urql-graphql/urql/tree/main/examples/with-refresh-auth)
## Typical Authentication Flow
**Initial login** — the user opens the application and authenticates for the first time. They enter their credentials and receive an auth token.
The token is saved to storage that is persisted though sessions, e.g. `localStorage` on the web or `AsyncStorage` in React Native. The token is
added to each subsequent request in an auth header.
**Resume** — the user opens the application after having authenticated in the past. In this case, we should already have the token in persisted
storage. We fetch the token from storage and add to each request, usually as an auth header.
**Forced log out due to invalid token** — the user's session could become invalid for a variety reasons: their token expired, they requested to be
signed out of all devices, or their session was invalidated remotely. In this case, we would want to
also log them out in the application, so they
could have the opportunity to log in again. To do this, we want to clear any persisted storage, and redirect them to the application home or login page.
**User initiated log out** — when the user chooses to log out of the application, we usually send a logout request to the API, then clear any tokens
from persisted storage, and redirect them to the application home or login page.
**Refresh (optional)** — this is not always implemented; if your API supports it, the
user will receive both an auth token, and a refresh token.
The auth token is usually valid for a shorter duration of time (e.g. 1 week) than the refresh token
(e.g. 6 months), and the latter can be used to request a new
auth token if the auth token has expired. The refresh logic is triggered either when the JWT is known to be invalid (e.g. by decoding it and inspecting the expiry date),
or when an API request returns with an unauthorized response. For graphQL APIs, it is usually an error code, instead of a 401 HTTP response, but both can be supported.
When the token has been successfully refreshed (this can be done as a mutation to the graphQL API or a request to a different API endpoint, depending on implementation),
we will save the new token in persisted storage, and retry the failed request with the new auth header. The user should be logged out and persisted storage cleared if
the refresh fails or if the re-executing the query with the new token fails with an auth error for the second time.
## Installation & Setup
First, install the `@urql/exchange-auth` alongside `urql`:
```sh
yarn add @urql/exchange-auth
# or
npm install --save @urql/exchange-auth
```
You'll then need to add the `authExchange`, that this package exposes to your `Client`. The `authExchange` is an asynchronous exchange, so it must be placed
in front of all `fetchExchange`s but after all other synchronous exchanges, like the `cacheExchange`.
```js
import { Client, cacheExchange, fetchExchange } from 'urql';
import { authExchange } from '@urql/exchange-auth';
const client = new Client({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
authExchange(async utils => {
return {
/* config... */
};
}),
fetchExchange,
],
});
```
You pass an initialization function to the `authExchange`. This function is called by the exchange
when it first initializes. It'll let you receive an object of utilities and you must return
a (promisified) object of configuration options.
Let's discuss each of the [configuration options](../api/auth-exchange.md#options) and how to use them in turn.
### Configuring the initializer function (initial load)
The initializer function must return a promise of a configuration object and hence also gives you an
opportunity to fetch your authentication state from storage.
```js
async function initializeAuthState() {
const token = localStorage.getItem('token');
const refreshToken = localStorage.getItem('refreshToken');
return { token, refreshToken };
}
authExchange(async utils => {
let { token, refreshToken } = initializeAuthState();
return {
/* config... */
};
});
```
The first step here is to retrieve our tokens from a kind of storage, which may be asynchronous as
well, as illustrated by `initializeAuthState`.
In React Native, this is very similar, but because persisted storage in React Native is always
asynchronous and promisified, we would await our tokens. This works because the
function that `authExchange` is async, i.e. must return a `Promise`.
```js
async function initializeAuthState() {
const token = await AsyncStorage.getItem(TOKEN_KEY);
const refreshToken = await AsyncStorage.getItem(REFRESH_KEY);
return { token, refreshToken };
}
authExchange(async utils => {
let { token, refreshToken } = initializeAuthState();
return {
/* config... */
};
});
```
### Configuring `addAuthToOperation`
The purpose of `addAuthToOperation` is to apply an auth state to each request. Here, we'll use the
tokens we retrieved from storage and add them to our operations.
In this example, we're using a utility we're passed, `appendHeaders`. This utility is a simply
shortcut to quickly add HTTP headers via `fetchOptions` to an `Operation`, however, we may as well
be editing the `Operation` context here using `makeOperation`.
```js
authExchange(async utils => {
let token = await AsyncStorage.getItem(TOKEN_KEY);
let refreshToken = await AsyncStorage.getItem(REFRESH_KEY);
return {
addAuthToOperation(operation) {
if (!token) return operation;
return utils.appendHeaders(operation, {
Authorization: `Bearer ${token}`,
});
},
// ...
};
});
```
First, we check that we have a non-null `token`. Then we apply it to the request using the
`appendHeaders` utility as an `Authorization` header.
We could also be using `makeOperation` here to update the context in any other way, such as:
```js
import { makeOperation } from '@urql/core';
makeOperation(operation.kind, operation, {
...operation.context,
someAuthThing: token,
});
```
### Configuring `didAuthError`
This function lets the `authExchange` know what is defined to be an API error for your API.
`didAuthError` is called by `authExchange` when it receives an `error` on an `OperationResult`, which
is of type [`CombinedError`](../api/core.md#combinederror).
We can for example check the error's `graphQLErrors` array in `CombinedError` to determine if an auth
error has occurred. While your API may implement this differently, an authentication error on an
execution result may look a little like this if your API uses `extensions.code` on errors:
```js
{
data: null,
errors: [
{
message: 'Unauthorized: Token has expired',
extensions: {
code: 'FORBIDDEN'
},
}
]
}
```
If you're building a new API, using `extensions` on errors is the recommended approach to add
metadata to your errors. We'll be able to determine whether any of the GraphQL errors were due
to an unauthorized error code, which would indicate an auth failure:
```js
authExchange(async utils => {
// ...
return {
// ...
didAuthError(error, _operation) {
return error.graphQLErrors.some(e => e.extensions?.code === 'FORBIDDEN');
},
};
});
```
For some GraphQL APIs, the authentication error is only communicated via a 401 HTTP status as is
common in RESTful APIs, which is suboptimal, but which we can still write a check for.
```js
authExchange(async utils => {
// ...
return {
// ...
didAuthError(error, _operation) {
return error.response?.status === 401;
},
};
});
```
If `didAuthError` returns `true`, it will trigger the `authExchange` to trigger the logic for asking
for re-authentication via `refreshAuth`.
### Configuring `refreshAuth` (triggered after an auth error has occurred)
If the API doesn't support any sort of token refresh, this is where we could simply log the user out.
```js
authExchange(async utils => {
// ...
return {
// ...
async refreshAuth() {
logout();
},
};
});
```
Here, `logout()` is a placeholder that is called when we got an error, so that we can redirect to a
login page again and clear our tokens from local storage or otherwise.
If we had a way to refresh our token using a refresh token, we can attempt to get a new token for the
user first:
```js
authExchange(async utils => {
let token = localStorage.getItem('token');
let refreshToken = localStorage.getItem('refreshToken');
return {
// ...
async refreshAuth() {
const result = await utils.mutate(REFRESH, { refreshToken });
if (result.data?.refreshLogin) {
// Update our local variables and write to our storage
token = result.data.refreshLogin.token;
refreshToken = result.data.refreshLogin.refreshToken;
localStorage.setItem('token', token);
localStorage.setItem('refreshToken', refreshToken);
} else {
// This is where auth has gone wrong and we need to clean up and redirect to a login page
localStorage.clear();
logout();
}
},
};
});
```
Here we use the special `mutate` utility method provided by the `authExchange` to do the token
refresh. This is a useful method to use if your GraphQL API expects you to make a GraphQL mutation
to update your authentication state. It will send the mutation and bypass all authentication and
prior exchanges.
If your authentication is not handled via GraphQL but a REST endpoint, you can use the `fetch` API
here however instead of a mutation.
All other requests will be paused while `refreshAuth` runs, so we won't have to deal with multiple
authentication errors or refreshes at once.
### Configuring `willAuthError`
`willAuthError` is an optional parameter and is run _before_ a request is made.
We can use it to trigger an authentication error and let the `authExchange` run our `refreshAuth`
function without the need to first let a request fail with an authentication error. For example, we
can use this to predict an authentication error, for instance, because of expired JWT tokens.
```js
authExchange(async utils => {
// ...
return {
// ...
willAuthError(_operation) {
// Check whether `token` JWT is expired
return false;
},
};
});
```
This can be really useful when we know when our authentication state is invalid and want to prevent
even sending any operation that we know will fail with an authentication error.
However, we have to be careful on how we define this function, if some queries or login mutations
are sent to our API without being logged in. In these cases, it's better to either detect the
mutations we'd like to allow or return `false` when a token isn't set in storage yet.
If we'd like to detect a mutation that will never fail with an authentication error, we could for
instance write the following logic:
```js
authExchange(async utils => {
// ...
return {
// ...
willAuthError(operation) {
if (
operation.kind === 'mutation' &&
// Here we find any mutation definition with the "login" field
operation.query.definitions.some(definition => {
return (
definition.kind === 'OperationDefinition' &&
definition.selectionSet.selections.some(node => {
// The field name is just an example, since signup may also be an exception
return node.kind === 'Field' && node.name.value === 'login';
})
);
})
) {
return false;
} else if (false /* is JWT expired? */) {
return true;
} else {
return false;
}
},
};
});
```
Alternatively, you may decide to let all operations through if your token isn't set in storage, i.e.
if you have no prior authentication state.
## Handling Logout by reacting to Errors
We can also handle authentication errors in a `mapExchange` instead of the `authExchange`.
To do this, we'll need to add the `mapExchange` to the exchanges array, _before_ the `authExchange`.
The order is very important here:
```js
import { createClient, cacheExchange, fetchExchange, mapExchange } from 'urql';
import { authExchange } from '@urql/exchange-auth';
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
mapExchange({
onError(error, _operation) {
const isAuthError = error.graphQLErrors.some(e => e.extensions?.code === 'FORBIDDEN');
if (isAuthError) {
logout();
}
},
}),
authExchange(async utils => {
return {
/* config */
};
}),
fetchExchange,
],
});
```
The `mapExchange` will only receive an auth error when the auth exchange has already tried and failed
to handle it. This means we have either failed to refresh the token, or there is no token refresh
functionality. If we receive an auth error in the `mapExchange`'s `onError` function
(as defined in the `didAuthError` configuration section above), then we can be confident that it is
an authentication error that the `authExchange` isn't able to recover from, and the user should be
logged out.
## Cache Invalidation on Logout
If we're dealing with multiple authentication states at the same time, e.g. logouts, we need to
ensure that the `Client` is reinitialized whenever the authentication state changes.
Here's an example of how we may do this in React if necessary:
```jsx
import { createClient, Provider } from 'urql';
const App = ({ isLoggedIn }: { isLoggedIn: boolean | null }) => {
const client = useMemo(() => {
if (isLoggedIn === null) {
return null;
}
return createClient({ /* config */ });
}, [isLoggedIn]);
if (!client) {
return null;
}
return {
<Provider value={client}>
{/* app content */}
<Provider>
}
}
```
When the application launches, the first thing we do is check whether the user has any authentication
tokens in persisted storage. This will tell us whether to show the user the logged in or logged out view.
The `isLoggedIn` prop should always be updated based on authentication state change. For instance, we may set it to
`true` after the user has authenticated and their tokens have been added to storage, and set it to
`false` once the user has been logged out and their tokens have been cleared. It's important to clear
or add tokens to a storage _before_ updating the prop in order for the auth exchange to work
correctly.
This pattern of creating a new `Client` when changing authentication states is especially useful
since it will also recreate our client-side cache and invalidate all cached data.
================================================
FILE: docs/advanced/authoring-exchanges.md
================================================
---
title: Authoring Exchanges
order: 8
---
# Exchange Author Guide
As we've learned [on the "Architecture" page](../architecture.md) page, `urql`'s `Client` structures
its data as an event hub. We have an input stream of operations, which are instructions for the
`Client` to provide a result. These results then come from an output stream of operation results.
_Exchanges_ are responsible for performing the important transform from the operations (input) stream
to the results stream. Exchanges are handler functions that deal with these input and
output streams. They're one of `urql`'s key components, and are needed to implement vital pieces of
logic such as caching, fetching, deduplicating requests, and more. In other words, Exchanges are
handlers that fulfill our GraphQL requests and can change the stream of operations or results.
In this guide we'll learn more about how exchanges work and how we can write our own exchanges.
## An Exchange Signature
Exchanges are akin to [middleware in
Redux](https://redux.js.org/advanced/middleware) due to the way that they apply transforms.
```ts
import { Client, Operation, OperationResult } from '@urql/core';
type ExchangeInput = { forward: ExchangeIO; client: Client };
type Exchange = (input: ExchangeInput) => ExchangeIO;
type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>;
```
The first parameter to an exchange is a `forward` function that refers to the next Exchange in the
chain. The second second parameter is the `Client` being used. Exchanges always return an `ExchangeIO`
function (this applies to the `forward` function as well), which accepts the source of
[_Operations_](../api/core.md#operation) and returns a source of [_Operation
Results_](../api/core.md#operationresult).
- [Read more about streams on the "Architecture" page.](../architecture.md#stream-patterns-in-urql)
- [Read more about the _Exchange_ type signature on the API docs.](../api/core.md#exchange)
## Using Exchanges
The `Client` accepts an `exchanges` option that. Initially, we may choose to just
set this to two very standard exchanges — `cacheExchange` and `fetchExchange`.
In essence these exchanges build a pipeline that runs in the order they're passed; _Operations_ flow
in from the start to the end, and _Results_ are returned through the chain in reverse.
Suppose we pass the `cacheExchange` and then the `fetchExchange` to the `exchanges`.
**First,** operations are checked against the cache. Depending on the `requestPolicy`,
cached results can be resolved from here instead, which would mean that the cache sends back the
result, and the operation doesn't travel any further in the chain.
**Second,** operations are sent to the API, and the result is turned into an `OperationResult`.
**Lastly,** operation results then travel through the exchanges in _reverse order_, which is because
exchanges are a pipeline where all operations travel forward deeper into the exchange chain, and
then backwards. When these results pass through the cache then the `cacheExchange` stores the
result.
```js
import { Client, fetchExchange, cacheExchange } from 'urql';
const client = new Client({
url: 'http://localhost:3000/graphql',
exchanges: [cacheExchange, fetchExchange],
});
```
We can add more exchanges to this chain, for instance, we can add the `mapExchange`, which can call a
callback whenever it sees [a `CombinedError`](../basics/errors.md) occur on a result.
```js
import { Client, fetchExchange, cacheExchange, mapExchange } from 'urql';
const client = new Client({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
mapExchange({
onError(error) {
console.error(error);
},
}),
fetchExchange,
],
});
```
This is an example for adding a synchronous exchange to the chain that only reacts to results. It
doesn't add any special behavior for operations travelling through it. An example for an
asynchronous exchange that looks at both operations and results [we may look at the `retryExchange`
which retries failed operations.](../advanced/retry-operations.md)
## The Rules of Exchanges
Before we can start writing some exchanges, there are a couple of consistent patterns and limitations that
must be adhered to when writing an exchange. We call these the "rules of Exchanges", which
also come in useful when trying to learn what Exchanges actually are.
For reference, this is a basic template for an exchange:
```js
const noopExchange = ({ client, forward }) => {
return operations$ => {
// <-- The ExchangeIO function
const operationResult$ = forward(operations$);
return operationResult$;
};
};
```
This exchange does nothing else than forward all operations and return all results. Hence, it's
called a `noopExchange` — an exchange that doesn't do anything.
### Forward and Return Composition
When you create a `Client` and pass it an array of exchanges, `urql` composes them left-to-right.
If we look at our previous `noopExchange` example in context, we can track what it does if it is located between the `cacheExchange` and the `fetchExchange`.
```js
import { Client, cacheExchange, fetchExchange } from 'urql';
const noopExchange = ({ client, forward }) => {
return operations$ => {
// <-- The ExchangeIO function
// We receive a stream of Operations from `cacheExchange` which
// we can modify before...
const forwardOperations$ = operations$;
// ...calling `forward` with the modified stream. The `forward`
// function is the next exchange's `ExchangeIO` function, in this
// case `fetchExchange`.
const operationResult$ = forward(operations$);
// We get back `fetchExchange`'s stream of results, which we can
// also change before returning, which is what `cacheExchange`
// will receive when calling `forward`.
return operationResult$;
};
};
const client = new Client({
exchanges: [cacheExchange, noopExchange, fetchExchange],
});
```
### How to Avoid Accidentally Dropping Operations
Typically the `operations$` stream will send you `query`, `mutation`,
`subscription`, and `teardown`. There is no constraint for new operations
to be added later on or a custom exchange adding new operations altogether.
This means that you have to take "unknown" operations into account and
not `filter` operations too aggressively.
```js
import { pipe, filter, merge } from 'wonka';
// DON'T: drop unknown operations
({ forward }) =>
operations$ => {
// This doesn't handle operations that aren't queries
const queries = pipe(
operations$,
filter(op => op.kind === 'query')
);
return forward(queries);
};
// DO: forward operations that you don't handle
({ forward }) =>
operations$ => {
const queries = pipe(
operations$,
filter(op => op.kind === 'query')
);
const rest = pipe(
operations$,
filter(op => op.kind !== 'query')
);
return forward(merge([queries, rest]));
};
```
If operations are grouped and/or filtered by what the exchange is handling, then it's also important to
make that any streams of operations not handled by the exchange should also be forwarded.
### Synchronous first, Asynchronous last
By default exchanges and Wonka streams are as predictable as possible.
Every operator in Wonka runs synchronously until asynchronicity is introduced.
This may happen when using a timing utility from Wonka, like
[`delay`](https://wonka.kitten.sh/api/operators#delay) or
[`throttle`](https://wonka.kitten.sh/api/operators#throttle)
This can also happen because the exchange inherently does something asynchronous, like fetching some
data or using a promise.
When writing exchanges, some will inevitably be asynchronous. For example if
they're fetching results, performing authentication, or other tasks
that you have to wait for.
This can cause problems, because the behavior in `urql` is built
to be _synchronous_ first. This is very helpful for suspense mode and allowing components receive cached data on their initial
mount without rerendering.
This why **all exchanges should be ordered synchronous first and
asynchronous last**.
What we for instance repeat as the default setup in our docs is this:
```js
import { Client, cacheExchange, fetchExchange } from 'urql';
new Client({
// ...
exchanges: [cacheExchange, fetchExchange];
});
```
The `cacheExchange` is completely synchronous.
The `fetchExchange` is asynchronous since it makes a `fetch` request and waits for a server response.
If we put an asynchronous exchange in front of the `cacheExchange`, that would be unexpected, and
since all results would then be delayed, nothing would ever be "cached" and instead always take some
amount of time to be returned.
When you're adding more exchanges, it's often crucial
to put them in a specific order. For instance, an authentication exchange
will need to go before the `fetchExchange`, a secondary cache will probably have to
go in front of the default cache exchange.
To ensure the correct behavior of suspense mode and
the initialization of our hooks, it's vital to order exchanges
so that synchronous ones come before asynchronous ones.
================================================
FILE: docs/advanced/auto-populate-mutations.md
================================================
---
title: Auto-populate Mutations
order: 9
---
# Automatically populating Mutations
The `populateExchange` allows you to auto-populate selection sets in your mutations using the
`@populate` directive. In combination with [Graphcache](../graphcache/README.md) this is a useful
tool to update the data in your application automatically following a mutation, when your app grows,
and it becomes harder to track all fields that have been queried before.
> **NOTE:** The `populateExchange` is _experimental_! Certain patterns and usage paths
> like GraphQL field arguments aren't covered yet, and the exchange hasn't been extensively used
> yet.
## Installation and Setup
The `populateExchange` can be installed via the `@urql/exchange-populate` package.
```sh
yarn add @urql/exchange-populate
# or
npm install --save @urql/exchange-populate
```
Afterwards we can set the `populateExchange` up by adding it to our list of `exchanges` in the
client options.
```ts
import { Client, fetchExchange } from '@urql/core';
import { populateExchange } from '@urql/exchange-populate';
const client = new Client({
// ...
exchanges: [populateExchange({ schema }), cacheExchange, fetchExchange],
});
```
The `populateExchange` should be placed in front of the `cacheExchange`, especially if you're using
[Graphcache](../graphcache/README.md), since it won't understand the `@populate` directive on its
own. It should also be placed in front the `cacheExchange` to avoid unnecessary work.
Adding the `populateExchange` now enables us to use the `@populate` directive in our mutations.
The `schema` option is the introspection result for your backend graphql schema, more information
about how to get your schema can be found [in the "Schema Awareness" Page of the Graphcache documentation.](../graphcache/schema-awareness.md#getting-your-schema).
## Example usage
Consider the following queries, which have been requested in other parts of your application:
```graphql
# Query 1
{
todos {
id
name
}
}
# Query 2
{
todos {
id
createdAt
}
}
```
Without the `populateExchange` you may write a mutation like the following which returns a newly created todo item:
```graphql
# Without populate
mutation addTodo(id: ID!) {
addTodo(id: $id) {
id # To update Query 1 & 2
name # To update Query 1
createdAt # To update Query 2
}
}
```
By using `populateExchange`, you no longer need to manually specify the selection set required to update your other queries. Instead you can just add the `@populate` directive.
```graphql
# With populate
mutation addTodo(id: ID!) {
addTodo(id: $id) @populate
}
```
### Choosing when to populate
You may not want to populate your whole mutation response. To reduce your payload, pass populate lower in your query.
```graphql
mutation addTodo(id: ID!) {
addTodo(id: $id) {
id
user @populate
}
}
```
### Using aliases
If you find yourself using multiple queries with variables, it may be necessary to
[use aliases](https://graphql.org/learn/queries/#aliases) to allow merging of queries.
> **Note:** This caveat may change in the future or this restriction may be lifted.
**Invalid usage**
```graphql
# Query 1
{
todos(first: 10) {
id
name
}
}
# Query 2
{
todos(last: 20) {
id
createdAt
}
}
```
**Usage with aliases**
```graphql
# Query 1
{
firstTodos: todos(first: 10) {
id
name
}
}
# Query 2
{
lastTodos: todos(last: 20) {
id
createdAt
}
}
```
================================================
FILE: docs/advanced/debugging.md
================================================
---
title: Debugging
order: 4
---
# Debugging
We've tried to make debugging in `urql` as seamless as possible by creating tools for users of `urql`
and those creating their own exchanges.
## Devtools
It's easiest to debug `urql` with the [`urql` devtools.](https://github.com/urql-graphql/urql-devtools/)
It offers tools to inspect internal ["Debug Events"](#debug-events) as they happen, to explore data
as your app is seeing it, and to quickly trigger GraphQL queries.
[For instructions on how to set up the devtools, check out `@urql/devtools`'s readme in its
repository.](https://github.com/urql-graphql/urql-devtools)

## Debug events
The "Debug Events" are internally what displays more information to the user on the devtools'
"Events" tab than just [Operations](../api/core.md#operation) and [Operation
Results](../api/core.md#operationresult).
Events may be fired inside exchanges to add additional development logging to an exchange.
The `fetchExchange` for instance will fire a `fetchRequest` event when a request is initiated and
either a `fetchError` or `fetchSuccess` event when a result comes back from the GraphQL API.
The [Devtools](#browser-devtools) aren't the only way to observe these internal events.
Anyone can start listening to these events for debugging events by calling the
[`Client`'s](../api/core.md#client) `client.subscribeToDebugTarget()` method.
Unlike `Operation`s these events are fire-and-forget events that are only used for debugging. Hence,
they shouldn't be used for anything but logging and not for messaging. **Debug events are also
entirely disabled in production.**
### Subscribing to Debug Events
Internally the `devtoolsExchange` calls the `client.subscribeToDebugTarget`, but if we're looking to
build custom debugging tools, it's also possible to call this function directly and to replace the
`devtoolsExchange`.
```
const { unsubscribe } = client.subscribeToDebugTarget(event => {
if (event.source === 'cacheExchange')
return;
console.log(event); // { type, message, operation, data, source, timestamp }
});
```
As demonstrated above, the `client.subscribeToDebugTarget` accepts a callback function and returns
a subscription with an `unsubscribe` method. We've seen this pattern in the prior ["Stream Patterns"
section on the "Architecture" page.](../architecture.md)
## Adding your own Debug Events
Debug events are a means of sharing implementation details to consumers of an exchange. If you're
creating an exchange and want to share relevant information with the `devtools`, then you may want
to start adding your own events.
#### Dispatching an event
[On the "Authoring Exchanges" page](./authoring-exchanges.md) we've learned about the [`ExchangeInput`
object](../api/core.md#exchangeinput), which comes with a `client` and a `forward` property.
It also contains a `dispatchDebug` property.
It is called with an object containing the following properties:
| Prop | Type | Description |
| ----------- | ----------- | ------------------------------------------------------------------------------------- |
| `type` | `string` | A unique type identifier for the Debug Event. |
| `message` | `string` | A human readable description of the event. |
| `operation` | `Operation` | The [`Operation`](../api/core.md#operation) that the event targets. |
| `data` | `?object` | This is an optional payload to include any data that may become useful for debugging. |
For instance, we may call `dispatchDebug` with our `fetchRequest` event. This is the event that the
`fetchExchange` uses to notify us that a request has commenced:
```ts
export const fetchExchange: Exchange = ({ forward, dispatchDebug }) => {
// ...
return ops$ => {
return pipe(
ops$,
// ...
mergeMap(operation => {
dispatchDebug({
type: 'fetchRequest',
message: 'A network request has been triggered',
operation,
data: {
/* ... */
},
});
// ...
})
);
};
};
```
If we're adding new events that aren't included in the main `urql` repository and are using
TypeScript, we may also declare a fixed type for the `data` property, so we can guarantee a
consistent payload for our Debug Events. This also prevents accidental conflicts.
```ts
// urql.d.ts
import '@urql/core';
declare module '@urql/core' {
interface DebugEventTypes {
customEventType: { somePayload: string };
}
}
```
Read more about extending types, like `urql`'s `DebugEventTypes` on the [TypeScript docs on
declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html).
### Tips
Lastly, in summary, here are a few tips, that are important when we're adding new Debug Events to
custom exchanges:
- ✅ **Share internal details**: Frequent debug messages on key events inside your exchange are very
useful when later inspecting them, e.g. in the `devtools`.
- ✅ **Create unique event types** : Key events should be easily identifiable and have a unique
names.
- ❌ **Don't listen to debug events inside your exchange**: While it's possible to call
`client.subscribeToDebugTarget` in an exchange it's only valuable when creating a debugging
exchange, like the `devtoolsExchange`.
- ❌ **Don't send warnings in debug events**: Informing your user about warnings isn't effective
when the event isn't seen. You should still rely on `console.warn` so all users see your important
warnings.
================================================
FILE: docs/advanced/persistence-and-uploads.md
================================================
---
title: Persistence & Uploads
order: 1
---
# Persisted Queries and Uploads
`urql` supports (Automatic) Persisted Queries, and File Uploads via GraphQL
Multipart requests. For persisted queries to work, some setup work is needed,
while File Upload support is built into `@urql/core@4`.
## Automatic Persisted Queries
Persisted Queries allow us to send requests to the GraphQL API that can easily be cached on the fly,
both by the GraphQL API itself and potential CDN caching layers. This is based on the unofficial
[GraphQL Persisted Queries
Spec](https://github.com/apollographql/apollo-link-persisted-queries#apollo-engine).
With Automatic Persisted Queries the client hashes the GraphQL query and turns it into an SHA256
hash and sends this hash instead of the full query. If the server has seen this GraphQL query before
it will recognise it by its hash and process the GraphQL API request as usual, otherwise it may
respond using a `PersistedQueryNotFound` error. In that case the client is supposed to instead send
the full GraphQL query, and the hash together, which will cause the query to be "registered" with the
server.
Additionally, we could also decide to send these hashed queries as GET requests instead of POST
requests. If we only send the persisted queries with hashes as GET requests then they become a lot
easier for a CDN to cache, as by default most caches would not cache POST requests automatically.
In `urql`, we may use the `@urql/exchange-persisted` package's `persistedExchange` to
enable support for Automatic Persisted Queries. This exchange works alongside other fetch or
subscription exchanges by adding metadata for persisted queries to each GraphQL
request by modifying the `extensions` object of operations.
> **Note:** [You can find a code example for `@urql/exchange-persisted` in an example in the `urql` repository.](https://github.com/urql-graphql/urql/tree/main/examples/with-apq)
### Installation & Setup
First install `@urql/exchange-persisted` alongside `urql`:
```sh
yarn add @urql/exchange-persisted
# or
npm install --save @urql/exchange-persisted
```
You'll then need to add the `persistedExchange` function, that this package exposes,
to your `exchanges`, in front of exchanges that communicate with the API:
```js
import { Client, fetchExchange, cacheExchange } from 'urql';
import { persistedExchange } from '@urql/exchange-persisted';
const client = new Client({
url: 'http://localhost:1234/graphql',
exchanges: [
cacheExchange,
persistedExchange({
preferGetForPersistedQueries: true,
}),
fetchExchange,
],
});
```
As we can see, typically it's recommended to set `preferGetForPersistedQueries` to `true`
to encourage persisted queries to use GET requests instead of POST so that CDNs can do their job.
When set to `true` or `'within-url-limit'`, persisted queries will use GET requests if the
resulting URL doesn't exceed the 2048 character limit.
The `fetchExchange` can see the modifications that the `persistedExchange` is
making to operations, and understands to leave out the `query` from any request
as needed. The same should be happening to the `subscriptionExchange`, if you're
using it for queries.
### Customizing Hashing
The `persistedExchange` also accepts a `generateHash` option. This may be used to swap out the
exchange's default method of generating SHA256 hashes. By default, the exchange will use the
built-in [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) when it's
available, and in Node.js it'll use the [Node Crypto Module](https://nodejs.org/api/crypto.html)
instead.
If you're using [the `graphql-persisted-document-loader` for
Webpack](https://github.com/leoasis/graphql-persisted-document-loader), for instance, then you will
already have a loader generating SHA256 hashes for you at compile time. In that case we could swap
out the `generateHash` function with a much simpler one that uses the `generateHash` function's
second argument, a GraphQL `DocumentNode` object.
```js
persistedExchange({
generateHash: (_, document) => document.documentId,
});
```
If you're using **React Native** then you may not have access to the Web Crypto API, which means
that you have to provide your own SHA256 function to the `persistedExchange`. Luckily, we can do
so easily by using the first argument `generateHash` receives, a GraphQL query as a string.
```js
import sha256 from 'hash.js/lib/hash/sha/256';
persistedExchange({
async generateHash(query) {
return sha256().update(query).digest('hex');
},
});
```
Additionally, if the API only expects persisted queries and not arbitrary ones and all queries are
pre-registered against the API then the `persistedExchange` may be put into a **non-automatic**
persisted queries mode by giving it the `enforcePersistedQueries: true` option. This disables any
retry logic and assumes that persisted queries will be handled like regular GraphQL requests.
## File Uploads
GraphQL server APIs commonly support the [GraphQL Multipart Request
spec](https://github.com/jaydenseric/graphql-multipart-request-spec) to allow for File Uploads
directly with a GraphQL API.
If a GraphQL API supports this, we can pass a [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File)
or a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) directly into our variables and
define the corresponding scalar for our variable, which is often called `File` or `Upload`.
In a browser, the `File` object may often be retrieved via a
[file input](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications),
for example.
> **Note:** If you are using your own version of `File` and `Blob` ensure you are properly extending the
> so it can be properly identified as a file.
The `@urql/core@4` package supports File Uploads natively, so we won't have to do any installation
or setup work. When `urql` sees a `File` or a `Blob` anywhere in your `variables`, it switches to
a `multipart/form-data` request, converts the request to a `FormData` object, according to the
GraphQL Multipart Request specification, and sends it off to the API.
> **Note:** Previously, this worked by installing the `@urql/multipart-fetch-exchange` package.
> however, this package has been deprecated and file uploads are now built into `@urql/core@4`.
[You can find a code example for file uploads in an example in the `urql` repository.](https://github.com/urql-graphql/urql/tree/main/examples/with-multipart)
================================================
FILE: docs/advanced/retry-operations.md
================================================
---
title: Retrying Operations
order: 5
---
# Retrying Operations
The `retryExchange` lets us retry specific operation, by default it will
retry only network errors, but we can specify additional options to add
functionality.
> **Note:** [You can find a code example for `@urql/exchange-retry` in an example in the `urql` repository.](https://github.com/urql-graphql/urql/tree/main/examples/with-retry)
## Installation and Setup
First install `@urql/exchange-retry` alongside `urql`:
```sh
yarn add @urql/exchange-retry
# or
npm install --save @urql/exchange-retry
```
You'll then need to add the `retryExchange`, exposed by this package, to your `urql` Client:
```js
import { Client, cacheExchange, fetchExchange } from 'urql';
import { retryExchange } from '@urql/exchange-retry';
// None of these options have to be added, these are the default values.
const options = {
initialDelayMs: 1000,
maxDelayMs: 15000,
randomDelay: true,
maxNumberAttempts: 2,
retryIf: err => err && err.networkError,
};
// Note the position of the retryExchange - it should be placed prior to the
// fetchExchange and after the cacheExchange for it to function correctly
const client = new Client({
url: 'http://localhost:1234/graphql',
exchanges: [
cacheExchange,
retryExchange(options), // Use the retryExchange factory to add a new exchange
fetchExchange,
],
});
```
We want to place the `retryExchange` before the `fetchExchange` so that retries are only performed _after_ the operation has passed through the cache and has attempted to fetch.
## The Options
There are a set of optional options that allow for fine-grained control over the `retry` mechanism.
We have the `initialDelayMs` to specify at what interval the `retrying` should start, this means that if we specify `1000` that when our `operation` fails we'll wait 1 second and then retry it.
Next up is the `maxDelayMs`, our `retryExchange` will keep increasing the time between retries, so we don't spam our server with requests it can't complete, this option ensures we don't exceed a certain threshold. This time between requests will increase with a random `back-off` factor multiplied by the `initialDelayMs`, read more about the [thundering herd problem](https://en.wikipedia.org/wiki/Thundering_herd_problem).
Talking about increasing the `delay` randomly, `randomDelay` allows us to disable this. When this option is set to `false` we'll only increase the time between attempts with the `initialDelayMs`. This means if we fail the first time we'll have 1 second wait, next fail we'll have 2 seconds and so on.
We can declare the maximum number of attempts (including the initial request) with `maxNumberAttempts`, otherwise, it defaults to 2 (which means one retry). If you want it to retry indefinitely, you can simply pass in `Number.POSITIVE_INFINITY`.
[For more information on the available options check out the API Docs.](../api/retry-exchange.md)
## Reacting to Different Errors
We can introduce specific triggers for the `retryExchange` to start retrying operations,
let's look at an example:
```js
import { Client, cacheExchange, fetchExchange } from 'urql';
import { retryExchange } from '@urql/exchange-retry';
const client = new Client({
url: 'http://localhost:1234/graphql',
exchanges: [
cacheExchange,
retryExchange({
retryIf: error => {
return !!(error.graphQLErrors.length > 0 || error.networkError);
},
}),
fetchExchange,
],
});
```
In the above example we'll retry when we have `graphQLErrors` or a `networkError`, we can go
more granular and check for certain errors in `graphQLErrors`.
## Failover / Fallback
In case of a network error, e.g., when part the infrastructure is down, but a fallback GraphQL endpoint is available, e.g., from a different provider on a different domain, the `retryWith` option allows for client-side failover. This could also be used in case of a `graphQLError`, for example, when APIs are deployed via a windowing strategy, i.e., a newer version at URL X, while an older one remains at Y.
Note that finer granularity depending on custom requirements may be applicable, and that this does not allow for balancing load.
```js
const fallbackUrl = 'http://localhost:1337/anotherGraphql';
const options = {
initialDelayMs: 1000,
maxDelayMs: 15000,
randomDelay: true,
maxNumberAttempts: 2,
retryWith: (error, operation) => {
if (error.networkError) {
const context = { ...operation.context, url: fallbackUrl };
return { ...operation, context };
}
return null;
},
};
```
================================================
FILE: docs/advanced/server-side-rendering.md
================================================
---
title: Server-side Rendering
order: 3
---
# Server-side Rendering
In server-side rendered applications we often need to set our application up so that data will be
fetched on the server-side and later sent down to the client for hydration. `urql` supports this
through the `ssrExchange.`
## The SSR Exchange
The `ssrExchange` has two functions. On the server-side it's able to gather all results as they're
being fetched, which can then be serialized and sent to the client. On the client-side it's able to
use these serialized results to rehydrate and render the application without refetching this data.
To start out with the `ssrExchange` we have to add the exchange to our `Client`:
```js
import { Client, cacheExchange, fetchExchange, ssrExchange } from '@urql/core';
const isServerSide = typeof window === 'undefined';
// The `ssrExchange` must be initialized with `isClient` and `initialState`
const ssr = ssrExchange({
isClient: !isServerSide,
initialState: !isServerSide ? window.__URQL_DATA__ : undefined,
});
const client = new Client({
exchanges: [
cacheExchange,
ssr, // Add `ssr` in front of the `fetchExchange`
fetchExchange,
],
});
```
The `ssrExchange` must be initialized with the `isClient` and `initialState` options. The `isClient`
option tells the exchange whether it's on the server- or client-side. In our example we use `typeof window` to determine this, but in Webpack environments you may also be able to use `process.browser`.
Optionally, we may also choose to enable `staleWhileRevalidate`. When enabled this flag will ensure that although a result may have been rehydrated from our SSR result, another
refetch `network-only` operation will be issued, to update stale data. This is useful for statically generated sites (SSG) that may ship stale data to our application initially.
The `initialState` option should be set to the serialized data you retrieve on your server-side.
This data may be retrieved using methods on `ssrExchange()`. You can retrieve the serialized data
after server-side rendering using `ssr.extractData()`:
```js
// Extract and serialise the data like so from the `ssr` instance
// we've previously created by calling `ssrExchange()`
const data = JSON.stringify(ssr.extractData());
const markup = ''; // The render code for our framework goes here
const html = `
<html>
<body>
<div id="root">${markup}</div>
<script>
window.__URQL_DATA__ = JSON.parse(${data});
</script>
</body>
</html>
`;
```
This will provide `__URQL_DATA__` globally, which we've used in our first example to inject data into
the `ssrExchange` on the client-side.
Alternatively you can also call `restoreData` as long as this call happens synchronously before the
`client` starts receiving queries.
```js
const isServerSide = typeof window === 'undefined';
const ssr = ssrExchange({ isClient: !isServerSide });
if (!isServerSide) {
ssr.restoreData(window.__URQL_DATA__);
}
```
## Using `react-ssr-prepass`
In the previous examples we've set up the `ssrExchange`, however with React this still requires us
to manually execute our queries before rendering a server-side React app [using `renderToString`
or `renderToNodeStream`](https://reactjs.org/docs/react-dom-server.html#rendertostring).
For React, `urql` has a "Suspense mode" that [allows data fetching to interrupt
rendering](https://reactjs.org/docs/concurrent-mode-suspense.html). However, Suspense is
not supported by React during server-side rendering.
Using [the `react-ssr-prepass` package](https://github.com/FormidableLabs/react-ssr-prepass) however,
we can implement a prerendering step before we let React server-side render, which allows us to
automatically fetch all data that the app requires with Suspense. This technique is commonly
referred to as a "two-pass approach", since our React element is traversed twice.
To set this up, first we'll install `react-ssr-prepass`. It has a peer dependency on `react-is`
and `react`.
```sh
yarn add react-ssr-prepass react-is react-dom
# or
npm install --save react-ssr-prepass react-is react-dom
```
Next, we'll modify our server-side code and add `react-ssr-prepass` in front of `renderToString`.
```jsx
import { renderToString } from 'react-dom/server';
import prepass from 'react-ssr-prepass';
import {
Client,
cacheExchange,
fetchExchange,
ssrExchange,
Provider,
} from 'urql';
const handleRequest = async (req, res) => {
// ...
const ssr = ssrExchange({ isClient: false });
const client = new Client({
url: 'https://??',
suspense: true, // This activates urql's Suspense mode on the server-side
exchanges: [cacheExchange, ssr, fetchExchange]
});
const element = (
<Provider value={client}>
<App />
</Provider>
);
// Using `react-ssr-prepass` this prefetches all data
await prepass(element);
// This is the usual React SSR rendering code
const markup = renderToString(element);
// Extract the data after prepass and rendering
const data = JSON.stringify(ssr.extractData());
res.status(200).send(`
<html>
<body>
<div id="root">${markup}</div>
<script>
window.__URQL_DATA__ = JSON.parse(${data});
</script>
</body>
</html>
`);
};
```
It's important to set enable the `suspense` option on the `Client`, which switches it to support
React suspense.
### With Preact
If you're using Preact instead of React, there's a drop-in replacement package for
`react-ssr-prepass`, which is called `preact-ssr-prepass`. It only has a peer dependency on Preact,
and we can install it like so:
```sh
yarn add preact-ssr-prepass preact
# or
npm install --save preact-ssr-prepass preact
```
All above examples for `react-ssr-prepass` will still be the same, except that instead of
using the `urql` package we'll have to import from `@urql/preact`, and instead of `react-ssr-prepass`
we'll have to import from. `preact-ssr-prepass`.
## Next.js
If you're using [Next.js](https://nextjs.org/) you can save yourself a lot of work by using
`@urql/next`. The `@urql/next` package is set to work with Next 13.
To set up `@urql/next`, first we'll install `@urql/next` and `urql` as
peer dependencies:
```sh
yarn add @urql/next urql graphql
# or
npm install --save @urql/next urql graphql
```
We now have two ways to leverage `@urql/next`, one being part of a Server component
or being part of the general `app/` folder.
In a server component we will import from `@urql/next/rsc`
```ts
// app/page.tsx
import React from 'react';
import { cacheExchange, createClient, fetchExchange, gql } from '@urql/core';
import { registerUrql } from '@urql/next/rsc';
const makeClient = () => {
return createClient({
url: 'https://trygql.formidable.dev/graphql/basic-pokedex',
exchanges: [cacheExchange, fetchExchange],
});
};
const { getClient } = registerUrql(makeClient);
export default async function Home() {
const result = await getClient().query(PokemonsQuery, {});
return (
<main>
<h1>This is rendered as part of an RSC</h1>
<ul>
{result.data.pokemons.map((x: any) => (
<li key={x.id}>{x.name}</li>
))}
</ul>
</main>
);
}
```
When we aren't leveraging server components we will import the things we will
need to do a bit more setup, we go to the `client` component's layout file and
structure it as the following.
```tsx
// app/client/layout.tsx
'use client';
import { useMemo } from 'react';
import { UrqlProvider, ssrExchange, cacheExchange, fetchExchange, createClient } from '@urql/next';
export default function Layout({ children }: React.PropsWithChildren) {
const [client, ssr] = useMemo(() => {
const ssr = ssrExchange({
isClient: typeof window !== 'undefined',
});
const client = createClient({
url: 'https://trygql.formidable.dev/graphql/web-collections',
exchanges: [cacheExchange, ssr, fetchExchange],
suspense: true,
});
return [client, ssr];
}, []);
return (
<UrqlProvider client={client} ssr={ssr}>
{children}
</UrqlProvider>
);
}
```
It is important that we pass both a client as well as the `ssrExchange` to the `Provider`
this way we will be able to restore the data that Next streams to the client later on
when we are hydrating.
The next step is to query data in your client components by means of the `useQuery`
method defined in `@urql/next`.
```tsx
// app/client/page.tsx
'use client';
import Link from 'next/link';
import { Suspense } from 'react';
import { useQuery, gql } from '@urql/next';
export default function Page() {
return (
<Suspense>
<Pokemons />
</Suspense>
);
}
const PokemonsQuery = gql`
query {
pokemons(limit: 10) {
id
name
}
}
`;
function Pokemons() {
const [result] = useQuery({ query: PokemonsQuery });
return (
<main>
<h1>This is rendered as part of SSR</h1>
<ul>
{result.data.pokemons.map((x: any) => (
<li key={x.id}>{x.name}</li>
))}
</ul>
</main>
);
}
```
The data queried in the above component will be rendered on the server
and re-hydrated back on the client. When using multiple Suspense boundaries
these will also get flushed as they complete and re-hydrated.
> When data is used throughout the application we advise against
> rendering this as part of a server-component so you can benefit
> from the client-side cache.
### Invalidating data from a server-component
When data is rendered by a server component but you dispatch a mutation
from a client component the server won't automatically know that the
server-component on the client needs refreshing. You can forcefully
tell the server to do so by using the Next router and calling `.refresh()`.
```tsx
import { useRouter } from 'next/navigation';
const Todo = () => {
const router = useRouter();
const executeMutation = async () => {
await updateTodo();
router.refresh();
};
};
```
### Disabling RSC fetch caching
You can pass `fetchOptions: { cache: "no-store" }` to the `createClient`
constructor to avoid running into cached fetches with server-components.
## Legacy Next.js (pages)
If you're using [Next.js](https://nextjs.org/) with the classic `pages` you can instead use `next-urql`.
To set up `next-urql`, first we'll install `next-urql` with `react-is` and `urql` as peer dependencies:
```sh
yarn add next-urql react-is urql graphql
# or
npm install --save next-urql react-is urql graphql
```
The peer dependency on `react-is` is inherited from `react-ssr-prepass` requiring it.
Note that if you are using Next before v9.4 you'll need to polyfill fetch, this can be
done through [`isomorphic-unfetch`](https://www.npmjs.com/package/isomorphic-unfetch).
We're now able to wrap any page or `_app.js` using the `withUrqlClient` higher-order component. If
we wrap `_app.js` we won't have to wrap any individual page.
```js
// pages/index.js
import React from 'react';
import { useQuery } from 'urql';
import { withUrqlClient } from 'next-urql';
const Index = () => {
const [result] = useQuery({
query: '{ test }',
});
// ...
};
export default withUrqlClient((_ssrExchange, ctx) => ({
// ...add your Client options here
url: 'http://localhost:3000/graphql',
}))(Index);
```
The `withUrqlClient` higher-order component function accepts the usual `Client` options as
an argument. This may either just be an object, or a function that receives the Next.js'
`getInitialProps` context.
One added caveat is that these options may not include the `exchanges` option because `next-urql`
injects the `ssrExchange` automatically at the right location. If you're setting up custom exchanges
you'll need to instead provide them in the `exchanges` property of the returned client object.
```js
import { cacheExchange, fetchExchange } from '@urql/core';
import { withUrqlClient } from 'next-urql';
export default withUrqlClient(ssrExchange => ({
url: 'http://localhost:3000/graphql',
exchanges: [cacheExchange, ssrExchange, fetchExchange],
}))(Index);
```
Unless the component that is being wrapped already has a `getInitialProps` method, `next-urql` won't add its own SSR logic, which automatically fetches queries during
server-side rendering. This can be explicitly enabled by passing the `{ ssr: true }` option as a second argument to `withUrqlClient`.
When you are using `getStaticProps`, `getServerSideProps`, or `getStaticPaths`, you should opt-out of `Suspense` by setting the `neverSuspend` option to `true` in your `withUrqlClient` configuration.
During the prepass of your component tree `next-urql` can't know how these functions will alter the props passed to your page component. This injection
could change the `variables` used in your `useQuery`. This will lead to error being thrown during the subsequent `toString` pass, which isn't supported in React 16.
### SSR with { ssr: true }
The `withUrqlClient` only wraps our component tree with the context provider by default.
To enable SSR, the easiest way is specifying the `{ ssr: true }` option as a second
argument to `withUrqlClient`:
```js
import { cacheExchange, fetchExchange } from '@urql/core';
import { withUrqlClient } from 'next-urql';
export default withUrqlClient(
ssrExchange => ({
url: 'http://localhost:3000/graphql',
exchanges: [cacheExchange, ssrExchange, fetchExchange],
}),
{ ssr: true } // Enables server-side rendering using `getInitialProps`
)(Index);
```
Be aware that wrapping the `_app` component using `withUrqlClient` with the `{ ssr: true }`
option disables Next's ["Automatic Static
Optimization"](https://nextjs.org/docs/advanced-features/automatic-static-optimization) for
**all our pages**. It is thus preferred to enable server-side rendering on a per-page basis.
### SSR with getStaticProps or getServerSideProps
Enabling server-side rendering using `getStaticProps` and `getServerSideProps` is a little
more involved, but has two major benefits:
1. allows **direct schema execution** for performance optimisation
2. allows performing extra operations in those functions
To make the functions work with the `withUrqlClient` wrapper, return the `urqlState` prop
with the extracted data from the `ssrExchange`:
```js
import { withUrqlClient, initUrqlClient } from 'next-urql';
import { ssrExchange, cacheExchange, fetchExchange, useQuery } from 'urql';
const TODOS_QUERY = `
query { todos { id text } }
`;
function Todos() {
const [res] = useQuery({ query: TODOS_QUERY });
return (
<div>
{res.data.todos.map(todo => (
<div key={todo.id}>
{todo.id} - {todo.text}
</div>
))}
</div>
);
}
export async function getStaticProps(ctx) {
const ssrCache = ssrExchange({ isClient: false });
const client = initUrqlClient(
{
url: 'your-url',
exchanges: [cacheExchange, ssrCache, fetchExchange],
},
false
);
// This query is used to populate the cache for the query
// used on this page.
await client.query(TODOS_QUERY).toPromise();
return {
props: {
// urqlState is a keyword here so withUrqlClient can pick it up.
urqlState: ssrCache.extractData(),
},
revalidate: 600,
};
}
export default withUrqlClient(
ssr => ({
url: 'your-url',
})
// Cannot specify { ssr: true } here so we don't wrap our component in getInitialProps
)(Todos);
```
The above example will make sure the page is rendered as a static-page, It's important that
you fully pre-populate your cache so in our case we were only interested in getting our todos,
if there are child components relying on data you'll have to make sure these are fetched as well.
The `getServerSideProps` and `getStaticProps` functions only run on the **server-side** — any
code used in them is automatically stripped away from the client-side bundle using the
[next-code-elimination tool](https://next-code-elimination.vercel.app/). This allows **executing
our schema directly** using `@urql/exchange-execute` if we have access to our GraphQL server:
```js
import { withUrqlClient, initUrqlClient } from 'next-urql';
import { ssrExchange, cacheExchange, fetchExchange, useQuery } from 'urql';
import { executeExchange } from '@urql/exchange-execute';
import { schema } from '@/server/graphql'; // our GraphQL server's executable schema
const TODOS_QUERY = `
query { todos { id text } }
`;
function Todos() {
const [res] = useQuery({ query: TODOS_QUERY });
return (
<div>
{res.data.todos.map(todo => (
<div key={todo.id}>
{todo.id} - {todo.text}
</div>
))}
</div>
);
}
export async function getServerSideProps(ctx) {
const ssrCache = ssrExchange({ isClient: false });
const client = initUrqlClient(
{
url: '', // not needed without `fetchExchange`
exchanges: [
cacheExchange,
ssrCache,
executeExchange({ schema }), // replaces `fetchExchange`
],
},
false
);
await client.query(TODOS_QUERY).toPromise();
return {
props: {
urqlState: ssrCache.extractData(),
},
};
}
export default withUrqlClient(ssr => ({
url: 'your-url',
}))(Todos);
```
Direct schema execution skips one network round trip by accessing your resolvers directly
instead of performing a `fetch` API call.
### Stale While Revalidate
If we choose to use Next's static site generation (SSG or ISG) we may be embedding data in our initial payload that's stale on the client. In this case, we may want to update this data immediately after rehydration.
We can pass `staleWhileRevalidate: true` to `withUrqlClient`'s second option argument to Switch it to a mode where it'll refresh its rehydrated data immediately by issuing another network request.
```js
export default withUrqlClient(
ssr => ({
url: 'your-url',
}),
{ staleWhileRevalidate: true }
)(...);
```
Now, although on rehydration we'll receive the stale data from our `ssrExchange` first, it'll also immediately issue another `network-only` operation to update the data.
During this revalidation our stale results will be marked using `result.stale`. While this is similar to what we see with `cache-and-network` without server-side rendering, it isn't quite the same. Changing the request policy wouldn't actually refetch our data on rehydration as the `ssrExchange` is simply a replacement of a full network request. Hence, this flag allows us to treat this case separately.
### Resetting the client instance
In rare scenario's you possibly will have to reset the client instance (reset all cache, ...), this
is an uncommon scenario, and we consider it "unsafe" so evaluate this carefully for yourself.
When this does seem like the appropriate solution any component wrapped with `withUrqlClient` will receive the `resetUrqlClient`
property, when invoked this will create a new top-level client and reset all prior operations.
## Vue Suspense
In Vue 3 a [new feature was introduced](https://vuedose.tips/go-async-in-vue-3-with-suspense/) that
natively allows components to suspend while data is loading, which works universally on the server
and on the client, where a replacement loading template is rendered on a parent while data is
loading.
We've previously seen how we can change our usage of `useQuery`'s `PromiseLike` result to [make use
of Vue Suspense on the "Queries" page.](../basics/vue.md#vue-suspense)
Any component's `setup()` function can be updated to instead be an `async setup()` function, in
other words, to return a `Promise` instead of directly returning its data. This means that we can
update any `setup()` function to make use of Suspense.
On the server-side we can then use `@vue/server-renderer`'s `renderToString`, which will return a
`Promise` that resolves when all suspense-related loading is completed.
```jsx
import { createSSRApp } = from 'vue'
import { renderToString } from '@vue/server-renderer';
import urql, {
createClient,
cacheExchange,
fetchExchange,
ssrExchange
} from '@urql/vue';
const handleRequest = async (req, res) => {
// This is where we'll put our root component
const app = createSSRApp(Root)
// NOTE: All we care about here is that the SSR Exchange is included
const ssr = ssrExchange({ isClient: false });
app.use(urql, {
exchanges: [cacheExchange, ssr, fetchExchange]
});
const markup = await renderToString(app);
const data = JSON.stringify(ssr.extractData());
res.status(200).send(`
<html>
<body>
<div id="root">${markup}</div>
<script>
window.__URQL_DATA__ = JSON.parse(${data});
</script>
</body>
</html>
`);
};
```
This effectively renders our Vue app on the server-side and provides the client-side data for
rehydration that we've set up in the above [SSR Exchange section](#the-ssr-exchange) to use.
================================================
FILE: docs/advanced/subscriptions.md
================================================
---
title: Subscriptions
order: 0
---
# Subscriptions
One feature of `urql` that was not mentioned in the ["Basics" sections](../basics/README.md) is `urql`'s
APIs and ability to handle GraphQL subscriptions.
## The Subscription Exchange
To add support for subscriptions we need to add the `subscriptionExchange` to our `Client`.
```js
import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
const client = new Client({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
fetchExchange,
subscriptionExchange({
forwardSubscription,
}),
],
});
```
Read more about Exchanges and how they work [on the "Authoring Exchanges"
page.](./authoring-exchanges.md) or what they are [on the "Architecture"
page.](../architecture.md)
In the above example, we add the `subscriptionExchange` to the `Client` with the default exchanges
added before it. The `subscriptionExchange` is a factory that accepts additional options and returns
the actual `Exchange` function. It does not make any assumption over the transport protocol and
scheme that is used. Instead, we need to pass a `forwardSubscription` function.
The `forwardSubscription` is called when the `subscriptionExchange` receives an `Operation`, so
typically, when you’re executing a GraphQL subscription. This will call the `forwardSubscription`
function with a GraphQL request body, in the same shape that a GraphQL HTTP API may receive it as
JSON input.
If you’re using TypeScript, you may notice that the input that `forwardSubscription` receives has
an optional `query` property. This is because of persisted query support. For some transports, the
`query` property may have to be defaulted to an empty string, which matches the GraphQL over HTTP
specification more closely.
When we define this function it must return an "Observable-like" object, which needs to follow the
[Observable spec](https://github.com/tc39/proposal-observable), which comes down to having an
object with a `.subscribe()` method accepting an observer.
### Setting up `graphql-ws`
For backends supporting `graphql-ws`, we recommend using the [graphql-ws](https://github.com/enisdenjo/graphql-ws) client.
```js
import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
import { createClient as createWSClient } from 'graphql-ws';
const wsClient = createWSClient({
url: 'ws://localhost/graphql',
});
const client = new Client({
url: '/graphql',
exchanges: [
cacheExchange,
fetchExchange,
subscriptionExchange({
forwardSubscription(request) {
const input = { ...request, query: request.query || '' };
return {
subscribe(sink) {
const unsubscribe = wsClient.subscribe(input, sink);
return { unsubscribe };
},
};
},
}),
],
});
```
In this example, we're creating a `SubscriptionClient`, are passing in a URL and some parameters,
and are using the `SubscriptionClient`'s `request` method to create a Subscription Observable, which
we return to the `subscriptionExchange` inside `forwardSubscription`.
[Read more on the `graphql-ws` README.](https://github.com/enisdenjo/graphql-ws/blob/master/README.md)
### Setting up `subscriptions-transport-ws`
For backends supporting `subscriptions-transport-ws`, [Apollo's `subscriptions-transport-ws`
package](https://github.com/apollographql/subscriptions-transport-ws) can be used.
> The `subscriptions-transport-ws` package isn't actively maintained. If your API supports the new protocol or you can swap the package out, consider using [`graphql-ws`](#setting-up-graphql-ws) instead.
```js
import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
import { SubscriptionClient } from 'subscriptions-transport-ws';
const subscriptionClient = new SubscriptionClient('ws://localhost/graphql', { reconnect: true });
const client = new Client({
url: '/graphql',
exchanges: [
cacheExchange,
fetchExchange,
subscriptionExchange({
forwardSubscription: request => subscriptionClient.request(request),
}),
],
});
```
In this example, we're creating a `SubscriptionClient`, are passing in a URL and some parameters,
and are using the `SubscriptionClient`'s `request` method to create a Subscription Observable, which
we return to the `subscriptionExchange` inside `forwardSubscription`.
[Read more about `subscription-transport-ws` on its README.](https://github.com/apollographql/subscriptions-transport-ws/blob/master/README.md)
### Using `fetch` for subscriptions
Some GraphQL backends (for example GraphQL Yoga) support built-in transport protocols that
can execute subscriptions via a simple HTTP fetch call.
In fact, this is how `@defer` and `@stream` directives are supported. These transports can
also be used for subscriptions.
```js
import { Client, cacheExchange, fetchExchange, subscriptionExchange } from 'urql';
const client = new Client({
url: '/graphql',
fetchSubscriptions: true,
exchanges: [cacheExchange, fetchExchange],
});
```
In this example, we only need to enable `fetchSubscriptions: true` on the `Client`, and the
`fetchExchange` will be used to send subscriptions to the API. If your API supports this transport,
it will stream results back to the `fetchExchange`.
[You can find a code example of subscriptions via `fetch` in an example in the `urql` repository.](https://github.com/urql-graphql/urql/tree/main/examples/with-subscriptions-via-fetch)
## React & Preact
The `useSubscription` hooks comes with a similar API to `useQuery`, which [we've learned about in
the "Queries" page in the "Basics" section.](../basics/react-preact.md#queries)
Its usage is extremely similar in that it accepts options, which may contain `query` and
`variables`. However, it also accepts a second argument, which is a reducer function, similar to
what you would pass to `Array.prototype.reduce`.
It receives the previous set of data that this function has returned or `undefined`.
As the second argument, it receives the event that has come in from the subscription.
You can use this to accumulate the data over time, which is useful for a
list for example.
In the following example, we create a subscription that informs us of
new messages. We will concatenate the incoming messages so that we
can display all messages that have come in over the subscription across
events.
```js
import React from 'react';
import { useSubscription } from 'urql';
const newMessages = `
subscription MessageSub {
newMessages {
id
from
text
}
}
`;
const handleSubscription = (messages = [], response) => {
return [response.newMessages, ...messages];
};
const Messages = () => {
const [res] = useSubscription({ query: newMessages }, handleSubscription);
if (!res.data) {
return <p>No new messages</p>;
}
return (
<ul>
{res.data.map(message => (
<p key={message.id}>
{message.from}: "{message.text}"
</p>
))}
</ul>
);
};
```
As we can see, the `res.data` is being updated and transformed by
the `handleSubscription` function. This works over time, so as
new messages come in, we will append them to the list of previous
messages.
[Read more about the `useSubscription` API in the API docs for it.](../api/urql.md#usesubscription)
## Svelte
The `subscriptionStore` function in `@urql/svelte` comes with a similar API to `query`, which [we've
learned about in the "Queries" page in the "Basics" section.](../basics/svelte.md#queries)
Its usage is extremely similar in that it accepts an `operationStore`, which will typically contain
our GraphQL subscription query.
In the following example, we create a subscription that informs us of new messages.
```js
<script>
import { gql, getContextClient, subscriptionStore } from '@urql/svelte';
const messages = subscriptionStore({
client: getContextClient(),
query: gql`
subscription MessageSub {
newMessages {
id
from
text
}
}
`,
});
</script>
{#if !$messages.data}
<p>No new messages</p>
{:else}
<ul>
{#each $messages.data.newMessages as message}
<li>{message.from}: "{message.text}"</li>
{/each}
</ul>
{/if}
```
As we can see, `$messages.data` is being updated and transformed by the `$messages` subscriptionStore. This works over time, so as new messages come in, we will append them to
the list of previous messages.
`subscriptionStore` optionally accepts a second argument, a handler function, allowing custom update behavior from the subscription.
[Read more about the `subscription` API in the API docs for it.](../api/svelte.md#subscriptionstore)
## Vue
The `useSubscription` API is very similar to `useQuery`, which [we've learned about in
the "Queries" page in the "Basics" section.](../basics/vue.md#queries)
Its usage is extremely similar in that it accepts options, which may contain `query` and
`variables`. However, it also accepts a second argument, which is a reducer function, similar to
what you would pass to `Array.prototype.reduce`.
It receives the previous set of data that this function has returned or `undefined`.
As the second argument, it receives the event that has come in from the subscription.
You can use this to accumulate the data over time, which is useful for a
list for example.
In the following example, we create a subscription that informs us of
new messages. We will concatenate the incoming messages so that we
can display all messages that have come in over the subscription across
events.
```jsx
<template>
<div v-if="error">
Oh no... {{error}}
</div>
<div v-else>
<ul v-if="data">
<li v-for="msg in data">{{ msg.from }}: "{{ msg.text }}"</li>
</ul>
</div>
</template>
<script>
import { useSubscription } from '@urql/vue';
export default {
setup() {
const handleSubscription = (messages = [], response) => {
return [response.newMessages, ...messages];
};
const result = useSubscription({
query: `
subscription MessageSub {
newMessages {
id
from
text
}
}
`,
}, handleSubscription)
return {
data: result.data,
error: result.error,
};
}
};
</script>
```
As we can see, the `result.data` is being updated and transformed by
the `handleSubscription` function. This works over time, so as
new messages come in, we will append them to the list of previous
messages.
[Read more about the `useSubscription` API in the API docs for it.](../api/vue.md#usesubscription)
## One-off Subscriptions
When you're using subscriptions directly without `urql`'s framework bindings, you can use the
`Client`'s `subscription` method for one-off subscriptions. This method is similar to the ones for
mutations and subscriptions [that we've seen before on the "Core Package" page.](../basics/core.md)
This method will always [returns a Wonka stream](../architecture.md#the-wonka-library) and doesn't
have a `.toPromise()` shortcut method, since promises won't return the multiple values that a
subscription may deliver. Let's convert the above example to one without framework code, as we may
use subscriptions in a Node.js environment.
```js
import { gql } from '@urql/core';
const MessageSub = gql`
subscription MessageSub {
newMessages {
id
from
text
}
}
`;
const { unsubscribe } = client.subscription(MessageSub).subscribe(result => {
console.log(result); // { data: ... }
});
```
================================================
FILE: docs/advanced/testing.md
================================================
---
title: Testing
order: 7
---
# Testing
Testing with `urql` can be done in a multitude of ways. The most effective and straightforward
method is to mock the `Client` to force your components into a fixed state during testing.
The following examples demonstrate this method of testing for React and the `urql` package only,
however the pattern itself can be adapted for any framework-bindings of `urql`.
## Mocking the client
For the most part, urql's hooks are just adapters for talking to the urql client.
The way in which they do this is by making calls to the client via context.
- `useQuery` calls `executeQuery`
- `useMutation` calls `executeMutation`
- `useSubscription` calls `executeSubscription`
In the section ["Stream Patterns" on the "Architecture" page](../architecture.md) we've seen, that
all methods on the client operate with and return streams. These streams are created using
[the Wonka library](../architecture.md#the-wonka-library), and we're able to create streams
ourselves to mock the different states of our operations, e.g. fetching, errors, or success with data.
You'll probably use one of these utility functions to create streams:
- `never`: This stream doesn’t emit any values and never completes, which puts our `urql` code in a permanent `fetching: true` state.
- `fromValue`: This utility function accepts a value and emits it immediately, which we can use to mock a result from the server.
- `makeSubject`: Allows us to create a source and imperatively push responses, which is useful to test subscription and simulate changes, i.e. multiple states.
Creating a mock `Client` is pretty quick as we'll create an object that contains the `Client`'s methods that the React `urql` hooks use. We'll mock the appropriate `execute` functions that we need to mock a set of hooks. After we've created the mock `Client` we can wrap components with the `Provider` from `urql` and pass it.
Here's an example client mock being used while testing a component.
```tsx
import { mount } from 'enzyme';
import { Provider } from 'urql';
import { never } from 'wonka';
import { MyComponent } from './MyComponent';
it('renders', () => {
const mockClient = {
executeQuery: jest.fn(() => never),
executeMutation: jest.fn(() => never),
executeSubscription: jest.fn(() => never),
};
const wrapper = mount(
<Provider value={mockClient}>
<MyComponent />
</Provider>
);
});
```
## Testing calls to the client
Once you have your mock setup, calls to the client can be tested.
```tsx
import { mount } from 'enzyme';
import { Provider } from 'urql';
import { MyComponent } from './MyComponent';
it('skips the query', () => {
mount(
<Provider value={mockClient}>
<MyComponent skip={true} />
</Provider>
);
expect(mockClient.executeQuery).toBeCalledTimes(0);
});
```
Testing mutations and subscriptions also work in a similar fashion.
```tsx
import { mount } from 'enzyme';
import { Provider } from 'urql';
import { MyComponent } from './MyComponent';
it('triggers a mutation', () => {
const wrapper = mount(
<Provider value={mockClient}>
<MyComponent />
</Provider>
);
const variables = { name: 'Carla' };
wrapper.find('input').simulate('change', { currentTarget: { value: variables.name } });
wrapper.find('button').simulate('click');
expect(mockClient.executeMutation).toBeCalledTimes(1);
expect(mockClient.executeMutation).toBeCalledWith(expect.objectContaining({ variables }), {});
});
```
## Forcing states
For testing render output, or creating fixtures, you may want to force the state of your components.
### Fetching
Fetching states can be simulated by returning a stream, which never returns. Wonka provides a utility for this, aptly called `never`.
Here's a fixture, which stays in the _fetching_ state.
```tsx
import { Provider } from 'urql';
import { never } from 'wonka';
import { MyComponent } from './MyComponent';
const fetchingState = {
executeQuery: () => never,
};
export default (
<Provider value={fetchingState}>
<MyComponent />
</Provider>
);
```
### Response (success)
Response states are simulated by providing a stream, which contains a network response. For single responses, Wonka's `fromValue` function can do this for us.
**Example snapshot test of response state**
```tsx
import { mount } from 'enzyme';
import { Provider } from 'urql';
import { fromValue } from 'wonka';
import { MyComponent } from './MyComponent';
it('matches snapshot', () => {
const responseState = {
executeQuery: () =>
fromValue({
data: {
posts: [
{ id: 1, title: 'Post title', content: 'This is a post' },
{ id: 3, title: 'Final post', content: 'Final post here' },
],
},
}),
};
const wrapper = mount(
<Provider value={responseState}>
<MyComponent />
</Provider>
);
expect(wrapper).toMatchSnapshot();
});
```
### Response (error)
Error responses are similar to success responses, only the value in the stream is changed.
```tsx
import { Provider, CombinedError } from 'urql';
import { fromValue } from 'wonka';
const errorState = {
executeQuery: () =>
fromValue({
error: new CombinedError({
networkError: Error('something went wrong!'),
}),
}),
};
```
### Handling multiple hooks
Returning different values for many `useQuery` calls can be done by introducing conditionals into the mocked client functions.
```tsx
import { fromValue } from 'wonka';
let mockClient;
beforeEach(() => {
mockClient = () => {
executeQuery: ({ query }) => {
if (query === GET_USERS) {
return fromValue(usersResponse);
}
if (query === GET_POSTS) {
return fromValue(postsResponse);
}
};
};
});
```
The above client we've created mocks all three operations — queries, mutations and subscriptions — to always remain in the `fetching: true` state.
Generally when we're _hoisting_ our mocked client and reuse it across multiple tests we have to be
mindful not to instantiate the mocks outside of Jest's lifecycle functions (like `it`, `beforeEach`,
`beforeAll` and such) as it may otherwise reset our mocked functions' return values or
implementation.
## Subscriptions
Testing subscriptions can be done by simulating the arrival of new data over time. To do this we may use the `interval` utility from Wonka, which emits values on a timer, and for each value we can map over the response that we'd like to mock.
If you prefer to have more control on when the new data is arriving you can use the `makeSubject` utility from Wonka. You can see more details in the next section.
Here's an example of testing a list component, which uses a subscription.
```tsx
import { OperationContext, makeOperation } from '@urql/core';
import { mount } from 'enzyme';
import { Provider } from 'urql';
import { MyComponent } from './MyComponent';
it('should update the list', done => {
const mockClient = {
executeSubscription: jest.fn(query =>
pipe(
interval(200),
map((i: number) => ({
// To mock a full result, we need to pass a mock operation back as well
operation: makeOperation('subscription', query, {} as OperationContext),
data: { posts: { id: i, title: 'Post title', content: 'This is a post' } },
}))
)
),
};
let index = 0;
const wrapper = mount(
<Provider value={mockClient}>
<MyComponent />
</Provider>
);
setTimeout(() => {
expect(wrapper.find('.list').children()).toHaveLength(index + 1); // See how many items are in the list
index++;
if (index === 2) done();
}, 200);
});
```
## Simulating changes
Simulating multiple responses can be useful, particularly testing `useEffect` calls dependent on changing query responses.
For this, a _subject_ is the way to go. In short, it's a stream that you can push responses to. The `makeSubject` function from Wonka is what you'll want to use for this purpose.
Below is an example of simulating subsequent responses (such as a cache update/refetch) in a test.
```tsx
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { Provider } from 'urql';
import { makeSubject } from 'wonka';
import { MyComponent } from './MyComponent';
const { source: stream, next: pushResponse } = makeSubject();
it('shows notification on updated data', () => {
const mockedClient = {
executeQuery: jest.fn(() => stream),
};
const wrapper = mount(
<Provider value={mockedClient}>
<MyComponent />
</Provider>
);
// First response
act(() => {
pushResponse({
data: {
posts: [{ id: 1, title: 'Post title', content: 'This is a post' }],
},
});
});
expect(wrapper.find('dialog').exists()).toBe(false);
// Second response
act(() => {
pushResponse({
data: {
posts: [
{ id: 1, title: 'Post title', content: 'This is a post' },
{ id: 1, title: 'Post title', content: 'This is a post' },
],
},
});
});
expect(wrapper.find('dialog').exists()).toBe(true);
});
```
================================================
FILE: docs/api/README.md
================================================
---
title: API
order: 9
---
# API
`urql` is a collection of multiple packages. You'll likely be using one of the framework bindings
package or exchange packages, which are all listed in this section.
Most of these packages will refer to or use utilities and types from the `@urql/core` package. [Read
more about the core package on the "Core" page.](../basics/core.md)
> **Note:** These API docs are deprecated as we now keep TSDocs in all published packages.
> You can view TSDocs while using these packages in your editor, as long as it supports the
> TypeScript Language Server.
> We're planning to replace these API docs with a separate web app soon.
- [`@urql/core` API docs](./core.md)
- [`urql` React API docs](./urql.md)
- [`@urql/preact` Preact API docs](./preact.md)
- [`@urql/svelte` Svelte API docs](./svelte.md)
- [`@urql/exchange-graphcache` API docs](./graphcache.md)
- [`@urql/exchange-retry` API docs](./retry-exchange.md)
- [`@urql/exchange-execute` API docs](./execute-exchange.md)
- [`@urql/exchange-request-policy` API docs](./request-policy-exchange.md)
- [`@urql/exchange-auth` API docs](./auth-exchange.md)
- [`@urql/exchange-refocus` API docs](./refocus-exchange.md)
================================================
FILE: docs/api/auth-exchange.md
================================================
---
title: '@urql/exchange-auth'
order: 10
---
# Authentication Exchange
> **Note:** These API docs are deprecated as we now keep TSDocs in all published packages.
> You can view TSDocs while using these packages in your editor, as long as it supports the
> TypeScript Language Server.
> We're planning to replace these API docs with a separate web app soon.
The `@urql/exchange-auth` package contains an addon `authExchange` for `urql` that aims to make it
easy to implement complex authentication and reauthentication flows as are typically found with JWT
token based API authentication.
## Installation and Setup
First install `@urql/exchange-auth` alongside `urql`:
```sh
yarn add @urql/exchange-auth
# or
npm install --save @urql/exchange-auth
```
You'll then need to add the `authExchange`, that this package exposes to your `Client`. The
`authExchange` is an asynchronous exchange, so it must be placed in front of all `fetchExchange`s
but after all other synchronous exchanges, like the `cacheExchange`.
```js
import { createClient, cacheExchange, fetchExchange } from 'urql';
import { authExchange } from '@urql/exchange-auth';
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
authExchange(async utils => {
return {
/* config... */
};
}),
fetchExchange,
],
});
```
The `authExchange` accepts an initialization function. This function is called when your exchange
and `Client` first start up, and must return an object of options wrapped in a `Promise`, which is
used to configure how your authentication method works.
You can use this function to first retrieve your authentication state from a kind
of local storage, or to call your API to validate your authentication state first.
The relevant configuration options, returned to the `authExchange`, then determine
how the `authExchange` behaves:
- `addAuthToOperation` must be provided to tell `authExchange` how to add authentication information
to an operation, e.g. how to add the authentication state to an operation's fetch headers.
- `willAuthError` may be provided to detect expired tokens or tell whether an operation will likely
fail due to an authentication error.
- `didAuthError` may be provided to let the `authExchange` detect authentication errors from the
API on results.
- `refreshAuth` is called when an authentication error occurs and gives you an opportunity to update
your authentication state. Afterwards, the `authExchange` will retry your operation.
[Read more examples in the documentation given here.](../advanced/authentication.md)
================================================
FILE: docs/api/core.md
================================================
---
title: '@urql/core'
order: 0
---
# @urql/core
> **Note:** These API docs are deprecated as we now keep TSDocs in all published packages.
> You can view TSDocs while using these packages in your editor, as long as it supports the
> TypeScript Language Server.
> We're planning to replace these API docs with a separate web app soon.
The `@urql/core` package is the basis of all framework bindings. Each bindings-package,
like [`urql` for React](./urql.md) or [`@urql/preact`](./preact.md), will reuse the core logic and
reexport all exports from `@urql/core`.
Therefore if you're not accessing utilities directly, aren't in a Node.js environment, and are using
framework bindings, you'll likely want to import from your framework bindings package directly.
[Read more about `urql`'s core on the "Core Package" page.](../basics/core.md)
## Client
The `Client` manages all operations and ongoing requests to the exchange pipeline.
It accepts several options on creation.
`@urql/core` also exposes `createClient()` that is just a convenient alternative to calling `new Client()`.
| Input | Type | Description |
| ----------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `exchanges` | `Exchange[]` | An array of `Exchange`s that the client should use |
| `url` | `string` | The GraphQL API URL as used by `fetchExchange` |
| `fetchOptions` | `RequestInit \| () => RequestInit` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request |
| `fetch` | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` |
| `suspense` | `?boolean` | Activates the experimental React suspense mode, which can be used during server-side rendering to prefetch data |
| `requestPolicy` | `?RequestPolicy` | Changes the default request policy that will be used. By default, this will be `cache-first`. |
| `preferGetMethod` | `?boolean \| 'force' \| 'within-url-limit'` | This is picked up by the `fetchExchange` and will force all queries (not mutations) to be sent using the HTTP GET method instead of POST if the length of the resulting URL doesn't exceed 2048 characters. When `'force'` is passed a GET request is always sent regardless of how long the resulting URL is. |
### client.executeQuery
Accepts a [`GraphQLRequest`](#graphqlrequest) and optionally `Partial<OperationContext>`, and returns a
[`Source<OperationResult>`](#operationresult) — a stream of query results that can be subscribed to.
Internally, subscribing to the returned source will create an [`Operation`](#operation), with
`kind` set to `'query'`, and dispatch it on the
exchanges pipeline. If no subscribers are listening to this operation anymore and unsubscribe from
the query sources, the `Client` will dispatch a "teardown" operation.
- [Instead of using this method directly, you may want to use the `client.query` shortcut
instead.](#clientquery)
- [See `createRequest` for a utility that creates `GraphQLRequest` objects.](#createrequest)
### client.executeSubscription
This is functionally the same as `client.executeQuery`, but creates operations for subscriptions
instead, with `kind` set to `'subscription'`.
### client.executeMutation
This is functionally the same as `client.executeQuery`, but creates operations for mutations
instead, with `kind` set to `'mutation'`.
A mutation source is always guaranteed to only respond with a single [`OperationResult`](#operationresult) and then complete.
### client.query
This is a shorthand method for [`client.executeQuery`](#clientexecutequery), which accepts a query
(`DocumentNode | string`) and variables separately and creates a [`GraphQLRequest`](#graphqlrequest) [`createRequest`](#createrequest) automatically.
The returned `Source<OperationResult>` will also have an added `toPromise` method, so the stream can
be conveniently converted to a promise.
```js
import { pipe, subscribe } from 'wonka';
const { unsubscribe } = pipe(
client.query('{ test }', {
/* vars */
}),
subscribe(result => {
console.log(result); // OperationResult
})
);
// or with toPromise, which also limits this to one result
client
.query('{ test }', {
/* vars */
})
.toPromise()
.then(result => {
console.log(result); // OperationResult
});
```
[Read more about how to use this API on the "Core Package"
page.](../basics/core.md#one-off-queries-and-mutations)
### client.mutation
This is similar to [`client.query`](#clientquery), but dispatches mutations instead.
[Read more about how to use this API on the "Core Package"
page.](../basics/core.md#one-off-queries-and-mutations)
### client.subscription
This is similar to [`client.query`](#clientquery), but does not provide a `toPromise()` helper method on the streams it returns.
[Read more about how to use this API on the "Subscriptions" page.](../advanced/subscriptions.md)
### client.reexecuteOperation
This method is commonly used in _Exchanges_ to reexecute an [`Operation`](#operation) on the
`Client`. It will only reexecute when there are still subscribers for the given
[`Operation`](#operation).
For an example, this method is used by the `cacheExchange` when an
[`OperationResult`](#operationresult) is invalidated in the cache and needs to be refetched.
### client.readQuery
This method is typically used to read data synchronously from a cache. It returns an [`OperationResult`](#operationresult) if a value is returned immediately or `null` if no value is returned while cancelling all side effects.
## CombinedError
The `CombinedError` is used in `urql` to normalize network errors and `GraphQLError`s if anything
goes wrong during a GraphQL request.
| Input | Type | Description |
| --------------- | -------------------------------- | ---------------------------------------------------------------------------------- |
| `networkError` | `?Error` | An unexpected error that might've occurred when trying to send the GraphQL request |
| `graphQLErrors` | `?Array<string \| GraphQLError>` | GraphQL Errors (if any) that were returned by the GraphQL API |
| `response` | `?any` | The raw response object (if any) from the `fetch` call |
[Read more about errors in `urql` on the "Error" page.](../basics/errors.md)
## Types
### GraphQLRequest
This often comes up as the **input** for every GraphQL request.
It consists of `query` and optionally `variables`.
| Prop | Type | Description |
| ----------- | -------------- | --------------------------------------------------------------------------------------------------------------------- |
| `key` | `number` | A unique key that identifies this exact combination of `query` and `variables`, which is derived using a stable hash. |
| `query` | `DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. |
| `variables` | `?object` | The variables to be used with the GraphQL request. |
The `key` property is a hash of both the `query` and the `variables`, to uniquely
identify the request. When `variables` are passed it is ensured that they're stably stringified so
that the same variables in a different order will result in the same `key`, since variables are
order-independent in GraphQL.
[A `GraphQLRequest` may be manually created using the `createRequest` helper.](#createrequest)
### OperationType
This determines what _kind of operation_ the exchanges need to perform.
This is one of:
- `'subscription'`
- `'query'`
- `'mutation'`
- `'teardown'`
The `'teardown'` operation is special in that it instructs exchanges to cancel
any ongoing operations with the same key as the `'teardown'` operation that is
received.
### Operation
The input for every exchange that informs GraphQL requests.
It extends the [`GraphQLRequest` type](#graphqlrequest) and contains these additional properties:
| Prop | Type | Description |
| --------- | ------------------ | --------------------------------------------- |
| `kind` | `OperationType` | The type of GraphQL operation being executed. |
| `context` | `OperationContext` | Additional metadata passed to exchange. |
An `Operation` also contains the `operationName` property, which is a deprecated alias of the `kind`
property and outputs a deprecation warning if it's used.
### RequestPolicy
This determines the strategy that a cache exchange should use to fulfill an operation.
When you implement a custom cache exchange it's recommended that these policies are
handled.
- `'cache-first'` (default)
- `'cache-only'`
- `'network-only'`
- `'cache-and-network'`
[Read more about request policies on the "Document Caching" page.](../basics/document-caching.md#request-policies)
### OperationContext
The context often carries options or metadata for individual exchanges, but may also contain custom
data that can be passed from almost all API methods in `urql` that deal with
[`Operation`s](#operation).
Some of these options are set when the `Client` is initialised, so in the following list of
properties you'll likely see some options that exist on the `Client` as well.
| Prop | Type | Description |
| --------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `fetchOptions` | `?RequestInit \| (() => RequestInit)` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request. |
| `fetch` | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` |
| `requestPolicy` | `RequestPolicy` | An optional [request policy](../basics/document-caching.md#request-policies) that should be used specifying the cache strategy. |
| `url` | `string` | The GraphQL endpoint, when using GET you should use absolute url's |
| `meta` | `?OperationDebugMeta` | Metadata that is only available in development for devtools. |
| `suspense` | `?boolean` | Whether suspense is enabled. |
| `preferGetMethod` | `?boolean \| 'force' \| 'within-url-limit'` | Instructs the `fetchExchange` to use HTTP GET for queries. |
| `additionalTypenames` | `?string[]` | Allows you to tell the operation that it depends on certain typenames (used in document-cache.) |
It also accepts additional, untyped parameters that can be used to send more
information to custom exchanges.
### OperationResult
The result of every GraphQL request, i.e. an `Operation`. It's very similar to what comes back from
a typical GraphQL API, but slightly enriched and normalized.
| Prop | Type | Description |
| ------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `operation` | `Operation` | The operation that this is a result for |
| `data` | `?any` | Data returned by the specified query |
| `error` | `?CombinedError` | A [`CombinedError`](#combinederror) instances that wraps network or `GraphQLError`s (if any) |
| `extensions` | `?Record<string, any>` | Extensions that the GraphQL server may have returned. |
| `stale` | `?boolean` | A flag that may be set to `true` by exchanges to indicate that the `data` is incomplete or out-of-date, and that the result will be updated soon. |
### ExchangeInput
This is the input that an [`Exchange`](#exchange) receives when it's initialized by the
[`Client`](#client)
| Input | Type | Description |
| --------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `forward` | `ExchangeIO` | The unction responsible for receiving an observable operation and returning a result |
| `client` | `Client` | The urql application-wide client library. Each execute method starts a GraphQL request and returns a stream of results. |
### Exchange
An exchange represents abstractions of small chunks of logic in `urql`.
They're small building blocks and similar to "middleware".
[Read more about _Exchanges_ on the "Authoring Exchanges" page.](../advanced/authoring-exchanges.md)
An exchange is defined to be a function that receives [`ExchangeInput`](#exchangeinput) and returns
an `ExchangeIO` function. The `ExchangeIO` function in turn will receive a stream of operations, and
must return a stream of results. If the exchange is purely transforming data, like the
`mapExchange` for instance, it'll call `forward`, which is the next Exchange's `ExchangeIO`
function to get a stream of results.
```js
type ExchangeIO = (Source<Operation>) => Source<OperationResult>;
type Exchange = ExchangeInput => ExchangeIO;
```
[If you haven't yet seen streams you can read more about "Stream Patterns" on the "Architecture"
page.](../architecture.md)
## Exchanges
### cacheExchange
The `cacheExchange` as [described on the "Document Caching" page.](../basics/document-caching.md). It's of type `Exchange`.
### subscriptionExchange
The `subscriptionExchange` as [described on the "Subscriptions" page.](../advanced/subscriptions.md). It's of type `Options => Exchange`.
It accepts a single input: `{ forwardSubscription }`. This is a function that
receives an enriched operation and must return an Observable-like object that
streams `GraphQLResult`s with `data` and `errors`.
The `forwardSubscription` function is commonly connected to the [`subscriptions-transport-ws`
package](https://github.com/apollographql/subscriptions-transport-ws).
### ssrExchange
The `ssrExchange` as [described on the "Server-side Rendering"
page.](../advanced/server-side-rendering.md).
It's of type `Options => Exchange`.
It accepts three inputs, `initialState` which is completely
optional and populates the server-side rendered data with
a rehydrated cache, `isClient` which can be set to
`true` or `false` to tell the `ssrExchange` whether to
write to (server-side) or read from (client-side) the cache, and
`staleWhileRevalidate` which will treat rehydrated data as stale
and refetch up-to-date data by reexecuring the operation using a `network-only` requests policy.
By default, `isClient` defaults to `true` when the `Client.suspense`
mode is disabled and to `false` when the `Client.suspense` mode
is enabled.
This can be used to extract data that has been queried on
the server-side, which is also described in the Basics section,
and is also used on the client-side to restore server-side
rendered data.
When called, this function creates an `Exchange`, which also has
two methods on it:
- `.restoreData(data)` which can be used to inject data, typically
on the client-side.
- `.extractData()` which is typically used on the server-side to
extract the server-side rendered data.
Basically, the `ssrExchange` is a small cache that collects data
during the server-side rendering pass, and allows you to populate
the cache on the client-side with the same data.
During React rehydration this cache will be emptied, and it will
become inactive and won't change the results of queries after
rehydration.
It needs to be used _after_ other caching Exchanges like the
`cacheExchange`, but before any _asynchronous_ Exchange like
the `fetchExchange`.
### debugExchange
An exchange that writes incoming `Operation`s to `console.log` and
writes completed `OperationResult`s to `console.log`.
This exchange is disabled in production and is based on the `mapExchange`.
If you'd like to customise it, you can replace it with a custom `mapExchange`.
### fetchExchange
The `fetchExchange` of type `Exchange` is responsible for sending operations of type `'query'` and
`'mutation'` to a GraphQL API using `fetch`.
### mapExchange
The `mapExchange` allows you to:
- react to or replace operations with `onOperation`,
- react to or replace results with `onResult`,
- and; react to errors in results with `onError`.
It can therefore be used to quickly react to the core events in the `Client` without writing a custom
exchange, effectively allowing you to ship your own `debugExchange`.
```ts
mapExchange({
onOperation(operation) {
console.log('operation', operation);
},
onResult(result) {
console.log('result', result);
},
});
```
It can also be used to react only to errors, which is the same as checking for `result.error`:
```ts
mapExchange({
onError(error, operation) {
console.log(`The operation ${operation.key} has errored with:`, error);
},
});
```
Lastly, it can be used to map operations and results, which may be useful to update the
`OperationContext` or perform other standard tasks that require you to wait for a result:
```ts
import { mapExchange, makeOperation } from '@urql/core';
mapExchange({
async onOperation(operation) {
// NOTE: This is only for illustration purposes
return makeOperation(operation.kind, operation, {
...operation.context,
test: true,
});
},
async onResult(result) {
// NOTE: This is only for illustration purposes
if (result.data === undefined) result.data = null;
return result;
},
});
```
### errorExchange (deprecated)
An exchange that lets you inspect errors. This can be useful for logging, or reacting to
different types of errors (e.g. logging the user out in case of a permission error).
In newer versions of `@urql/core`, it's identical to the `mapExchange` and its export has been
replaced as the `mapExchange` also allows you to pass an `onError` function.
## Utilities
### gql
This is a `gql` tagged template literal function, similar to the one that's also commonly known from
`graphql-tag`. It can be used to write GraphQL documents in a tagged template literal and returns a
parsed `DocumentNode` that's primed against the `createRequest`'s cache for `key`s.
```js
import { gql } from '@urql/core';
const SharedFragment = gql`
fragment UserFrag on User {
id
name
}
`;
gql`
query {
user
...UserFrag
}
${SharedFragment}
`;
```
Unlike `graphql-tag`, this function outputs a warning in development when names of fragments in the
document are duplicated. It does not output warnings when fragment names were duplicated globally
however.
### stringifyVariables
This function is a variation of `JSON.stringify` that sorts any object's keys that is being
stringified to ensure that two objects with a different order of keys will be stably stringified to
the same string.
```js
stringifyVariables({ a: 1, b: 2 }); // {"a":1,"b":2}
stringifyVariables({ b: 2, a: 1 }); // {"a":1,"b":2}
```
### createRequest
This utility accepts a GraphQL query of type `string | DocumentNode` and optionally an object of
variables, and returns a [`GraphQLRequest` object](#graphqlrequest).
Since the [`client.executeQuery`](#clientexecutequery) and other execute methods only accept
[`GraphQLRequest`s](#graphqlrequest), this helper is commonly used to create that request first. The
[`client.query`](#clientquery) and [`client.mutation`](#clientmutation) methods use this helper as
well to create requests.
The helper takes care of creating a unique `key` for the `GraphQLRequest`. This is a hash of the
`query` and `variables` if they're passed. The `variables` will be stringified using
[`stringifyVariables`](#stringifyvariables), which outputs a stable JSON string.
Additionally, this utility will ensure that the `query` reference will remain stable. This means
that if the same `query` will be passed in as a string or as a fresh `DocumentNode`, then the output
will always have the same `DocumentNode` reference.
### makeOperation
This utility is used to either turn a [`GraphQLRequest` object](#graphqlrequest) into a new
[`Operation` object](#operation) or to copy an `Operation`. It adds the `kind` property, and the
`operationName` alias that outputs a deprecation warning.
It accepts three arguments:
- An `Operation`'s `kind` (See [`OperationType`](#operationtype)
- A [`GraphQLRequest` object](#graphqlrequest) or another [`Operation`](#operation) that should be
copied.
- and; optionally a [partial `OperationContext` object.](#operationcontext). This argument may be
left out if the context is to be copied from the operation that may be passed as a second argument.
Hence some valid uses of the utility are:
```js
// Create a new operation from scratch
makeOperation('query', createRequest(query, variables), client.createOperationContext(opts));
// Turn an operation into a 'teardown' operation
makeOperation('teardown', operation);
// Copy an existing operation while modifying its context
makeOperation(operation.kind, operation, {
...operation.context,
preferGetMethod: true,
});
```
### makeResult
This is a helper function that converts a GraphQL API result to an
[`OperationResult`](#operationresult).
It accepts an [`Operation`](#operation), the API result, and optionally the original `FetchResponse`
for debugging as arguments, in that order.
### makeErrorResult
This is a helper function that creates an [`OperationResult`](#operationresult) for GraphQL API
requests that failed with a generic or network error.
It accepts an [`Operation`](#operation), the error, and optionally the original `FetchResponse`
for debugging as arguments, in that order.
### formatDocument
This utility is used by the [`cacheExchange`](#cacheexchange) and by
[Graphcache](../graphcache/README.md) to add `__typename` fields to GraphQL `DocumentNode`s.
### composeExchanges
This utility accepts an array of `Exchange`s and composes them into a single one.
It chains them in the order that they're given, left to right.
```js
function composeExchanges(Exchange[]): Exchange;
```
This can be used to combine some exchanges and is also used by [`Client`](#client)
to handle the `exchanges` input.
================================================
FILE: docs/api/execute-exchange.md
================================================
---
title: '@urql/exchange-execute'
order: 6
---
# Execute Exchange
> **Note:** These API docs are deprecated as we now keep TSDocs in all published packages.
> You can view TSDocs while using these packages in your editor, as long as it supports the
> TypeScript Language Server.
> We're planning to replace these API docs with a separate web app soon.
The `@urql/exchange-execute` package contains an addon `executeExchange` for `urql` that may be used to
execute queries against a local schema. It is therefore a drop-in replacement for the default
_fetchExchange_ and useful for the server-side, debugging, or testing.
## Installation and Setup
First install `@urql/exchange-execute` alongside `urql`:
```sh
yarn add @urql/exchange-execute
# or
npm install --save @urql/exchange-execute
```
You'll then need to add the `executeExchange`, exposed by this package, to your `Client`.
It'll typically replace the `fetchExchange` or similar exchanges and must be used last if possible,
since it'll handle operations and return results.
```js
import { createClient, cacheExchange } from 'urql';
import { executeExchange } from '@urql/exchange-execute';
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [
cacheExchange,
executeExchange({
/* config */
}),
],
});
```
The `executeExchange` accepts an object of options, which are all similar to the arguments that
`graphql/execution/execute` accepts. Typically you'd pass it the `schema` option, some resolvers
if your schema isn't already executable as `fieldResolver` / `typeResolver` / `rootValue`,
and a `context` value or function.
## Options
| Option | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `schema` | This is of type `GraphQLSchema` and accepts either a schema that is or isn't executable. This field is _required_ while all other fields are _optional_. |
| `rootValue` | The root value that `graphql`'s `execute` will use when starting to execute the schema. |
| `fieldResolver` | A given field resolver function. Creating an executable schema may be easier than providing this, but this resolver will be passed on to `execute` as expected. |
| `typeResolver` | A given type resolver function. Creating an executable schema may be easier than providing this, but this resolver will be passed on to `execute` as expected. |
| `context` | This may either be a function that receives an [`Operation`](./core.md#operation) and returns the context value, or just a plain context value. Similarly to a GraphQL server this is useful as all resolvers will have access to your `context` |
================================================
FILE: docs/api/graphcache.md
================================================
---
title: '@urql/exchange-graphcache'
order: 4
---
# @urql/exchange-graphcache
> **Note:** These API docs are deprecated as we now keep TSDocs in all published packages.
> You can view TSDocs while using these packages in your editor, as long as it supports the
> TypeScript Language Server.
> We're planning to replace these API docs with a separate web app soon.
The `@urql/exchange-graphcache` package contains an addon `cacheExchange` for `urql` that may be
used to replace the default [`cacheExchange`](./core.md#cacheexchange), which switches `urql` from
using ["Document Caching"](../basics/document-caching.md) to ["Normalized
Caching"](../graphcache/normalized-caching.md).
[Read more about how to use and configure _Graphcache_ in the "Graphcache"
section](../graphcache/README.md)
## cacheExchange
The `cacheExchange` function, as exported by `@urql/exchange-graphcache`, accepts a single object of
options and returns an [`Exchange`](./core.md#exchange).
| Input | Description |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `keys` | A mapping of key generator functions for types that are used to override the default key generation that _Graphcache_ uses to normalize data for given types. |
| `resolvers` | A nested mapping of resolvers, which are used to override the record or entity that _Graphcache_ resolves for a given field for a type. |
| `directives` | A mapping of directives, which are functions accepting directive arguments and returning a resolver, which can be referenced by `@localDirective` or `@_localDirective` in queries. |
| `updates` | A nested mapping of updater functions for mutation and subscription fields, which may be used to add side-effects that update other parts of the cache when the given subscription or mutation field is written to the cache. |
| `optimistic` | A mapping of mutation fields to resolvers that may be used to provide _Graphcache_ with an optimistic result for a given mutation field that should be applied to the cached data temporarily. |
| `schema` | A serialized GraphQL schema that is used by _Graphcache_ to resolve partial data, interfaces, and enums. The schema also used to provide helpful warnings for [schema awareness](../graphcache/schema-awareness.md). |
| `storage` | A persisted storage interface that may be provided to preserve cache data for [offline support](../graphcache/offline.md). |
| `globalIDs` | A boolean or list of typenames that have globally unique ids, this changes how graphcache internally keys the entities. This can be useful for complex interface relationships. |
| `logger` | A function that will be invoked for warning/debug/... logs |
The `@urql/exchange-graphcache` package also exports the `offlineExchange`; which is identical to
the `cacheExchange` but activates [offline support](../graphcache/offline.md) when the `storage` option is passed.
### `keys` option
This is a mapping of typenames to `KeyGenerator` functions.
```ts
interface KeyingConfig {
[typename: string]: (data: Data) => null | string;
}
```
It may be used to alter how _Graphcache_ generates the key it uses for normalization for individual
types. The key generator function may also always return `null` when a type should always be
embedded.
[Read more about how to set up `keys` in the "Key Generation" section of the "Normalized Caching"
page.](../graphcache/normalized-caching.md#key-generation)
### `resolvers` option
This configuration is a mapping of typenames to field names to `Resolver` functions.
A resolver may be defined to override the entity or record that a given field on a type should
resolve on the cache.
```ts
interface ResolverConfig {
[typeName: string]: {
[fieldName: string]: Resolver;
};
}
```
A `Resolver` receives four arguments when it's called: `parent`, `args`, `cache`, and
`info`.
| Argument | Type | Description |
| -------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `parent` | `Data` | The parent entity that the given field is on. |
| `args` | `object` | The arguments for the given field the updater is executed on. |
| `cache` | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) |
| `info` | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) |
We can use the arguments it receives to either return new data based on just the arguments and other
cache information, but we may also read information about the parent and return new data for the
current field.
```js
{
Todo: {
createdAt(parent, args, cache) {
// Read `createdAt` on the parent but return a Date instance
const date = cache.resolve(parent, 'createdAt');
return new Date(date);
}
}
}
```
[Read more about how to set up `resolvers` on the "Computed Queries"
page.](../graphcache/local-resolvers.md)
### `updates` option
The `updates` configuration is a mapping of `'Mutation' | 'Subscription'` to field names to
`UpdateResolver` functions. An update resolver may be defined to add side-effects that run when a
given mutation field or subscription field is written to the cache. These side-effects are helpful
to update data in the cache that is implicitly changed on the GraphQL API, that _Graphcache_ can't
know about automatically.
For mutation fields that don't have an updater, Graphcache has a fallback: if a returned entity
isn't currently found in the cache, it assumes a create-mutation and invalidates cached
entities of that type. This behavior was introduced in Graphcache v7 and is skipped once an updater
for the mutation field is added.
```ts
interface UpdatesConfig {
Mutation: {
[fieldName: string]: UpdateResolver;
};
Subscription: {
[fieldName: string]: UpdateResolver;
};
}
```
An `UpdateResolver` receives four arguments when it's called: `result`, `args`, `cache`, and
`info`.
| Argument | Type | Description |
| -------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `result` | `any` | Always the entire `data` object from the mutation or subscription. |
| `args` | `object` | The arguments for the given field the updater is executed on. |
| `cache` | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) |
| `info` | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) |
It's possible to derive more information about the current update using the `info` argument. For
instance this metadata contains the current `fieldName` of the updater which may be used to make an
updater function more reusable, along with `parentKey` and other key fields. It also contains
`variables` and `fragments` which remain the same for the entire write operation, and additionally
it may have the `error` field set to describe whether the current field is `null` because the API
encountered a `GraphQLError`.
[Read more about how to set up `updates` on the "Custom Updates"
page.](../graphcache/cache-updates.md)
### `optimistic` option
The `optimistic` configuration is a mapping of Mutation field names to `OptimisticMutationResolver`
functions, which return optimistic mutation results for given fields. These results are used by
_Graphcache_ to optimistically update the cache data, which provides an immediate and temporary
change to its data before a mutation completes.
```ts
interface OptimisticMutationConfig {
[mutationFieldName: string]: OptimisticMutationResolver;
}
```
A `OptimisticMutationResolver` receives three arguments when it's called: `variables`, `cache`, and
`info`.
| Argument | Type | Description |
| -------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `args` | `object` | The arguments that the given mutation field received. |
| `cache` | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) |
| `info` | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) |
[Read more about how to set up `optimistic` on the "Custom Updates"
page.](../graphcache/cache-updates.md)
### `schema` option
The `schema` option may be used to pass a `IntrospectionQuery` data to _Graphcache_, in other words
it's used to provide schema information to it. This schema is then used to resolve and return
partial results when querying, which are results that the cache can partially resolve as long as no
required fields are missing.
[Read more about how to use the `schema` option on the "Schema Awareness"
page.](../graphcache/schema-awareness.md)
### `storage` option
The `storage` option is an interface of methods that are used by the `offlineExchange` to persist
the cache's data to persisted storage on the user's device. it
> **NOTE:** Offline Support is currently experimental! It hasn't been extensively tested yet and
> may not always behave as expected. Please try it out with caution!
| Method | Type | Description |
| ----------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `writeData` | `(delta: SerializedEntries) => Promise<void>` | This provided method must be able to accept an object of key-value entries that will be persisted to the storage. This method is called as a batch of updated entries becomes ready. |
| `readData` | `() => Promise<SerializedEntries>` | This provided method must be able to return a single combined object of previous key-value entries that have been previously preserved using `writeData`. It's only called on startup. |
| `writeMetadata` | `(json: SerializedRequest[]) => void` | This provided method must be able to persist metadata for the cache. For backwards compatibility it should be able to accept any JSON data. |
| `readMetadata` | `() => Promise<null \| SerializedRequest[]>` | This provided method must be able to read the persisted metadata that has previously been written using `writeMetadata`. It's only called on startup. |
| `onOnline` | `(cb: () => void) => void` | This method must be able to accept a callback that is called when the user's device comes back online. |
| `onCacheHydrated` | `() => void` | This method will be called when the `cacheExchange` has finished hydrating the data coming from storage. |
These options are split into three parts:
- The `writeMetadata` and `readMetadata` methods are used to persist in-progress optimistic
mutations to a storage so that they may be retried if the app has been closed while some
optimistic mutations were still in progress.
- The `writeData` and `readData` methods are used to persist any cache data. This is the normalized
data that _Graphcache_ usually keeps in memory. The `cacheExchange` will frequently call
`writeData` with a partial object of its cache data, which `readData` must then be able to return
in a single combined object on startup. We call the partial objects that `writeData` is called
with "deltas".
- The `onOnline` method is only used to receive a trigger that determines whether the user's device
has come back online, which is used to retry optimistic mutations that have previously failed due
to being offline.
The `storage` option may also be used with the `cacheExchange` instead of the `offlineExchange`, but
will then only use `readData` and `writeData` to persist its cache data. This is not full offline
support, but will rather be "persistence support".
[Read more about how to use the `storage` option on the "Offline Support"
page.](../graphcache/offline.md)
## Cache
An instance of the `Cache` interface is passed to every resolvers and updater function. It may be
used to read cached data or write cached data, which may be used in combination with the
[`cacheExchange` configuration](#cacheexchange) to alter the default behaviour of _Graphcache_.
### keyOfEntity
The `cache.keyOfEntity` method may be called with a partial `Data` object and will return the key
for that object, or `null` if it's not keyable.
An object may not be keyable if it's missing the `__typename` or `id` (which falls back to `_id`)
fields. This method does take the [`keys` configuration](#keys-option) into account.
```js
cache.keyOfEntity({ __typename: 'Todo', id: 1 }); // 'Todo:1'
cache.keyOfEntity({ __typename: 'Query' }); // 'Query'
cache.keyOfEntity({ __typename: 'Unknown' }); // null
```
There's an alternative method, `cache.keyOfField` which generates a key for a given field. This is
only rarely needed but similar to `cache.keyOfEntity`. This method accepts a field name and
optionally a field's arguments.
```js
cache.keyOfField('todo'); // 'todo'
cache.keyOfField('todo', { id: 1 }); // 'todo({"id":1})'
```
Internally, these are the keys that records and links are stored on per entity.
### resolve
This method retrieves a value or link for a given field, given a partially keyable `Data` object or
entity, a field name, and optionally the field's arguments. Internally this method accesses the
cache by using `cache.keyOfEntity` and `cache.keyOfField`.
```js
// This may resolve a link:
cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }); // 'Todo:1'
// This may also resolve records / scalar values:
cache.resolve({ __typename: 'Todo', id: 1 }, 'id'); // 1
// You can also chain multiple calls to `cache.resolve`!
cache.resolve(cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }), 'id'); // 1
```
As you can see in the last example of this code snippet, the `Data` object can also be replaced by
an entity key, which makes it possible to pass a key from `cache.keyOfEntity` or another call to
`cache.resolve` instead of the partial entity.
> **Note:** Because `cache.resolve` may return either a scalar value or another entity key, it may
> be dangerous to use in some cases. It's a good idea to make sure first whether the field you're
> reading will be a key or a value.
The `cache.resolve` method may also be called with a field key as generated by `cache.keyOfField`.
```js
cache.resolve({ __typename: 'Query' }, cache.keyOfField('todo', { id: 1 })); // 'Todo:1'
```
This specialized case is likely only going to be useful in combination with
[`cache.inspectFields`](#inspectfields).
### inspectFields
The `cache.inspectFields` method may be used to interrogate the cache about all available fields on
a specific entity. It accepts a partial entity or an entity key, like [`cache.resolve`](#resolve)'s
first argument.
When calling the method this returns an array of `FieldInfo` objects, one per field (including
differing arguments) that is known to the cache. The `FieldInfo` interface has three properties:
`fieldKey`, `fieldName`, and `arguments`:
| Argument | Type | Description |
| ----------- | ---------------- | ------------------------------------------------------------------------------- |
| `fieldName` | `string` | The field's name (without any arguments, just the name) |
| `arguments` | `object \| null` | The field's arguments, or `null` if the field doesn't have any arguments |
| `fieldKey` | `string` | The field's cache key, which is similar to what `cache.keyOfField` would return |
This works on any given entity. When calling this method the cache works in reverse on its data
structure, by parsing the entity's individual field keys.
p
```js
cache.inspectFields({ __typename: 'Query' });
/*
[
{ fieldName: 'todo', arguments: { id: 1 }, fieldKey: 'id({"id":1})' },
{ fieldName: 'todo', arguments: { id: 2 }, fieldKey: 'id({"id":2})' },
...
]
*/
```
### readFragment
`cache.readFragment` accepts a GraphQL `DocumentNode` as the first argument and a partial entity or
an entity key as the second, like [`cache.resolve`](#resolve)'s first argument.
The method will then attempt to read the entity according to the fragment entirely from the cached
data. If any data is uncached and missing it'll return `null`.
```js
import { gql } from '@urql/core';
cache.readFragment(
gql`
fragment _ on Todo {
id
text
}
`,
{ id: 1 }
); // Data or null
```
Note that the `__typename` may be left out on the partial entity if the fragment isn't on an
interface or union type, since in that case the `__typename` is already present on the fragment
itself.
If any fields on the fragment require variables, you can pass them as the third argument like so:
```js
import { gql } from '@urql/core';
cache.readFragment(
gql`
fragment _ on User {
id
permissions(byGroupId: $groupId)
}
`,
{ id: 1 }, // this identifies the fragment (User) entity
{ groupId: 5 } // any additional field variables
);
```
If you need a specific fragment in a document containing multiple you can leverage
the fourth argument like this:
```js
import { gql } from '@urql/core';
cache.readFragment(
gql`
fragment todoFields on Todo {
id
}
fragment userFields on User {
id
}
`,
{ id: 1 }, // this identifies the fragment (User) entity
undefined,
'userFields' // if not passed we take the first fragment, in this case todoFields
);
```
[Read more about using `readFragment` on the ["Local Resolvers"
page.](../graphcache/local-resolvers.md#reading-a-fragment)
### readQuery
The `cache.readQuery` method is similar to `cache.readFragment`, but instead of reading a fragment
from cache, it reads an entire query. The only difference between how these two methods are used is
`cache.readQuery`'s input, which is an object instead of two arguments.
The method accepts a `{ query, variables }` object as the first argument, where `query` may either
be a `DocumentNode` or a `string` and variables may optionally be an object.
```js
cache.readQuery({
query: `
query ($id: ID!) {
todo(id: $id) { id, text }
}
`,
variables: {
id: 1
}
); // Data or null
```
[Read more about using `readQuery` on the ["Local Resolvers"
page.](../graphcache/local-resolvers.md#reading-a-query)
### link
Corresponding to [`cache.resolve`](#resolve), the `cache.link` method allows
links in the cache to be updated. While the `cache.resolve` method reads both
records and links from the cache, the `cache.link` method will only ever write
links as fragments (See [`cache.writeFragment`](#writefragment) below) are more
suitable for updating scalar data in the cache.
The arguments for `cache.link` are identical to [`cache.resolve`](#resolve) and
the field's arguments are optional. However, the last argument must always be
a link, meaning `null`, an entity key, a keyable entity, or a list of these.
In other words, `cache.link` accepts an entity to write to as its first argument,
with the same arguments as `cache.keyOfEntity`. It then accepts one or two arguments
that are passed to `cache.keyOfField` to get the targeted field key. And lastly,
you may pass a list or a single entity (or an entity key).
```js
// Link Query.todo field to a todo item
cache.link({ __typename: 'Query' }, 'todo', { __typename: 'Todo', id: 1 });
// You may also pass arguments instead:
cache.link({ __typename: 'Query' }, 'todo', { id: 1 }, { __typename: 'Todo', id: 1 });
// Or use entity keys instead of the entities themselves:
cache.link('Query', 'todo', cache.keyOfEntity({ __typename: 'Todo', id: 1 }));
```
The method may [output a
warning](../graphcache/errors.md#12-cant-generate-a-key-for-writefragment-or-link) when any of the
entities were passed as objects but aren't keyable, which is useful when a scalar or a non-keyable
object have been passed to `cache.link` accidentally.
### writeFragment
Corresponding to [`cache.readFragment`](#readfragments), the `cache.writeFragment` method allows
data in the cache to be updated.
The arguments for `cache.writeFragment` are identical to [`cache.readFragment`](#readfragment),
however the second argument, `data`, should not only contain properties that are necessary to derive
an entity key from the given data, but also the fields that will be written:
```js
import { gql } from '@urql/core';
cache.writeFragment(
gql`
fragment _ on Todo {
text
}
`,
{ id: 1, text: 'New Todo Text' }
);
```
In the example we can see that the `writeFragment` method returns `undefined`. Furthermore we pass
`id` in our `data` object so that an entity key can be written, but the fragment itself doesn't have
to include these fields.
If you need a specific fragment in a document containing multiple you can leverage
the fourth argument like this:
```js
import { gql } from '@urql/core';
cache.writeFragment(
gql`
fragment todoFields on Todo {
id
text
}
fragment userFields on User {
id
name
}
`,
{ id: 1, name: 'New Name' }
undefined,
'userFields' // if not passed we take the first fragment, in this case todoFields
);
```
[Read more about using `writeFragment` on the ["Custom Updates"
page.](../graphcache/cache-updates.md#cachewritefragment)
### updateQuery
Similarly to [`cache.writeFragment`](#writefragment), there's an analogous method for
[`cache.readQuery`](#readquery) that may be used to update query data.
The `cache.updateQuery` method accepts the same `{ query, variables }` object input as its first
argument, which is the query we'd like to write to the cache. As a second argument the method
accepts an updater function. This function will be called with the query data that is already in the
cache (which may be `null` if the data is uncached) and must return the new data that should be
written to the cache.
```js
const TodoQuery = `
query ($id: ID!) {
todo(id: $id) { id, text }
}
`;
cache.updateQuery({ query: TodoQuery, variables: { id: 1 } }, data => {
if (!data) return null;
data.todo.text = 'New Todo Text';
return data;
});
```
As we can see, our updater may return `null` to cancel updating any data, which we do in case the
query data is uncached.
We can also see that data can simply be mutated and doesn't have to be altered immutably. This is
because all data from the cache is already a deep copy and hence we can do to it whatever we want.
[Read more about using `updateQuery` on the "Custom Updates"
page.](../graphcache/cache-updates.md#cacheupdatequery)
### invalidate
The `cache.invalidate` method can be used to delete (i.e. "evict") an entity from the cache
entirely. This will cause it to disappear from all queries in _Graphcache_.
Its arguments are identical to [`cache.resolve`](#resolve).
Since deleting an entity will lead to some queries containing missing and uncached data, calling
`invalidate` may lead to additional GraphQL requests being sent, unless you're using [_Graphcache_'s
"Schema Awareness" feature](../graphcache/schema-awareness.md), which takes optional fields into
account.
This method accepts a partial entity or an entity key as its first argument, similar to
[`cache.resolve`](#resolve)'s first argument.
```js
cache.invalidate({ __typename: 'Todo', id: 1 }); // Invalidates Todo:1
```
Additionally `cache.invalidate` may be used to delete specific fields only, which can be useful when
for instance a list is supposed to be evicted from cache, where a full invalidation may be
impossible. This is often the case when a field on the root `Query` needs to be deleted.
This method therefore accepts two additional arguments, similar to [`cache.resolve`](#resolve).
```js
// Invalidates `Query.todos` with the `first: 10` argument:
cache.invalidate('Query', 'todos', { first: 10 });
```
## Info
This is a metadata object that is passed to every resolver and updater function. It contains basic
information about the current GraphQL document and query, and also some information on the current
field that a given resolver or updater is called on.
| Argument | Type | Description |
| ---------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `parent` | `Data` | The field's parent entity's data, as it was written or read up until now, which means it may be incomplete. [Use `cache.resolve`](#resolve) to read from it. |
| `parentTypeName` | `string` | The field's parent entity's typename |
| `parentKey` | `string` | The field's parent entity's cache key (if any) |
| `parentFieldKey` | `string` | The current key's cache key, which is the parent entity's key combined with the current field's key (This is mostly obsolete) |
| `fieldName` | `string` | The current field's name |
| `fragments` | `{ [name: string]: FragmentDefinitionNode }` | A dictionary of fragments from the current GraphQL document |
| `variables` | `object` | The current GraphQL operation's variables (may be an empty object) |
| `error` | `GraphQLError \| undefined` | The current GraphQLError for a given field. This will always be `undefined` for resolvers and optimistic updaters, but may be present for updaters when the API has returned an error for a given field. |
| `partial` | `?boolean` | This may be set to `true` at any point in time (by your custom resolver or by _Graphcache_) to indicate that some data is uncached and missing |
| `optimistic` | `?boolean` | This is only `true` when an optimistic mutation update is running |
> **Note:** Using `info` is regarded as a last resort. Please only use information from it if
> there's no other solution to get to the metadata you need. We don't regard the `Info` API as
> stable and may change it with a simple minor version bump.
## The `/extras` import
The `extras` subpackage is published with _Graphcache_ and contains helpers and utilities that don't
have to be included in every app or aren't needed by all users of _Graphcache_.
All utilities from extras may be imported from `@urql/exchange-graphcache/extras`.
Currently the `extras` subpackage only contains the [pagination resolvers that have been me
gitextract_87_w7_87/ ├── .changeset/ │ ├── README.md │ ├── config.json │ ├── late-boats-listen.md │ └── shiny-pets-give.md ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ ├── RFC.md │ │ ├── bug_report.yaml │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── actions/ │ │ ├── discord-message/ │ │ │ ├── action.mjs │ │ │ └── action.yml │ │ └── pnpm-run/ │ │ ├── action.mjs │ │ └── action.yml │ └── workflows/ │ ├── ci.yml │ ├── mirror.yml │ └── release.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs/ │ ├── README.md │ ├── advanced/ │ │ ├── README.md │ │ ├── authentication.md │ │ ├── authoring-exchanges.md │ │ ├── auto-populate-mutations.md │ │ ├── debugging.md │ │ ├── persistence-and-uploads.md │ │ ├── retry-operations.md │ │ ├── server-side-rendering.md │ │ ├── subscriptions.md │ │ └── testing.md │ ├── api/ │ │ ├── README.md │ │ ├── auth-exchange.md │ │ ├── core.md │ │ ├── execute-exchange.md │ │ ├── graphcache.md │ │ ├── preact.md │ │ ├── refocus-exchange.md │ │ ├── request-policy-exchange.md │ │ ├── retry-exchange.md │ │ ├── svelte.md │ │ ├── urql.md │ │ └── vue.md │ ├── architecture.md │ ├── basics/ │ │ ├── README.md │ │ ├── core.md │ │ ├── document-caching.md │ │ ├── errors.md │ │ ├── react-preact.md │ │ ├── solid-start.md │ │ ├── solid.md │ │ ├── svelte.md │ │ ├── typescript-integration.md │ │ ├── ui-patterns.md │ │ └── vue.md │ ├── comparison.md │ ├── graphcache/ │ │ ├── README.md │ │ ├── cache-updates.md │ │ ├── errors.md │ │ ├── local-directives.md │ │ ├── local-resolvers.md │ │ ├── normalized-caching.md │ │ ├── offline.md │ │ └── schema-awareness.md │ └── showcase.md ├── examples/ │ ├── README.md │ ├── pnpm-workspace.yaml │ ├── with-apq/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── LocationsList.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-defer-stream-directives/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── server/ │ │ │ ├── apollo-server.js │ │ │ ├── graphql-yoga.js │ │ │ └── schema.js │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── Songs.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-graphcache-pagination/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── PaginatedNpmSearch.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-graphcache-updates/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── client.js │ │ │ ├── index.jsx │ │ │ └── pages/ │ │ │ ├── Links.jsx │ │ │ └── LoginForm.jsx │ │ └── vite.config.js │ ├── with-infinite-pagination/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── SearchResults.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-multipart/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── FileUpload.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-next/ │ │ ├── README.md │ │ ├── app/ │ │ │ ├── layout.tsx │ │ │ ├── non-rsc/ │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── next-env.d.ts │ │ ├── package.json │ │ └── tsconfig.json │ ├── with-pagination/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── PaginatedNpmSearch.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-react/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── PokemonList.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-react-native/ │ │ ├── App.js │ │ ├── README.md │ │ ├── app.json │ │ ├── index.js │ │ ├── package.json │ │ └── src/ │ │ └── screens/ │ │ └── PokemonList.js │ ├── with-refresh-auth/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── authStore.js │ │ │ ├── client.js │ │ │ ├── index.jsx │ │ │ └── pages/ │ │ │ ├── LoginForm.jsx │ │ │ └── Profile.jsx │ │ └── vite.config.js │ ├── with-retry/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── Color.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-solid/ │ │ ├── .eslintrc.js │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── PokemonList.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-solid-start/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── app.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── app.tsx │ │ │ ├── entry-client.tsx │ │ │ ├── entry-server.tsx │ │ │ └── routes/ │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── with-subscriptions-via-fetch/ │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── server/ │ │ │ ├── graphql-yoga.js │ │ │ └── schema.js │ │ ├── src/ │ │ │ ├── App.jsx │ │ │ ├── Songs.jsx │ │ │ └── index.jsx │ │ └── vite.config.js │ ├── with-svelte/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.svelte │ │ │ ├── PokemonList.svelte │ │ │ └── main.js │ │ └── vite.config.mjs │ └── with-vue3/ │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.vue │ │ ├── PokemonList.vue │ │ └── main.js │ └── vite.config.js ├── exchanges/ │ ├── auth/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── authExchange.test.ts │ │ │ ├── authExchange.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── context/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── context.test.ts │ │ │ ├── context.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── execute/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── execute.test.ts │ │ │ ├── execute.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── graphcache/ │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── benchmarks/ │ │ │ ├── 10000Reads.html │ │ │ ├── 10000ReadsComplex.html │ │ │ ├── 10000Writes.html │ │ │ ├── 10000WritesComplex.html │ │ │ ├── 1000Reads.html │ │ │ ├── 1000ReadsComplex.html │ │ │ ├── 1000Writes.html │ │ │ ├── 1000WritesComplex.html │ │ │ ├── 100Reads.html │ │ │ ├── 100ReadsComplex.html │ │ │ ├── 100Writes.html │ │ │ ├── 100WritesComplex.html │ │ │ ├── 50000Reads.html │ │ │ ├── 50000Writes.html │ │ │ ├── 5000Reads.html │ │ │ ├── 5000Writes.html │ │ │ ├── 500Reads.html │ │ │ ├── 500Writes.html │ │ │ ├── addTodo.html │ │ │ ├── benchmarks.js │ │ │ ├── entities.js │ │ │ ├── makeEntries.js │ │ │ ├── operations.js │ │ │ ├── package.json │ │ │ ├── readMe.md │ │ │ ├── updateTodo.html │ │ │ └── urqlClient.js │ │ ├── cypress/ │ │ │ ├── fixtures/ │ │ │ │ └── example.json │ │ │ ├── plugins/ │ │ │ │ └── index.js │ │ │ └── support/ │ │ │ ├── component-index.html │ │ │ └── component.js │ │ ├── cypress.config.js │ │ ├── e2e-tests/ │ │ │ ├── query.spec.tsx │ │ │ └── updates.spec.tsx │ │ ├── help.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ast/ │ │ │ │ ├── graphql.ts │ │ │ │ ├── index.ts │ │ │ │ ├── node.ts │ │ │ │ ├── schema.ts │ │ │ │ ├── schemaPredicates.test.ts │ │ │ │ ├── schemaPredicates.ts │ │ │ │ ├── traversal.test.ts │ │ │ │ ├── traversal.ts │ │ │ │ ├── variables.test.ts │ │ │ │ └── variables.ts │ │ │ ├── cacheExchange-types.test.ts │ │ │ ├── cacheExchange.test.ts │ │ │ ├── cacheExchange.ts │ │ │ ├── default-storage/ │ │ │ │ └── index.ts │ │ │ ├── extras/ │ │ │ │ ├── index.ts │ │ │ │ ├── relayPagination.test.ts │ │ │ │ ├── relayPagination.ts │ │ │ │ ├── simplePagination.test.ts │ │ │ │ └── simplePagination.ts │ │ │ ├── helpers/ │ │ │ │ ├── help.ts │ │ │ │ └── operation.ts │ │ │ ├── index.ts │ │ │ ├── offlineExchange.test.ts │ │ │ ├── offlineExchange.ts │ │ │ ├── operations/ │ │ │ │ ├── invalidate.ts │ │ │ │ ├── query.test.ts │ │ │ │ ├── query.ts │ │ │ │ ├── shared.test.ts │ │ │ │ ├── shared.ts │ │ │ │ ├── write.test.ts │ │ │ │ └── write.ts │ │ │ ├── store/ │ │ │ │ ├── __snapshots__/ │ │ │ │ │ └── store.test.ts.snap │ │ │ │ ├── data.test.ts │ │ │ │ ├── data.ts │ │ │ │ ├── keys.ts │ │ │ │ ├── store.test.ts │ │ │ │ └── store.ts │ │ │ ├── test-utils/ │ │ │ │ ├── altered_root_schema.json │ │ │ │ ├── examples-1.test.ts │ │ │ │ ├── examples-2.test.ts │ │ │ │ ├── examples-3.test.ts │ │ │ │ ├── relayPagination_schema.json │ │ │ │ ├── simple_schema.json │ │ │ │ ├── suite.test.ts │ │ │ │ └── utils.ts │ │ │ └── types.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── persisted/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── persistedExchange.test.ts │ │ │ ├── persistedExchange.ts │ │ │ ├── sha256.ts │ │ │ └── test-utils.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── populate/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helpers/ │ │ │ │ ├── help.ts │ │ │ │ ├── node.ts │ │ │ │ └── traverse.ts │ │ │ ├── index.ts │ │ │ ├── populateExchange.test.ts │ │ │ └── populateExchange.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── refocus/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── refocusExchange.test.ts │ │ │ └── refocusExchange.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── request-policy/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── requestPolicyExchange.test.ts │ │ │ └── requestPolicyExchange.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── retry/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── retryExchange.test.ts │ │ │ └── retryExchange.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ └── throw-on-error/ │ ├── CHANGELOG.md │ ├── README.md │ ├── jsr.json │ ├── package.json │ ├── src/ │ │ ├── index.ts │ │ ├── throwOnErrorExchange.test.ts │ │ └── throwOnErrorExchange.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── package.json ├── packages/ │ ├── core/ │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __snapshots__/ │ │ │ │ └── client.test.ts.snap │ │ │ ├── client.test.ts │ │ │ ├── client.ts │ │ │ ├── exchanges/ │ │ │ │ ├── __snapshots__/ │ │ │ │ │ ├── fetch.test.ts.snap │ │ │ │ │ └── subscription.test.ts.snap │ │ │ │ ├── cache.test.ts │ │ │ │ ├── cache.ts │ │ │ │ ├── compose.test.ts │ │ │ │ ├── compose.ts │ │ │ │ ├── debug.test.ts │ │ │ │ ├── debug.ts │ │ │ │ ├── fallback.test.ts │ │ │ │ ├── fallback.ts │ │ │ │ ├── fetch.test.ts │ │ │ │ ├── fetch.ts │ │ │ │ ├── index.ts │ │ │ │ ├── map.test.ts │ │ │ │ ├── map.ts │ │ │ │ ├── ssr.test.ts │ │ │ │ ├── ssr.ts │ │ │ │ ├── subscription.test.ts │ │ │ │ └── subscription.ts │ │ │ ├── gql.test.ts │ │ │ ├── gql.ts │ │ │ ├── index.ts │ │ │ ├── internal/ │ │ │ │ ├── __snapshots__/ │ │ │ │ │ └── fetchSource.test.ts.snap │ │ │ │ ├── fetchOptions.test.ts │ │ │ │ ├── fetchOptions.ts │ │ │ │ ├── fetchSource.test.ts │ │ │ │ ├── fetchSource.ts │ │ │ │ └── index.ts │ │ │ ├── test-utils/ │ │ │ │ ├── index.ts │ │ │ │ └── samples.ts │ │ │ ├── types.ts │ │ │ └── utils/ │ │ │ ├── __snapshots__/ │ │ │ │ └── error.test.ts.snap │ │ │ ├── collectTypenames.test.ts │ │ │ ├── collectTypenames.ts │ │ │ ├── error.test.ts │ │ │ ├── error.ts │ │ │ ├── formatDocument.test.ts │ │ │ ├── formatDocument.ts │ │ │ ├── graphql.ts │ │ │ ├── hash.test.ts │ │ │ ├── hash.ts │ │ │ ├── index.ts │ │ │ ├── operation.ts │ │ │ ├── request.test.ts │ │ │ ├── request.ts │ │ │ ├── result.test.ts │ │ │ ├── result.ts │ │ │ ├── streamUtils.ts │ │ │ ├── variables.test.ts │ │ │ └── variables.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── introspection/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── getIntrospectedSchema.ts │ │ │ ├── index.ts │ │ │ └── minifyIntrospectionQuery.ts │ │ └── tsconfig.json │ ├── next-urql/ │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── DataHydrationContext.ts │ │ │ ├── Provider.ts │ │ │ ├── htmlescape.ts │ │ │ ├── index.ts │ │ │ ├── rsc.ts │ │ │ ├── useQuery.ts │ │ │ └── useUrqlValue.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── preact-urql/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Mutation.test.tsx │ │ │ │ ├── Mutation.ts │ │ │ │ ├── Query.test.tsx │ │ │ │ ├── Query.ts │ │ │ │ ├── Subscription.test.tsx │ │ │ │ ├── Subscription.ts │ │ │ │ └── index.ts │ │ │ ├── context.ts │ │ │ ├── hooks/ │ │ │ │ ├── constants.ts │ │ │ │ ├── index.ts │ │ │ │ ├── useMutation.test.tsx │ │ │ │ ├── useMutation.ts │ │ │ │ ├── useQuery.test.tsx │ │ │ │ ├── useQuery.ts │ │ │ │ ├── useRequest.ts │ │ │ │ ├── useSource.ts │ │ │ │ ├── useSubscription.test.tsx │ │ │ │ └── useSubscription.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── react-urql/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── core/ │ │ │ ├── index.d.ts │ │ │ ├── index.esm.js │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── cypress/ │ │ │ ├── fixtures/ │ │ │ │ └── example.json │ │ │ └── support/ │ │ │ ├── component-index.html │ │ │ └── component.js │ │ ├── cypress.config.js │ │ ├── e2e-tests/ │ │ │ └── useQuery.spec.tsx │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Mutation.test.tsx │ │ │ │ ├── Mutation.ts │ │ │ │ ├── Query.test.tsx │ │ │ │ ├── Query.ts │ │ │ │ ├── Subscription.ts │ │ │ │ └── index.ts │ │ │ ├── context.ts │ │ │ ├── hooks/ │ │ │ │ ├── __snapshots__/ │ │ │ │ │ ├── useMutation.test.tsx.snap │ │ │ │ │ ├── useQuery.test.tsx.snap │ │ │ │ │ └── useSubscription.test.tsx.snap │ │ │ │ ├── cache.ts │ │ │ │ ├── index.ts │ │ │ │ ├── state.ts │ │ │ │ ├── useMutation.test.tsx │ │ │ │ ├── useMutation.ts │ │ │ │ ├── useQuery.spec.ts │ │ │ │ ├── useQuery.test.tsx │ │ │ │ ├── useQuery.ts │ │ │ │ ├── useRequest.test.ts │ │ │ │ ├── useRequest.ts │ │ │ │ ├── useSubscription.test.tsx │ │ │ │ └── useSubscription.ts │ │ │ ├── index.ts │ │ │ └── test-utils/ │ │ │ └── ssr.test.tsx │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── site/ │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── plugins/ │ │ │ ├── assets-fix/ │ │ │ │ └── node.api.js │ │ │ ├── monorepo-fix/ │ │ │ │ └── node.api.js │ │ │ ├── preact/ │ │ │ │ └── node.api.js │ │ │ └── react-router/ │ │ │ └── browser.api.js │ │ ├── public/ │ │ │ ├── browserconfig.xml │ │ │ └── site.webmanifest │ │ ├── src/ │ │ │ ├── analytics.js │ │ │ ├── app.js │ │ │ ├── assets/ │ │ │ │ ├── anchor.js │ │ │ │ └── chevron.js │ │ │ ├── components/ │ │ │ │ ├── body-copy.js │ │ │ │ ├── button.js │ │ │ │ ├── footer.js │ │ │ │ ├── header.js │ │ │ │ ├── link.js │ │ │ │ ├── loading.js │ │ │ │ ├── markdown.js │ │ │ │ ├── mdx.js │ │ │ │ ├── navigation.js │ │ │ │ ├── panel.js │ │ │ │ ├── scroll-to-top.js │ │ │ │ ├── secondary-title.js │ │ │ │ ├── section-title.js │ │ │ │ ├── sidebar-search-input.js │ │ │ │ ├── sidebar.js │ │ │ │ └── wrapper.js │ │ │ ├── constants.js │ │ │ ├── google-analytics.js │ │ │ ├── google-tag-manager.js │ │ │ ├── html.js │ │ │ ├── index.js │ │ │ ├── screens/ │ │ │ │ ├── 404/ │ │ │ │ │ ├── 404.js │ │ │ │ │ └── index.js │ │ │ │ ├── docs/ │ │ │ │ │ ├── article.js │ │ │ │ │ ├── header.js │ │ │ │ │ └── index.js │ │ │ │ └── home/ │ │ │ │ ├── _content.js │ │ │ │ ├── features.js │ │ │ │ ├── get-started.js │ │ │ │ ├── hero.js │ │ │ │ ├── index.js │ │ │ │ └── more-oss.js │ │ │ └── styles/ │ │ │ ├── global.js │ │ │ └── theme.js │ │ ├── static.config.js │ │ └── vercel.json │ ├── solid-start-urql/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── context.test.tsx │ │ │ ├── context.ts │ │ │ ├── createMutation.test.ts │ │ │ ├── createMutation.ts │ │ │ ├── createQuery.test.tsx │ │ │ ├── createQuery.ts │ │ │ ├── createSubscription.test.ts │ │ │ ├── createSubscription.ts │ │ │ ├── index.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── solid-urql/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── context.ts │ │ │ ├── createMutation.test.ts │ │ │ ├── createMutation.ts │ │ │ ├── createQuery.test.tsx │ │ │ ├── createQuery.ts │ │ │ ├── createSubscription.test.ts │ │ │ ├── createSubscription.ts │ │ │ ├── index.ts │ │ │ ├── suspense.test.tsx │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── storage-rn/ │ │ ├── CHANGELOG.md │ │ ├── LICENCE │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── makeAsyncStorage.test.ts │ │ │ └── makeAsyncStorage.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ ├── svelte-urql/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jsr.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── common.ts │ │ │ ├── context.ts │ │ │ ├── index.ts │ │ │ ├── mutationStore.test.ts │ │ │ ├── mutationStore.ts │ │ │ ├── queryStore.test.ts │ │ │ ├── queryStore.ts │ │ │ ├── subscriptionStore.test.ts │ │ │ └── subscriptionStore.ts │ │ ├── tsconfig.json │ │ └── vitest.config.ts │ └── vue-urql/ │ ├── CHANGELOG.md │ ├── README.md │ ├── jsr.json │ ├── package.json │ ├── src/ │ │ ├── index.ts │ │ ├── useClient.test.ts │ │ ├── useClient.ts │ │ ├── useClientHandle.ts │ │ ├── useMutation.test.ts │ │ ├── useMutation.ts │ │ ├── useQuery.test.ts │ │ ├── useQuery.ts │ │ ├── useSubscription.test.ts │ │ ├── useSubscription.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── pnpm-workspace.yaml ├── scripts/ │ ├── actions/ │ │ ├── build-all.mjs │ │ ├── lib/ │ │ │ ├── commands.mjs │ │ │ ├── constants.mjs │ │ │ ├── github.mjs │ │ │ └── packages.mjs │ │ └── pack-all.mjs │ ├── babel/ │ │ ├── transform-debug-target.mjs │ │ ├── transform-invariant-warning.mjs │ │ └── transform-pipe.mjs │ ├── changesets/ │ │ ├── changelog.js │ │ ├── jsr.mjs │ │ └── version.mjs │ ├── eslint/ │ │ └── preset.js │ ├── prepare/ │ │ ├── index.js │ │ └── postinstall.js │ ├── rollup/ │ │ ├── cleanup-plugin.mjs │ │ ├── config.mjs │ │ ├── plugins.mjs │ │ └── settings.mjs │ └── vitest/ │ └── setup.js ├── tsconfig.json ├── vercel.json └── vitest.config.ts
SYMBOL INDEX (524 symbols across 157 files)
FILE: .github/actions/discord-message/action.mjs
constant GITHUB_TOKEN (line 4) | const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
constant WEBHOOK_URL (line 5) | const WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL;
function getReleaseBody (line 30) | async function getReleaseBody(name, version) {
function main (line 45) | async function main() {
FILE: examples/with-apq/src/App.jsx
function App (line 17) | function App() {
FILE: examples/with-apq/src/LocationsList.jsx
constant LOCATIONS_QUERY (line 4) | const LOCATIONS_QUERY = gql`
FILE: examples/with-defer-stream-directives/src/App.jsx
function App (line 20) | function App() {
FILE: examples/with-defer-stream-directives/src/Songs.jsx
constant SONGS_QUERY (line 10) | const SONGS_QUERY = gql`
FILE: examples/with-graphcache-pagination/src/App.jsx
function App (line 22) | function App() {
FILE: examples/with-graphcache-pagination/src/PaginatedNpmSearch.jsx
constant NPM_SEARCH (line 7) | const NPM_SEARCH = gql`
FILE: examples/with-graphcache-updates/src/App.jsx
function App (line 25) | function App() {
FILE: examples/with-graphcache-updates/src/client.js
method createLink (line 8) | createLink(result, _args, cache, _info) {
method addAuthToOperation (line 51) | addAuthToOperation(operation) {
method didAuthError (line 59) | didAuthError(error) {
method willAuthError (line 64) | willAuthError(operation) {
method refreshAuth (line 83) | async refreshAuth() {
FILE: examples/with-graphcache-updates/src/pages/Links.jsx
constant LINKS_QUERY (line 4) | const LINKS_QUERY = gql`
constant CREATE_LINK_MUTATION (line 16) | const CREATE_LINK_MUTATION = gql`
FILE: examples/with-graphcache-updates/src/pages/LoginForm.jsx
constant LOGIN_MUTATION (line 4) | const LOGIN_MUTATION = gql`
constant REGISTER_MUTATION (line 13) | const REGISTER_MUTATION = gql`
FILE: examples/with-infinite-pagination/src/App.jsx
function PaginatedNpmSearch (line 15) | function PaginatedNpmSearch() {
function App (line 44) | function App() {
FILE: examples/with-multipart/src/App.jsx
function App (line 11) | function App() {
FILE: examples/with-multipart/src/FileUpload.jsx
constant UPLOAD_FILE (line 4) | const UPLOAD_FILE = gql`
FILE: examples/with-next/app/layout.tsx
function RootLayout (line 6) | function RootLayout({
FILE: examples/with-next/app/non-rsc/layout.tsx
function Layout (line 12) | function Layout({ children }: React.PropsWithChildren) {
FILE: examples/with-next/app/non-rsc/page.tsx
function Page (line 7) | function Page() {
function Pokemons (line 26) | function Pokemons() {
function Pokemon (line 55) | function Pokemon(props: any) {
FILE: examples/with-next/app/page.tsx
function Home (line 25) | async function Home() {
FILE: examples/with-pagination/src/App.jsx
function App (line 11) | function App() {
FILE: examples/with-pagination/src/PaginatedNpmSearch.jsx
constant NPM_SEARCH (line 7) | const NPM_SEARCH = gql`
FILE: examples/with-react-native/src/screens/PokemonList.js
constant POKEMONS_QUERY (line 5) | const POKEMONS_QUERY = gql`
FILE: examples/with-react/src/App.jsx
function App (line 11) | function App() {
FILE: examples/with-react/src/PokemonList.jsx
constant POKEMONS_QUERY (line 4) | const POKEMONS_QUERY = gql`
FILE: examples/with-refresh-auth/src/App.jsx
function App (line 31) | function App() {
FILE: examples/with-refresh-auth/src/authStore.js
constant TOKEN_KEY (line 1) | const TOKEN_KEY = 'token';
constant REFRESH_TOKEN_KEY (line 2) | const REFRESH_TOKEN_KEY = 'refresh_token';
FILE: examples/with-refresh-auth/src/client.js
constant REFRESH_TOKEN_MUTATION (line 11) | const REFRESH_TOKEN_MUTATION = gql`
method addAuthToOperation (line 25) | addAuthToOperation(operation) {
method didAuthError (line 32) | didAuthError(error) {
method willAuthError (line 37) | willAuthError(operation) {
method refreshAuth (line 60) | async refreshAuth() {
FILE: examples/with-refresh-auth/src/pages/LoginForm.jsx
constant LOGIN_MUTATION (line 4) | const LOGIN_MUTATION = gql`
constant REGISTER_MUTATION (line 13) | const REGISTER_MUTATION = gql`
FILE: examples/with-refresh-auth/src/pages/Profile.jsx
constant PROFILE_QUERY (line 4) | const PROFILE_QUERY = gql`
FILE: examples/with-retry/src/App.jsx
function App (line 25) | function App() {
FILE: examples/with-retry/src/Color.jsx
constant RANDOM_COLOR_QUERY (line 4) | const RANDOM_COLOR_QUERY = gql`
FILE: examples/with-solid-start/src/app.tsx
function App (line 16) | function App() {
FILE: examples/with-solid-start/src/routes/index.tsx
constant POKEMONS_QUERY (line 6) | const POKEMONS_QUERY = gql`
constant ADD_POKEMON_MUTATION (line 15) | const ADD_POKEMON_MUTATION = gql`
function Home (line 24) | function Home() {
FILE: examples/with-solid/src/App.jsx
function App (line 15) | function App() {
FILE: examples/with-solid/src/PokemonList.jsx
constant POKEMONS_QUERY (line 6) | const POKEMONS_QUERY = gql`
FILE: examples/with-subscriptions-via-fetch/server/schema.js
method resolve (line 23) | resolve() {
method resolve (line 34) | resolve(root) {
FILE: examples/with-subscriptions-via-fetch/src/App.jsx
method alphabet (line 14) | alphabet(parent, _args, cache) {
function App (line 29) | function App() {
FILE: examples/with-subscriptions-via-fetch/src/Songs.jsx
constant LIST_QUERY (line 4) | const LIST_QUERY = gql`
constant SONG_SUBSCRIPTION (line 12) | const SONG_SUBSCRIPTION = gql`
FILE: exchanges/auth/src/authExchange.test.ts
method addAuthToOperation (line 64) | addAuthToOperation(operation) {
method refreshAuth (line 70) | async refreshAuth() {
method addAuthToOperation (line 99) | addAuthToOperation(operation) {
method refreshAuth (line 105) | async refreshAuth() {
method addAuthToOperation (line 137) | addAuthToOperation(operation) {
method refreshAuth (line 144) | async refreshAuth() {
method addAuthToOperation (line 180) | addAuthToOperation(operation) {
method refreshAuth (line 186) | async refreshAuth() {
method addAuthToOperation (line 238) | addAuthToOperation(operation) {
method refreshAuth (line 244) | async refreshAuth() {
method addAuthToOperation (line 295) | addAuthToOperation(operation) {
method refreshAuth (line 302) | async refreshAuth() {
method addAuthToOperation (line 348) | addAuthToOperation(operation) {
method refreshAuth (line 353) | async refreshAuth() {
method addAuthToOperation (line 400) | addAuthToOperation(operation) {
method refreshAuth (line 406) | async refreshAuth() {
method addAuthToOperation (line 457) | addAuthToOperation(operation) {
method refreshAuth (line 464) | async refreshAuth() {
FILE: exchanges/auth/src/authExchange.ts
type AuthUtilities (line 26) | interface AuthUtilities {
type AuthConfig (line 68) | interface AuthConfig {
function authExchange (line 198) | function authExchange(
FILE: exchanges/context/src/context.ts
type ContextExchangeArgs (line 7) | interface ContextExchangeArgs {
FILE: exchanges/execute/src/execute.ts
type ExecuteExchangeArgs (line 23) | interface ExecuteExchangeArgs {
type ExecuteParams (line 40) | type ExecuteParams = ExecutionArgs | SubscriptionArgs;
function next (line 72) | function next({
FILE: exchanges/graphcache/benchmarks/operations.js
constant ALL_TODOS_QUERY (line 2) | const ALL_TODOS_QUERY = `
constant ALL_WRITERS_QUERY (line 11) | const ALL_WRITERS_QUERY = `
constant ALL_BOOKS_QUERY (line 23) | const ALL_BOOKS_QUERY = `
constant ALL_STORES_QUERY (line 34) | const ALL_STORES_QUERY = `
constant ALL_EMPLOYEES_QUERY (line 43) | const ALL_EMPLOYEES_QUERY = `
constant ALL_AUTHORS_QUERY (line 52) | const ALL_AUTHORS_QUERY = `
constant ADD_TODO_MUTATION (line 62) | const ADD_TODO_MUTATION = `
constant UPDATE_TODO_MUTATION (line 71) | const UPDATE_TODO_MUTATION = `
constant ADD_TODOS_MUTATION (line 80) | const ADD_TODOS_MUTATION = `
constant ADD_WRITERS_MUTATION (line 89) | const ADD_WRITERS_MUTATION = `
constant ADD_BOOKS_MUTATION (line 101) | const ADD_BOOKS_MUTATION = `
constant ADD_STORES_MUTATION (line 112) | const ADD_STORES_MUTATION = `
constant ADD_EMPLOYEES_MUTATION (line 121) | const ADD_EMPLOYEES_MUTATION = `
constant ADD_AUTHORS_MUTATION (line 130) | const ADD_AUTHORS_MUTATION = `
FILE: exchanges/graphcache/cypress.config.js
method setupNodeEvents (line 10) | setupNodeEvents(_on, _config) {
FILE: exchanges/graphcache/src/ast/graphql.ts
type OrNever (line 3) | type OrNever<T> = void extends T ? never : T;
type IntrospectionQuery (line 5) | type IntrospectionQuery =
type IntrospectionTypeRef (line 16) | type IntrospectionTypeRef =
type IntrospectionInputTypeRef (line 30) | type IntrospectionInputTypeRef =
type IntrospectionInputValue (line 38) | type IntrospectionInputValue =
type IntrospectionType (line 47) | type IntrospectionType =
FILE: exchanges/graphcache/src/ast/node.ts
type SelectionSet (line 13) | type SelectionSet = readonly FormattedNode<SelectionNode>[];
constant EMPTY_DIRECTIVES (line 15) | const EMPTY_DIRECTIVES: Record<string, DirectiveNode | undefined> = {};
FILE: exchanges/graphcache/src/ast/schema.ts
type SchemaField (line 8) | interface SchemaField {
type SchemaObject (line 14) | interface SchemaObject {
type SchemaUnion (line 21) | interface SchemaUnion {
type SchemaIntrospector (line 27) | interface SchemaIntrospector {
type PartialIntrospectionSchema (line 35) | interface PartialIntrospectionSchema {
type IntrospectionData (line 42) | type IntrospectionData =
method isSubType (line 98) | isSubType(abstract: string, possible: string) {
FILE: exchanges/graphcache/src/ast/schemaPredicates.ts
constant BUILTIN_NAME (line 18) | const BUILTIN_NAME = '__';
function expectObjectType (line 106) | function expectObjectType(schema: SchemaIntrospector, typename: string) {
function expectAbstractType (line 118) | function expectAbstractType(schema: SchemaIntrospector, typename: string) {
function expectValidKeyingConfig (line 131) | function expectValidKeyingConfig(
function expectValidUpdatesConfig (line 151) | function expectValidUpdatesConfig(
function warnAboutResolver (line 209) | function warnAboutResolver(name: string, logger: Logger | undefined): vo...
function warnAboutAbstractResolver (line 217) | function warnAboutAbstractResolver(
function expectValidResolversConfig (line 231) | function expectValidResolversConfig(
function expectValidOptimisticMutationsConfig (line 280) | function expectValidOptimisticMutationsConfig(
FILE: exchanges/graphcache/src/ast/traversal.ts
function getMainOperation (line 21) | function getMainOperation(doc: DocumentNode): OperationDefinitionNode {
FILE: exchanges/graphcache/src/cacheExchange-types.test.ts
type Maybe (line 9) | type Maybe<T> = T | null;
type Scalars (line 11) | type Scalars = {
type Author (line 18) | type Author = {
type MutationToggleTodoArgs (line 26) | type MutationToggleTodoArgs = {
type Query (line 30) | type Query = {
type Todo (line 35) | type Todo = {
type WithTypename (line 43) | type WithTypename<T extends { __typename?: any }> = {
type GraphCacheKeysConfig (line 47) | type GraphCacheKeysConfig = {
type GraphCacheResolvers (line 51) | type GraphCacheResolvers = {
type GraphCacheOptimisticUpdaters (line 83) | type GraphCacheOptimisticUpdaters = {
type GraphCacheUpdaters (line 90) | type GraphCacheUpdaters = {
type GraphCacheConfig (line 100) | type GraphCacheConfig = {
FILE: exchanges/graphcache/src/cacheExchange.test.ts
method logger (line 149) | logger(severity, message) {
method name (line 1667) | name() {
FILE: exchanges/graphcache/src/cacheExchange.ts
type OperationResultWithMeta (line 38) | interface OperationResultWithMeta extends Partial<OperationResult> {
type Operations (line 45) | type Operations = Set<number>;
type OperationMap (line 46) | type OperationMap = Map<number, Operation>;
type ResultMap (line 47) | type ResultMap = Map<number, Data | null>;
type OptimisticDependencies (line 48) | type OptimisticDependencies = Map<number, Dependencies>;
type DependentOperations (line 49) | type DependentOperations = Map<string, Operations>;
FILE: exchanges/graphcache/src/default-storage/index.ts
type StorageOptions (line 29) | interface StorageOptions {
type DefaultStorage (line 43) | interface DefaultStorage extends StorageAdapter {
method clear (line 82) | clear() {
method readMetadata (line 95) | readMetadata(): Promise<null | SerializedRequest[]> {
method writeMetadata (line 109) | writeMetadata(metadata: SerializedRequest[]) {
method writeData (line 125) | writeData(entries: SerializedEntries): Promise<void> {
method readData (line 141) | readData(): Promise<SerializedEntries> {
method onOnline (line 179) | onOnline(cb: () => void) {
FILE: exchanges/graphcache/src/extras/relayPagination.test.ts
function itemNode (line 8) | function itemNode(numItem: number) {
function itemEdge (line 15) | function itemEdge(numItem: number) {
FILE: exchanges/graphcache/src/extras/relayPagination.ts
type MergeMode (line 4) | type MergeMode = 'outwards' | 'inwards';
type PaginationParams (line 7) | interface PaginationParams {
type PageInfo (line 20) | interface PageInfo {
type Page (line 28) | interface Page {
FILE: exchanges/graphcache/src/extras/simplePagination.ts
type MergeMode (line 4) | type MergeMode = 'before' | 'after';
type PaginationParams (line 7) | interface PaginationParams {
FILE: exchanges/graphcache/src/helpers/help.ts
type ErrorCode (line 13) | type ErrorCode =
type DebugNode (line 43) | type DebugNode = ExecutableDefinitionNode | InlineFragmentNode;
function invariant (line 76) | function invariant(
function warn (line 93) | function warn(
FILE: exchanges/graphcache/src/offlineExchange.ts
type OfflineExchangeOpts (line 33) | interface OfflineExchangeOpts extends CacheExchangeOpts {
method readData (line 174) | readData() {
FILE: exchanges/graphcache/src/operations/invalidate.ts
type PartialFieldInfo (line 5) | interface PartialFieldInfo {
FILE: exchanges/graphcache/src/operations/query.test.ts
constant TODO_QUERY (line 11) | const TODO_QUERY = gql`
FILE: exchanges/graphcache/src/operations/query.ts
type QueryResult (line 55) | interface QueryResult {
function getFieldResolver (line 306) | function getFieldResolver(
FILE: exchanges/graphcache/src/operations/shared.ts
type Context (line 44) | interface Context {
class SelectionIterator (line 164) | class SelectionIterator {
method constructor (line 194) | constructor(
method next (line 215) | next(): FormattedNode<FieldNode> | undefined {
FILE: exchanges/graphcache/src/operations/write.test.ts
constant TODO_QUERY (line 11) | const TODO_QUERY = gql`
FILE: exchanges/graphcache/src/operations/write.ts
type WriteResult (line 51) | interface WriteResult {
constant KEYLESS_TYPE_RE (line 418) | const KEYLESS_TYPE_RE = /^__|PageInfo|(Connection|Edge)$/;
FILE: exchanges/graphcache/src/store/data.ts
type Dict (line 24) | type Dict<T> = Record<string, T>;
type KeyMap (line 25) | type KeyMap<T> = Map<string, T>;
type OperationMap (line 26) | type OperationMap<T> = Map<number, T>;
type NodeMap (line 28) | interface NodeMap<T> {
type InMemoryData (line 33) | interface InMemoryData {
function makeData (line 79) | function makeData(data?: DataField | void, isArray?: boolean) {
constant DEFAULT_EMPTY_SET (line 241) | const DEFAULT_EMPTY_SET = new Set<string>();
function getRefCount (line 344) | function getRefCount(entityKey: string): number {
function isEqualLinkOrScalar (line 730) | function isEqualLinkOrScalar(
FILE: exchanges/graphcache/src/store/store.ts
type DocumentNode (line 40) | type DocumentNode = TypedDocumentNode<any, any>;
type RootField (line 41) | type RootField = 'query' | 'mutation' | 'subscription';
class Store (line 46) | class Store<
method constructor (line 65) | constructor(opts?: C) {
method keyOfField (line 126) | keyOfField(fieldName: string, fieldArgs?: FieldArgs) {
method keyOfEntity (line 130) | keyOfEntity(data: Entity) {
method resolve (line 160) | resolve(
method invalidate (line 176) | invalidate(entity: Entity, field?: string, args?: FieldArgs) {
method inspectFields (line 202) | inspectFields(entity: Entity): FieldInfo[] {
method updateQuery (line 207) | updateQuery<T = Data, V = Variables>(
method readQuery (line 218) | readQuery<T = Data, V = Variables>(input: QueryInput<T, V>): T | null {
method readFragment (line 223) | readFragment<T = Data, V = Variables>(
method writeFragment (line 238) | writeFragment<T = Data, V = Variables>(
method link (line 262) | link(
FILE: exchanges/graphcache/src/test-utils/examples-1.test.ts
method text (line 416) | text(result, _, cache) {
FILE: exchanges/graphcache/src/test-utils/suite.test.ts
type TestCase (line 8) | interface TestCase {
FILE: exchanges/graphcache/src/types.ts
type NullArray (line 20) | type NullArray<T> = Array<null | T | NullArray<T>>;
type Fragments (line 29) | interface Fragments {
type Primitive (line 37) | type Primitive = null | number | boolean | string;
type ScalarObject (line 49) | interface ScalarObject {
type Scalar (line 58) | type Scalar = Primitive | ScalarObject;
type SystemFields (line 71) | interface SystemFields {
type EntityField (line 84) | type EntityField = undefined | Scalar | NullArray<Scalar>;
type DataField (line 93) | type DataField = Scalar | Data | NullArray<Scalar> | NullArray<Data>;
type DataFields (line 102) | interface DataFields {
type Variables (line 113) | interface Variables {
type Data (line 125) | type Data = SystemFields & DataFields;
type Entity (line 134) | type Entity = undefined | null | Data | string;
type Link (line 143) | type Link<Key = string> = null | Key | NullArray<Key>;
type FieldArgs (line 155) | type FieldArgs = Variables | null | undefined;
type FieldInfo (line 163) | interface FieldInfo {
type KeyInfo (line 175) | interface KeyInfo {
type OperationRequest (line 187) | interface OperationRequest {
type ResolveInfo (line 203) | interface ResolveInfo {
type QueryInput (line 267) | interface QueryInput<T = Data, V = Variables> {
type Cache (line 273) | interface Cache {
type ResolverResult (line 528) | type ResolverResult =
type Logger (line 534) | type Logger = (
type CacheExchangeOpts (line 540) | type CacheExchangeOpts = {
type Resolver (line 698) | type Resolver<
type ResolverConfig (line 722) | type ResolverConfig = {
type Directive (line 728) | type Directive = (
type DirectivesConfig (line 732) | type DirectivesConfig = {
type UpdateResolver (line 768) | type UpdateResolver<ParentData = DataFields, Args = Variables> = {
type KeyGenerator (line 807) | type KeyGenerator = {
type UpdatesConfig (line 834) | type UpdatesConfig = {
type MakeFunctional (line 850) | type MakeFunctional<T> = T extends { __typename: string }
type OptimisticMutationResolver (line 876) | type OptimisticMutationResolver<
type OptimisticMutationConfig (line 912) | type OptimisticMutationConfig = {
type KeyingConfig (line 941) | type KeyingConfig = {
type PossibleTypesConfig (line 945) | type PossibleTypesConfig = {
type SerializedEntries (line 950) | interface SerializedEntries {
type SerializedRequest (line 955) | interface SerializedRequest {
type StorageAdapter (line 965) | interface StorageAdapter {
type Dependencies (line 1007) | type Dependencies = Set<string>;
type OperationType (line 1012) | type OperationType = 'read' | 'write';
type WithTypename (line 1017) | type WithTypename<T extends { __typename?: any }> = T & {
FILE: exchanges/persisted/src/persistedExchange.ts
type PersistedExchangeOptions (line 32) | interface PersistedExchangeOptions {
FILE: exchanges/populate/src/helpers/help.ts
type ErrorCode (line 5) | type ErrorCode =
function warn (line 29) | function warn(message: string, code: ErrorCode) {
FILE: exchanges/populate/src/helpers/node.ts
type GraphQLFlatType (line 4) | type GraphQLFlatType = Exclude<GraphQLOutputType, GraphQLWrappingType>;
function createNameNode (line 19) | function createNameNode(value: string): NameNode {
FILE: exchanges/populate/src/helpers/traverse.ts
function traverse (line 13) | function traverse(
function resolveFields (line 57) | function resolveFields(
function getUsedFragmentNames (line 85) | function getUsedFragmentNames(node: FragmentDefinitionNode) {
FILE: exchanges/populate/src/populateExchange.ts
type Options (line 28) | interface Options {
type PopulateExchangeOpts (line 52) | interface PopulateExchangeOpts {
type TypeKey (line 70) | type TypeKey = GraphQLObjectType | GraphQLInterfaceType;
type FieldValue (line 72) | type FieldValue = Record<string, FieldUsage>;
type TypeFields (line 73) | type TypeFields = Map<String, FieldValue>;
type FieldUsage (line 75) | interface FieldUsage {
type FragmentMap (line 81) | type FragmentMap<T extends string = string> = Record<T, FragmentDefiniti...
constant SKIP_COUNT_TYPE (line 82) | const SKIP_COUNT_TYPE = /^PageInfo|(Connection|Edge)$/;
FILE: exchanges/refocus/src/refocusExchange.ts
type RefocusOptions (line 4) | interface RefocusOptions {
FILE: exchanges/request-policy/src/requestPolicyExchange.ts
type Options (line 8) | interface Options {
FILE: exchanges/retry/src/retryExchange.ts
type RetryExchangeOptions (line 16) | interface RetryExchangeOptions {
type RetryState (line 82) | interface RetryState {
FILE: exchanges/throw-on-error/src/throwOnErrorExchange.ts
method onResult (line 11) | onResult(result) {
FILE: packages/core/src/client.ts
type ClientOptions (line 59) | interface ClientOptions {
type Client (line 190) | interface Client {
function nextOperation (line 561) | function nextOperation(operation: Operation) {
function dispatchOperation (line 579) | function dispatchOperation(operation?: Operation | void) {
method reexecuteOperation (line 702) | reexecuteOperation(operation: Operation) {
method createRequestOperation (line 733) | createRequestOperation(kind, request, opts) {
method executeRequestOperation (line 759) | executeRequestOperation(operation) {
method executeQuery (line 801) | executeQuery(query, opts) {
method executeSubscription (line 806) | executeSubscription(query, opts) {
method executeMutation (line 815) | executeMutation(query, opts) {
method readQuery (line 820) | readQuery(query, variables, context) {
method query (line 833) | query(query, variables, context) {
method subscription (line 837) | subscription(query, variables, context) {
method mutation (line 844) | mutation(query, variables, context) {
FILE: packages/core/src/exchanges/cache.ts
type ResultCache (line 15) | type ResultCache = Map<number, OperationResult>;
type OperationCache (line 16) | type OperationCache = Map<string, Set<number>>;
FILE: packages/core/src/exchanges/compose.ts
method forward (line 30) | forward(operations$) {
method dispatchDebug (line 40) | dispatchDebug(event) {
FILE: packages/core/src/exchanges/map.ts
type MapExchangeOpts (line 6) | interface MapExchangeOpts {
FILE: packages/core/src/exchanges/ssr.ts
type SerializedResult (line 16) | interface SerializedResult {
type SSRData (line 35) | interface SSRData {
type SSRExchangeParams (line 40) | interface SSRExchangeParams {
type SSRExchange (line 95) | interface SSRExchange extends Exchange {
FILE: packages/core/src/exchanges/subscription.test.ts
method subscribe (line 34) | subscribe(observer) {
method subscribe (line 66) | subscribe(observer) {
method subscribe (line 94) | subscribe(observer) {
FILE: packages/core/src/exchanges/subscription.ts
type ObserverLike (line 29) | interface ObserverLike<T> {
type ObservableLike (line 47) | interface ObservableLike<T> {
type SubscriptionOperation (line 61) | type SubscriptionOperation = FetchBody;
type SubscriptionForwarder (line 68) | type SubscriptionForwarder = (
type SubscriptionExchangeOpts (line 74) | interface SubscriptionExchangeOpts {
function nextResult (line 145) | function nextResult(value: ExecutionResult) {
method error (line 158) | error(error) {
method complete (line 170) | complete() {
FILE: packages/core/src/gql.ts
function gql (line 62) | function gql(parts: string | TemplateStringsArray /* arguments */) {
FILE: packages/core/src/internal/fetchOptions.ts
type FetchBody (line 11) | interface FetchBody {
function makeFetchBody (line 24) | function makeFetchBody<
FILE: packages/core/src/internal/fetchSource.test.ts
method get (line 257) | get() {
method get (line 383) | get() {
method get (line 501) | get() {
FILE: packages/core/src/internal/fetchSource.ts
type ChunkData (line 53) | type ChunkData = Buffer | Uint8Array;
function makeFetchSource (line 262) | function makeFetchSource(
FILE: packages/core/src/types.ts
type PersistedDocument (line 13) | interface PersistedDocument extends DocumentNode {
type TypedDocumentNode (line 36) | type TypedDocumentNode<
type FormattedNode (line 61) | type FormattedNode<Node> = Node extends readonly (infer Child)[]
type DocumentInput (line 88) | type DocumentInput<
type ErrorLike (line 96) | type ErrorLike = Partial<GraphQLError> | Error;
type Extensions (line 101) | type Extensions = Record<string, any>;
type PersistedRequestExtensions (line 109) | interface PersistedRequestExtensions {
type RequestExtensions (line 119) | interface RequestExtensions {
type Path (line 124) | type Path = readonly (string | number)[];
type IncrementalPayload (line 134) | interface IncrementalPayload {
type PendingIncrementalResult (line 193) | type PendingIncrementalResult = {
type ExecutionResult (line 199) | interface ExecutionResult {
type OperationResultSource (line 245) | type OperationResultSource<T extends OperationResult> = Source<T> &
type OperationType (line 271) | type OperationType = 'subscription' | 'query' | 'mutation' | 'teardown';
type RequestPolicy (line 284) | type RequestPolicy =
type CacheOutcome (line 300) | type CacheOutcome = 'miss' | 'partial' | 'hit';
type AnyVariables (line 308) | type AnyVariables = { [prop: string]: any } | void | undefined;
type GraphQLRequest (line 325) | interface GraphQLRequest<
type GraphQLRequestParams (line 378) | type GraphQLRequestParams<
type OperationDebugMeta (line 417) | interface OperationDebugMeta {
type OperationInstance (line 469) | type OperationInstance = number & {
type OperationContext (line 499) | interface OperationContext {
type Operation (line 623) | interface Operation<
type OperationResult (line 655) | interface OperationResult<
type ExchangeInput (line 726) | interface ExchangeInput {
type Exchange (line 791) | type Exchange = (input: ExchangeInput) => ExchangeIO;
type ExchangeIO (line 808) | type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>;
type DebugEventTypes (line 816) | interface DebugEventTypes {
type DebugEventArg (line 859) | type DebugEventArg<T extends keyof DebugEventTypes | string> = {
type DebugEvent (line 875) | type DebugEvent<T extends keyof DebugEventTypes | string = string> =
FILE: packages/core/src/utils/collectTypenames.ts
type EntityLike (line 1) | interface EntityLike {
FILE: packages/core/src/utils/error.ts
class CombinedError (line 55) | class CombinedError extends Error {
method constructor (line 95) | constructor(input: {
method toString (line 117) | toString(): string {
FILE: packages/core/src/utils/graphql.ts
type OrNever (line 4) | type OrNever<T> = void extends T ? never : T;
type GraphQLError (line 6) | type GraphQLError =
type DocumentNode (line 10) | type DocumentNode =
type DefinitionNode (line 14) | type DefinitionNode =
FILE: packages/core/src/utils/hash.ts
type HashValue (line 7) | type HashValue = number & {
FILE: packages/core/src/utils/operation.ts
function makeOperation (line 53) | function makeOperation(kind, request, context) {
FILE: packages/core/src/utils/request.ts
type PersistedDocumentNode (line 15) | type PersistedDocumentNode = TypedDocumentNode & {
type KeyedDocumentNode (line 22) | type KeyedDocumentNode = TypedDocumentNode & {
constant SOURCE_NAME (line 26) | const SOURCE_NAME = 'gql';
constant GRAPHQL_STRING_RE (line 27) | const GRAPHQL_STRING_RE = /("{3}[\s\S]*"{3}|"(?:\\.|[^"])*")/g;
constant REPLACE_CHAR_RE (line 28) | const REPLACE_CHAR_RE = /(?:#[^\n\r]+)?(?:[\r\n]+|$)/g;
FILE: packages/core/src/utils/streamUtils.ts
function withPromise (line 10) | function withPromise<T extends OperationResult>(
FILE: packages/core/src/utils/variables.ts
type FileMap (line 1) | type FileMap = Map<string, File | Blob>;
class NoopConstructor (line 88) | class NoopConstructor {}
FILE: packages/introspection/src/minifyIntrospectionQuery.ts
type MinifySchemaOptions (line 174) | interface MinifySchemaOptions {
FILE: packages/next-urql/src/DataHydrationContext.ts
type DataHydrationValue (line 6) | interface DataHydrationValue {
function transportDataToJS (line 21) | function transportDataToJS(data: any) {
function useDataHydrationContext (line 45) | function useDataHydrationContext(): DataHydrationValue | undefined {
function buildContext (line 63) | function buildContext({ nonce }: { nonce?: string }): DataHydrationValue {
FILE: packages/next-urql/src/Provider.ts
function UrqlProvider (line 51) | function UrqlProvider({
FILE: packages/next-urql/src/htmlescape.ts
constant ESCAPE_LOOKUP (line 7) | const ESCAPE_LOOKUP: { [match: string]: string } = {
constant ESCAPE_REGEX (line 15) | const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
function htmlEscapeJsonString (line 17) | function htmlEscapeJsonString(str: string): string {
FILE: packages/next-urql/src/rsc.ts
function registerUrql (line 23) | function registerUrql(makeClient: () => Client): {
FILE: packages/next-urql/src/useQuery.ts
type UseQueryArgs (line 19) | type UseQueryArgs<
type UseQueryState (line 83) | interface UseQueryState<
type UseQueryExecute (line 148) | type UseQueryExecute = (opts?: Partial<OperationContext>) => void;
type UseQueryResponse (line 159) | type UseQueryResponse<
function useQuery (line 200) | function useQuery<
FILE: packages/next-urql/src/useUrqlValue.ts
type UrqlResult (line 10) | type UrqlResult = { data?: any; error?: any; extensions?: any };
function useUrqlValue (line 12) | function useUrqlValue(operationKey: number): void {
FILE: packages/preact-urql/src/components/Mutation.ts
type MutationProps (line 16) | interface MutationProps<
type MutationState (line 32) | interface MutationState<
function Mutation (line 47) | function Mutation<
FILE: packages/preact-urql/src/components/Query.ts
type QueryProps (line 17) | type QueryProps<
type QueryState (line 31) | interface QueryState<
function Query (line 46) | function Query<
FILE: packages/preact-urql/src/components/Subscription.ts
type SubscriptionProps (line 24) | type SubscriptionProps<
type SubscriptionState (line 41) | interface SubscriptionState<
function Subscription (line 56) | function Subscription<
FILE: packages/preact-urql/src/context.ts
constant OBJ (line 5) | const OBJ = {};
FILE: packages/preact-urql/src/hooks/useMutation.ts
type UseMutationState (line 27) | interface UseMutationState<
type UseMutationExecute (line 84) | type UseMutationExecute<
type UseMutationResponse (line 101) | type UseMutationResponse<
function useMutation (line 145) | function useMutation<
FILE: packages/preact-urql/src/hooks/useQuery.ts
type UseQueryArgs (line 36) | type UseQueryArgs<
type UseQueryState (line 100) | interface UseQueryState<
type UseQueryExecute (line 162) | type UseQueryExecute = (opts?: Partial<OperationContext>) => void;
type UseQueryResponse (line 173) | type UseQueryResponse<
function toSuspenseSource (line 181) | function toSuspenseSource<T>(source: Source<T>): Source<T> {
function useQuery (line 259) | function useQuery<
FILE: packages/preact-urql/src/hooks/useRequest.ts
function useRequest (line 13) | function useRequest<
FILE: packages/preact-urql/src/hooks/useSource.ts
type Updater (line 8) | type Updater<T> = (input: T) => void;
function useSource (line 35) | function useSource<T, R>(
FILE: packages/preact-urql/src/hooks/useSubscription.ts
type UseSubscriptionArgs (line 22) | type UseSubscriptionArgs<
type SubscriptionHandler (line 84) | type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;
type UseSubscriptionState (line 99) | interface UseSubscriptionState<
type UseSubscriptionExecute (line 166) | type UseSubscriptionExecute = (opts?: Partial<OperationContext>) => void;
type UseSubscriptionResponse (line 177) | type UseSubscriptionResponse<
function useSubscription (line 218) | function useSubscription<
FILE: packages/react-urql/cypress.config.js
method setupNodeEvents (line 10) | setupNodeEvents(_on, _config) {
FILE: packages/react-urql/src/components/Mutation.ts
type MutationProps (line 16) | interface MutationProps<
type MutationState (line 32) | interface MutationState<
function Mutation (line 47) | function Mutation<
FILE: packages/react-urql/src/components/Query.ts
type QueryProps (line 17) | type QueryProps<
type QueryState (line 31) | interface QueryState<
function Query (line 46) | function Query<
FILE: packages/react-urql/src/components/Subscription.ts
type SubscriptionProps (line 24) | type SubscriptionProps<
type SubscriptionState (line 40) | interface SubscriptionState<
function Subscription (line 55) | function Subscription<
FILE: packages/react-urql/src/context.ts
constant OBJ (line 4) | const OBJ = {};
FILE: packages/react-urql/src/hooks/cache.ts
type CacheEntry (line 4) | type CacheEntry = OperationResult | Promise<unknown> | undefined;
type Cache (line 6) | interface Cache {
type ClientWithCache (line 12) | interface ClientWithCache extends Client {
method get (line 34) | get(key) {
method set (line 37) | set(key, value) {
method dispose (line 41) | dispose(key) {
FILE: packages/react-urql/src/hooks/state.ts
type Stateish (line 39) | interface Stateish {
function deferDispatch (line 77) | function deferDispatch<F extends Dispatch<any>>(
FILE: packages/react-urql/src/hooks/useMutation.ts
type UseMutationState (line 27) | interface UseMutationState<
type UseMutationExecute (line 84) | type UseMutationExecute<
type UseMutationResponse (line 101) | type UseMutationResponse<
function useMutation (line 145) | function useMutation<
FILE: packages/react-urql/src/hooks/useQuery.ts
type UseQueryArgs (line 34) | type UseQueryArgs<
type UseQueryState (line 98) | interface UseQueryState<
type UseQueryExecute (line 163) | type UseQueryExecute = (opts?: Partial<OperationContext>) => void;
type UseQueryResponse (line 174) | type UseQueryResponse<
function useQuery (line 220) | function useQuery<
FILE: packages/react-urql/src/hooks/useRequest.ts
function useRequest (line 8) | function useRequest<
FILE: packages/react-urql/src/hooks/useSubscription.ts
type UseSubscriptionArgs (line 29) | type UseSubscriptionArgs<
type SubscriptionHandler (line 91) | type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;
type UseSubscriptionState (line 106) | interface UseSubscriptionState<
type UseSubscriptionExecute (line 173) | type UseSubscriptionExecute = (opts?: Partial<OperationContext>) => void;
type UseSubscriptionResponse (line 184) | type UseSubscriptionResponse<
function useSubscription (line 225) | function useSubscription<
FILE: packages/site/plugins/assets-fix/node.api.js
method webpack (line 2) | webpack(config) {
FILE: packages/site/plugins/monorepo-fix/node.api.js
constant NODE_MODULES_JS_RE (line 3) | const NODE_MODULES_JS_RE = /node_modules[/\\].*\.js$/;
constant REACT_STATIC_RE (line 4) | const REACT_STATIC_RE = /node_modules[/\\]react-static/;
FILE: packages/site/src/screens/home/features.js
class Features (line 59) | class Features extends React.Component {
method render (line 60) | render() {
FILE: packages/site/src/screens/home/get-started.js
class GetStarted (line 36) | class GetStarted extends React.Component {
method render (line 37) | render() {
FILE: packages/solid-start-urql/src/context.ts
type UrqlContext (line 8) | interface UrqlContext {
type UseClient (line 29) | type UseClient = () => Client;
type UseQuery (line 36) | type UseQuery = () => typeof defaultQuery;
type UseAction (line 43) | type UseAction = () => typeof defaultAction;
FILE: packages/solid-start-urql/src/createMutation.ts
type CreateMutationAction (line 12) | type CreateMutationAction<
function createMutation (line 69) | function createMutation<
FILE: packages/solid-start-urql/src/createQuery.ts
function createQuery (line 50) | function createQuery<
FILE: packages/solid-start-urql/src/createSubscription.test.ts
constant QUERY (line 14) | const QUERY = gql`
FILE: packages/solid-start-urql/src/createSubscription.ts
type CreateSubscriptionExecute (line 22) | type CreateSubscriptionExecute = (
type CreateSubscriptionArgs (line 26) | type CreateSubscriptionArgs<
type CreateSubscriptionState (line 36) | type CreateSubscriptionState<
type SubscriptionHandler (line 48) | type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;
type CreateSubscriptionResult (line 50) | type CreateSubscriptionResult<
FILE: packages/solid-urql/src/context.ts
type UseClient (line 7) | type UseClient = () => Client;
FILE: packages/solid-urql/src/createMutation.test.ts
constant QUERY (line 15) | const QUERY = gql`
FILE: packages/solid-urql/src/createMutation.ts
type CreateMutationState (line 15) | type CreateMutationState<
type CreateMutationExecute (line 73) | type CreateMutationExecute<
type CreateMutationResult (line 90) | type CreateMutationResult<
FILE: packages/solid-urql/src/createQuery.ts
type CreateQueryExecute (line 42) | type CreateQueryExecute = (opts?: Partial<OperationContext>) => void;
type CreateQueryState (line 56) | type CreateQueryState<
type CreateQueryArgs (line 77) | type CreateQueryArgs<
type CreateQueryResult (line 132) | type CreateQueryResult<
method get (line 325) | get(
FILE: packages/solid-urql/src/createSubscription.test.ts
constant QUERY (line 19) | const QUERY = gql`
FILE: packages/solid-urql/src/createSubscription.ts
type CreateSubscriptionExecute (line 43) | type CreateSubscriptionExecute = (
type CreateSubscriptionArgs (line 48) | type CreateSubscriptionArgs<
type CreateSubscriptionState (line 78) | type CreateSubscriptionState<
type SubscriptionHandler (line 155) | type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;
type CreateSubscriptionResult (line 166) | type CreateSubscriptionResult<
FILE: packages/storage-rn/src/makeAsyncStorage.ts
type StorageOptions (line 5) | interface StorageOptions {
type DefaultAsyncStorage (line 33) | interface DefaultAsyncStorage extends StorageAdapter {
FILE: packages/svelte-urql/src/common.ts
type OperationResultState (line 12) | interface OperationResultState<
type OperationResultStore (line 30) | type OperationResultStore<
type Pausable (line 62) | interface Pausable {
method pause (line 103) | pause() {
method resume (line 106) | resume() {
FILE: packages/svelte-urql/src/mutationStore.ts
type MutationArgs (line 20) | type MutationArgs<
function mutationStore (line 90) | function mutationStore<
FILE: packages/svelte-urql/src/queryStore.ts
type QueryArgs (line 36) | type QueryArgs<
function queryStore (line 117) | function queryStore<
FILE: packages/svelte-urql/src/subscriptionStore.ts
type SubscriptionHandler (line 54) | type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;
type SubscriptionArgs (line 61) | type SubscriptionArgs<
function subscriptionStore (line 135) | function subscriptionStore<
FILE: packages/vue-urql/src/useClient.test.ts
method setup (line 12) | setup() {
method setup (line 31) | setup() {
method setup (line 40) | setup() {
method setup (line 73) | setup() {
FILE: packages/vue-urql/src/useClient.ts
function provideClient (line 35) | function provideClient(opts: ClientOptions | Client | Ref<Client>) {
function install (line 77) | function install(app: App, opts: ClientOptions | Client | Ref<Client>) {
function useClient (line 99) | function useClient(): Ref<Client> {
FILE: packages/vue-urql/src/useClientHandle.ts
type ClientHandle (line 35) | interface ClientHandle {
function useClientHandle (line 129) | function useClientHandle(): ClientHandle {
FILE: packages/vue-urql/src/useMutation.ts
type UseMutationResponse (line 35) | interface UseMutationResponse<T, V extends AnyVariables = AnyVariables> {
function useMutation (line 131) | function useMutation<T = any, V extends AnyVariables = AnyVariables>(
function callUseMutation (line 137) | function callUseMutation<T = any, V extends AnyVariables = AnyVariables>(
FILE: packages/vue-urql/src/useQuery.ts
type UseQueryArgs (line 29) | type UseQueryArgs<
type UseQueryState (line 92) | interface UseQueryState<T = any, V extends AnyVariables = AnyVariables> {
type UseQueryResponse (line 193) | type UseQueryResponse<
function useQuery (line 233) | function useQuery<T = any, V extends AnyVariables = AnyVariables>(
function callUseQuery (line 239) | function callUseQuery<T = any, V extends AnyVariables = AnyVariables>(
FILE: packages/vue-urql/src/useSubscription.ts
type UseSubscriptionArgs (line 27) | type UseSubscriptionArgs<
type SubscriptionHandler (line 86) | type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;
type SubscriptionHandlerArg (line 89) | type SubscriptionHandlerArg<T, R> =
type UseSubscriptionResponse (line 105) | interface UseSubscriptionResponse<
function useSubscription (line 221) | function useSubscription<
function callUseSubscription (line 232) | function callUseSubscription<
FILE: packages/vue-urql/src/utils.ts
type MaybeRefOrGetter (line 18) | type MaybeRefOrGetter<T> = T | (() => T) | Ref<T>;
type MaybeRefOrGetterObj (line 19) | type MaybeRefOrGetterObj<T extends {}> =
function useClientState (line 66) | function useClientState<T = any, V extends AnyVariables = AnyVariables>(
FILE: scripts/babel/transform-debug-target.mjs
method ExportNamedDeclaration (line 16) | ExportNamedDeclaration(path) {
method CallExpression (line 27) | CallExpression(path, meta) {
FILE: scripts/babel/transform-invariant-warning.mjs
method CallExpression (line 16) | CallExpression(path) {
FILE: scripts/babel/transform-pipe.mjs
method ImportDeclaration (line 10) | ImportDeclaration(path, state) {
method CallExpression (line 28) | CallExpression(path, state) {
FILE: scripts/changesets/changelog.js
constant REPO (line 6) | const REPO = 'urql-graphql/urql';
constant SEE_LINE (line 7) | const SEE_LINE = /^See:\s*(.*)/i;
constant TRAILING_CHAR (line 8) | const TRAILING_CHAR = /[.;:]$/g;
FILE: scripts/rollup/cleanup-plugin.mjs
function cleanup (line 3) | function cleanup() {
FILE: scripts/rollup/config.mjs
method writeBundle (line 36) | async writeBundle() {
method interop (line 109) | interop(id) {
method onwarn (line 130) | onwarn() {}
Condensed preview — 671 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,738K chars).
[
{
"path": ".changeset/README.md",
"chars": 512,
"preview": "# Changesets\n\nHello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that wo"
},
{
"path": ".changeset/config.json",
"chars": 492,
"preview": "{\n \"$schema\": \"https://unpkg.com/@changesets/config@0.3.0/schema.json\",\n \"changelog\": \"../scripts/changesets/changelog"
},
{
"path": ".changeset/late-boats-listen.md",
"chars": 180,
"preview": "---\n'@urql/solid-start': minor\n---\n\nFix SSR runtime failures caused by importing SolidStart's `action` API at module loa"
},
{
"path": ".changeset/shiny-pets-give.md",
"chars": 170,
"preview": "---\n'@urql/solid-start': patch\n---\n\nFix `createSubscription` to use `@urql/solid-start` context instead of re-exporting "
},
{
"path": ".editorconfig",
"chars": 274,
"preview": "# http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_size = 2\nend_of_line = lf\nindent_style = space\ninsert_"
},
{
"path": ".gitattributes",
"chars": 12,
"preview": "* text=auto\n"
},
{
"path": ".github/CODEOWNERS",
"chars": 226,
"preview": "/.github/ @urql-graphql/core\n/.changeset/config.json @urql-graphql/core\n/scripts/actions/* @urql-graphql/core\n/scripts/p"
},
{
"path": ".github/ISSUE_TEMPLATE/RFC.md",
"chars": 1115,
"preview": "---\nname: 'RFC'\nabout: Propose an enhancement / feature and start a discussion\ntitle: 'RFC: Your Proposal'\nlabels: \"futu"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
"chars": 1969,
"preview": "name: \"\\U0001F41E Bug report\"\ndescription: Report an issue with urql\nlabels: []\nbody:\n - type: markdown\n attributes:"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 313,
"preview": "blank_issues_enabled: true\ncontact_links:\n - name: Ask a question\n url: https://github.com/urql-graphql/urql/discuss"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 739,
"preview": "<!--\n Thanks for opening a pull request! We appreciate your dedication and help!\n Before submitting your pull request,"
},
{
"path": ".github/actions/discord-message/action.mjs",
"chars": 2480,
"preview": "import * as core from '@actions/core';\nimport * as github from '@actions/github';\n\nconst GITHUB_TOKEN = process.env.GITH"
},
{
"path": ".github/actions/discord-message/action.yml",
"chars": 345,
"preview": "name: 'Send a discord message'\ndescription: 'Send a discord message as a result of an urql publish.'\ninputs:\n published"
},
{
"path": ".github/actions/pnpm-run/action.mjs",
"chars": 298,
"preview": "import { execa } from 'execa';\n\nconst run = execa('pnpm', ['run', process.env.INPUT_COMMAND], {\n cwd: process.cwd(),\n})"
},
{
"path": ".github/actions/pnpm-run/action.yml",
"chars": 201,
"preview": "name: 'Run a pnpm command'\ndescription: 'Locally run a forked pnpm command as an action.'\ninputs:\n command:\n descrip"
},
{
"path": ".github/workflows/ci.yml",
"chars": 5181,
"preview": "name: CI\n\non:\n pull_request:\n pull_request_review:\n types: [submitted, edited]\n branches: changeset-release/main"
},
{
"path": ".github/workflows/mirror.yml",
"chars": 773,
"preview": "# Mirrors to https://tangled.sh/@kitten.sh (knot.kitten.sh)\nname: Mirror (Git Backup)\non:\n push:\n branches:\n - "
},
{
"path": ".github/workflows/release.yml",
"chars": 2310,
"preview": "name: Release\non:\n push:\n branches:\n - main\n\njobs:\n release:\n name: Release\n runs-on: ubuntu-24.04\n t"
},
{
"path": ".gitignore",
"chars": 488,
"preview": "/.idea\n/.vscode\n**/node_modules\n*.log\n.rts2_cache*\n.husky\ndist/\nbuild/\ncoverage/\npackage-lock.json\n.DS_Store\n.next\n\npack"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3395,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 12007,
"preview": "# Development\n\nThanks for contributing! We want to ensure that `urql` evolves and fulfills\nits idea of extensibility and"
},
{
"path": "LICENSE",
"chars": 1128,
"preview": "MIT License\n\nCopyright (c) 2018–2020 Formidable,\nCopyright (c) urql GraphQL Team and other contributors\n\nPermission is h"
},
{
"path": "README.md",
"chars": 5157,
"preview": "<div align=\"center\">\n <img alt=\"urql\" width=\"250\" src=\"packages/site/src/assets/sidebar-badge.svg\" />\n\n <br />\n <br /"
},
{
"path": "docs/README.md",
"chars": 3587,
"preview": "---\ntitle: Overview\norder: 1\n---\n\n# Overview\n\n`urql` is a highly customizable and versatile GraphQL client with which yo"
},
{
"path": "docs/advanced/README.md",
"chars": 1601,
"preview": "---\ntitle: Advanced\norder: 4\n---\n\n# Advanced\n\nIn this chapter we'll dive into various topics of \"advanced\" `urql` usage."
},
{
"path": "docs/advanced/authentication.md",
"chars": 15136,
"preview": "---\ntitle: Authentication\norder: 6\n---\n\n# Authentication\n\nMost APIs include some type of authentication, usually in the "
},
{
"path": "docs/advanced/authoring-exchanges.md",
"chars": 9192,
"preview": "---\ntitle: Authoring Exchanges\norder: 8\n---\n\n# Exchange Author Guide\n\nAs we've learned [on the \"Architecture\" page](../a"
},
{
"path": "docs/advanced/auto-populate-mutations.md",
"chars": 3500,
"preview": "---\ntitle: Auto-populate Mutations\norder: 9\n---\n\n# Automatically populating Mutations\n\nThe `populateExchange` allows you"
},
{
"path": "docs/advanced/debugging.md",
"chars": 5736,
"preview": "---\ntitle: Debugging\norder: 4\n---\n\n# Debugging\n\nWe've tried to make debugging in `urql` as seamless as possible by creat"
},
{
"path": "docs/advanced/persistence-and-uploads.md",
"chars": 6527,
"preview": "---\ntitle: Persistence & Uploads\norder: 1\n---\n\n# Persisted Queries and Uploads\n\n`urql` supports (Automatic) Persisted Qu"
},
{
"path": "docs/advanced/retry-operations.md",
"chars": 4600,
"preview": "---\ntitle: Retrying Operations\norder: 5\n---\n\n# Retrying Operations\n\nThe `retryExchange` lets us retry specific operation"
},
{
"path": "docs/advanced/server-side-rendering.md",
"chars": 20859,
"preview": "---\ntitle: Server-side Rendering\norder: 3\n---\n\n# Server-side Rendering\n\nIn server-side rendered applications we often ne"
},
{
"path": "docs/advanced/subscriptions.md",
"chars": 11595,
"preview": "---\ntitle: Subscriptions\norder: 0\n---\n\n# Subscriptions\n\nOne feature of `urql` that was not mentioned in the [\"Basics\" se"
},
{
"path": "docs/advanced/testing.md",
"chars": 9159,
"preview": "---\ntitle: Testing\norder: 7\n---\n\n# Testing\n\nTesting with `urql` can be done in a multitude of ways. The most effective a"
},
{
"path": "docs/api/README.md",
"chars": 1197,
"preview": "---\ntitle: API\norder: 9\n---\n\n# API\n\n`urql` is a collection of multiple packages. You'll likely be using one of the frame"
},
{
"path": "docs/api/auth-exchange.md",
"chars": 2631,
"preview": "---\ntitle: '@urql/exchange-auth'\norder: 10\n---\n\n# Authentication Exchange\n\n> **Note:** These API docs are deprecated as "
},
{
"path": "docs/api/core.md",
"chars": 25994,
"preview": "---\ntitle: '@urql/core'\norder: 0\n---\n\n# @urql/core\n\n> **Note:** These API docs are deprecated as we now keep TSDocs in a"
},
{
"path": "docs/api/execute-exchange.md",
"chars": 3500,
"preview": "---\ntitle: '@urql/exchange-execute'\norder: 6\n---\n\n# Execute Exchange\n\n> **Note:** These API docs are deprecated as we no"
},
{
"path": "docs/api/graphcache.md",
"chars": 34570,
"preview": "---\ntitle: '@urql/exchange-graphcache'\norder: 4\n---\n\n# @urql/exchange-graphcache\n\n> **Note:** These API docs are depreca"
},
{
"path": "docs/api/preact.md",
"chars": 483,
"preview": "---\ntitle: '@urql/preact'\norder: 2\n---\n\n# @urql/preact\n\n> **Note:** These API docs are deprecated as we now keep TSDocs "
},
{
"path": "docs/api/refocus-exchange.md",
"chars": 1025,
"preview": "---\ntitle: '@urql/exchange-refocus'\norder: 11\n---\n\n# Refocus Exchange\n\n> **Note:** These API docs are deprecated as we n"
},
{
"path": "docs/api/request-policy-exchange.md",
"chars": 2902,
"preview": "---\ntitle: '@urql/exchange-request-policy'\norder: 9\n---\n\n# Request Policy Exchange\n\n> **Note:** These API docs are depre"
},
{
"path": "docs/api/retry-exchange.md",
"chars": 3986,
"preview": "---\ntitle: '@urql/exchange-retry'\norder: 5\n---\n\n# Retry Exchange\n\n> **Note:** These API docs are deprecated as we now ke"
},
{
"path": "docs/api/svelte.md",
"chars": 6852,
"preview": "---\ntitle: '@urql/svelte'\norder: 3\n---\n\n# Svelte API\n\n> **Note:** These API docs are deprecated as we now keep TSDocs in"
},
{
"path": "docs/api/urql.md",
"chars": 7905,
"preview": "---\ntitle: urql (React)\norder: 1\n---\n\n# React API\n\n> **Note:** These API docs are deprecated as we now keep TSDocs in al"
},
{
"path": "docs/api/vue.md",
"chars": 10257,
"preview": "---\ntitle: '@urql/vue'\norder: 3\n---\n\n# Vue API\n\n> **Note:** These API docs are deprecated as we now keep TSDocs in all p"
},
{
"path": "docs/architecture.md",
"chars": 13017,
"preview": "---\ntitle: Architecture\norder: 3\n---\n\n# Architecture\n\n`urql` is a highly customizable and flexible GraphQL client.\nAs yo"
},
{
"path": "docs/basics/README.md",
"chars": 1049,
"preview": "---\ntitle: Basics\norder: 2\n---\n\n# Basics\n\nIn this chapter we'll explain the basics of `urql` and how to get started with"
},
{
"path": "docs/basics/core.md",
"chars": 12765,
"preview": "---\ntitle: Core / Node.js\norder: 3\n---\n\n# Core and Node.js Usage\n\nThe `@urql/core` package contains `urql`'s `Client`, s"
},
{
"path": "docs/basics/document-caching.md",
"chars": 4467,
"preview": "---\ntitle: Document Caching\norder: 4\n---\n\n# Document Caching\n\nBy default, `urql` uses a concept called _Document Caching"
},
{
"path": "docs/basics/errors.md",
"chars": 1294,
"preview": "---\ntitle: Errors\norder: 5\n---\n\n# Error handling\n\nWhen we use a GraphQL API there are two kinds of errors we may encount"
},
{
"path": "docs/basics/react-preact.md",
"chars": 15645,
"preview": "---\ntitle: React/Preact Bindings\norder: 0\n---\n\n# React/Preact\n\nThis guide covers how to install and setup `urql` and the"
},
{
"path": "docs/basics/solid-start.md",
"chars": 23953,
"preview": "---\ntitle: SolidStart Bindings\norder: 3\n---\n\n# SolidStart\n\nThis guide covers how to use `@urql/solid-start` with SolidSt"
},
{
"path": "docs/basics/solid.md",
"chars": 15271,
"preview": "---\ntitle: Solid Bindings\norder: 3\n---\n\n# Solid\n\nThis guide covers how to install and setup `@urql/solid` and the `Clien"
},
{
"path": "docs/basics/svelte.md",
"chars": 13681,
"preview": "---\ntitle: Svelte Bindings\norder: 2\n---\n\n# Svelte\n\n## Getting started\n\nThis \"Getting Started\" guide covers how to instal"
},
{
"path": "docs/basics/typescript-integration.md",
"chars": 5634,
"preview": "---\ntitle: TypeScript integration\norder: 7\n---\n\n# URQL and TypeScript\n\nURQL, with the help of [GraphQL Code Generator](h"
},
{
"path": "docs/basics/ui-patterns.md",
"chars": 6350,
"preview": "---\ntitle: UI-Patterns\norder: 6\n---\n\n# UI Patterns\n\n> This page is incomplete. You can help us expanding it by suggestin"
},
{
"path": "docs/basics/vue.md",
"chars": 21990,
"preview": "---\ntitle: Vue Bindings\norder: 1\n---\n\n# Vue\n\n## Getting started\n\nThe `@urql/vue` bindings have been written with [Vue\n3]"
},
{
"path": "docs/comparison.md",
"chars": 17004,
"preview": "---\ntitle: Comparison\norder: 8\n---\n\n# Comparison\n\n> This comparison page aims to be detailed, unbiased, and up-to-date. "
},
{
"path": "docs/graphcache/README.md",
"chars": 4539,
"preview": "---\ntitle: Graphcache\norder: 5\n---\n\n# Graphcache\n\nIn `urql`, caching is fully configurable via [exchanges](../architectu"
},
{
"path": "docs/graphcache/cache-updates.md",
"chars": 28332,
"preview": "---\ntitle: Cache Updates\norder: 4\n---\n\n# Cache Updates\n\nAs we've learned [on the page on \"Normalized\nCaching\"](./normali"
},
{
"path": "docs/graphcache/errors.md",
"chars": 17936,
"preview": "---\ntitle: Errors\norder: 8\n---\n\n# Help!\n\n**This document lists out all errors and warnings in `@urql/exchange-graphcache"
},
{
"path": "docs/graphcache/local-directives.md",
"chars": 3775,
"preview": "---\ntitle: Local Directives\norder: 3\n---\n\n# Local Directives\n\nPreviously, we've learned about local resolvers [on the \"N"
},
{
"path": "docs/graphcache/local-resolvers.md",
"chars": 30021,
"preview": "---\ntitle: Local Resolvers\norder: 2\n---\n\n# Local Resolvers\n\nPreviously, we've learned about local resolvers [on the \"Nor"
},
{
"path": "docs/graphcache/normalized-caching.md",
"chars": 25272,
"preview": "---\ntitle: Normalized Caching\norder: 1\n---\n\n# Normalized Caching\n\nIn GraphQL, like its name suggests, we create schemas "
},
{
"path": "docs/graphcache/offline.md",
"chars": 8412,
"preview": "---\ntitle: Offline Support\norder: 7\n---\n\n# Offline Support\n\n_Graphcache_ allows you to build an offline-first app with b"
},
{
"path": "docs/graphcache/schema-awareness.md",
"chars": 8871,
"preview": "---\ntitle: Schema Awareness\norder: 5\n---\n\n# Schema Awareness\n\nPreviously, [on the \"Normalized Caching\" page](./normalize"
},
{
"path": "docs/showcase.md",
"chars": 2896,
"preview": "---\ntitle: Showcase\norder: 7\n---\n\n# Showcase\n\n`urql` wouldn't be the same without our growing and loving community of us"
},
{
"path": "examples/README.md",
"chars": 11056,
"preview": "<table>\n<thead>\n <tr>\n <th>Example</th>\n <th></th>\n <th></th>\n </tr>\n</thead>\n<tbody>\n\n<tr>\n <td>\n <a hre"
},
{
"path": "examples/pnpm-workspace.yaml",
"chars": 18,
"preview": "packages:\n - '*'\n"
},
{
"path": "examples/with-apq/README.md",
"chars": 1471,
"preview": "# With Automatic Persisted Queries\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/wi"
},
{
"path": "examples/with-apq/index.html",
"chars": 759,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-apq/package.json",
"chars": 387,
"preview": "{\n \"name\": \"with-apq\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n \"dependencies"
},
{
"path": "examples/with-apq/src/App.jsx",
"chars": 524,
"preview": "import React from 'react';\nimport { Client, Provider, fetchExchange } from 'urql';\nimport { persistedExchange } from '@u"
},
{
"path": "examples/with-apq/src/LocationsList.jsx",
"chars": 696,
"preview": "import React from 'react';\nimport { gql, useQuery } from 'urql';\n\nconst LOCATIONS_QUERY = gql`\n query Locations($query:"
},
{
"path": "examples/with-apq/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-apq/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-defer-stream-directives/README.md",
"chars": 1129,
"preview": "# With `@defer` / `@stream` Directives\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/example"
},
{
"path": "examples/with-defer-stream-directives/index.html",
"chars": 779,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-defer-stream-directives/package.json",
"chars": 813,
"preview": "{\n \"name\": \"with-defer-stream-directives\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"server:apollo\":"
},
{
"path": "examples/with-defer-stream-directives/server/apollo-server.js",
"chars": 455,
"preview": "// NOTE: This currently fails because responses for @defer/@stream are not sent\n// as multipart responses, but the reque"
},
{
"path": "examples/with-defer-stream-directives/server/graphql-yoga.js",
"chars": 333,
"preview": "const { createYoga } = require('graphql-yoga');\nconst { useDeferStream } = require('@graphql-yoga/plugin-defer-stream');"
},
{
"path": "examples/with-defer-stream-directives/server/schema.js",
"chars": 1441,
"preview": "const {\n GraphQLList,\n GraphQLObjectType,\n GraphQLSchema,\n GraphQLString,\n} = require('graphql');\n\nconst schema = ne"
},
{
"path": "examples/with-defer-stream-directives/src/App.jsx",
"chars": 507,
"preview": "import React from 'react';\nimport { Client, Provider, fetchExchange } from 'urql';\n\nimport { cacheExchange } from '@urql"
},
{
"path": "examples/with-defer-stream-directives/src/Songs.jsx",
"chars": 899,
"preview": "import React from 'react';\nimport { gql, useQuery } from 'urql';\n\nconst SecondVerseFragment = gql`\n fragment secondVers"
},
{
"path": "examples/with-defer-stream-directives/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-defer-stream-directives/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-graphcache-pagination/README.md",
"chars": 1476,
"preview": "# With Graphcache's Pagination\n\n<p>\n <a\n href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with"
},
{
"path": "examples/with-graphcache-pagination/index.html",
"chars": 777,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-graphcache-pagination/package.json",
"chars": 406,
"preview": "{\n \"name\": \"with-graphcache-pagination\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n "
},
{
"path": "examples/with-graphcache-pagination/src/App.jsx",
"chars": 649,
"preview": "import React from 'react';\nimport { Client, Provider, fetchExchange } from 'urql';\nimport { cacheExchange } from '@urql/"
},
{
"path": "examples/with-graphcache-pagination/src/PaginatedNpmSearch.jsx",
"chars": 1257,
"preview": "import React, { useState } from 'react';\nimport { gql, useQuery } from 'urql';\n\nconst limit = 5;\nconst query = 'graphql'"
},
{
"path": "examples/with-graphcache-pagination/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-graphcache-pagination/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-graphcache-updates/README.md",
"chars": 1511,
"preview": "# With Graphcache's Pagination\n\n<p>\n <a\n href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with"
},
{
"path": "examples/with-graphcache-updates/index.html",
"chars": 774,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-graphcache-updates/package.json",
"chars": 440,
"preview": "{\n \"name\": \"with-graphcache-updates\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n"
},
{
"path": "examples/with-graphcache-updates/src/App.jsx",
"chars": 701,
"preview": "import React, { useState, useEffect } from 'react';\nimport { Provider } from 'urql';\n\nimport client from './client';\nimp"
},
{
"path": "examples/with-graphcache-updates/src/client.js",
"chars": 2778,
"preview": "import { Client, fetchExchange, gql } from 'urql';\nimport { authExchange } from '@urql/exchange-auth';\nimport { cacheExc"
},
{
"path": "examples/with-graphcache-updates/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-graphcache-updates/src/pages/Links.jsx",
"chars": 1740,
"preview": "import React from 'react';\nimport { gql, useQuery, useMutation } from 'urql';\n\nconst LINKS_QUERY = gql`\n query Links($f"
},
{
"path": "examples/with-graphcache-updates/src/pages/LoginForm.jsx",
"chars": 2638,
"preview": "import React from 'react';\nimport { gql, useMutation } from 'urql';\n\nconst LOGIN_MUTATION = gql`\n mutation Login($input"
},
{
"path": "examples/with-graphcache-updates/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-infinite-pagination/README.md",
"chars": 2294,
"preview": "# With Infinite Pagination (in React)\n\n<p>\n <a\n href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/exampl"
},
{
"path": "examples/with-infinite-pagination/index.html",
"chars": 766,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-infinite-pagination/package.json",
"chars": 352,
"preview": "{\n \"name\": \"with-pagination\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n \"depen"
},
{
"path": "examples/with-infinite-pagination/src/App.jsx",
"chars": 1369,
"preview": "import React, { useState } from 'react';\nimport { Client, Provider, cacheExchange, fetchExchange } from 'urql';\n\nimport "
},
{
"path": "examples/with-infinite-pagination/src/SearchResults.jsx",
"chars": 4981,
"preview": "import React, { useCallback } from 'react';\nimport { gql, useQuery } from 'urql';\n\n// We define a fragment, just to defi"
},
{
"path": "examples/with-infinite-pagination/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-infinite-pagination/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-multipart/README.md",
"chars": 1264,
"preview": "# With Multipart File Upload\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-mul"
},
{
"path": "examples/with-multipart/index.html",
"chars": 765,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-multipart/package.json",
"chars": 351,
"preview": "{\n \"name\": \"with-multipart\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n \"depend"
},
{
"path": "examples/with-multipart/src/App.jsx",
"chars": 371,
"preview": "import React from 'react';\nimport { Client, Provider, fetchExchange } from 'urql';\n\nimport FileUpload from './FileUpload"
},
{
"path": "examples/with-multipart/src/FileUpload.jsx",
"chars": 984,
"preview": "import React, { useState } from 'react';\nimport { gql, useMutation } from 'urql';\n\nconst UPLOAD_FILE = gql`\n mutation U"
},
{
"path": "examples/with-multipart/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-multipart/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-next/README.md",
"chars": 902,
"preview": "# With Next.js\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-next\">\n <img\n "
},
{
"path": "examples/with-next/app/layout.tsx",
"chars": 274,
"preview": "export const metadata = {\n title: 'Create Next App',\n description: 'Generated by create next app',\n};\n\nexport default "
},
{
"path": "examples/with-next/app/non-rsc/layout.tsx",
"chars": 665,
"preview": "'use client';\n\nimport { useMemo } from 'react';\nimport {\n UrqlProvider,\n ssrExchange,\n cacheExchange,\n fetchExchange"
},
{
"path": "examples/with-next/app/non-rsc/page.tsx",
"chars": 1163,
"preview": "'use client';\n\nimport Link from 'next/link';\nimport { Suspense } from 'react';\nimport { useQuery, gql } from '@urql/next"
},
{
"path": "examples/with-next/app/page.tsx",
"chars": 925,
"preview": "import Link from 'next/link';\nimport { cacheExchange, createClient, fetchExchange, gql } from '@urql/core';\nimport { reg"
},
{
"path": "examples/with-next/next-env.d.ts",
"chars": 201,
"preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edite"
},
{
"path": "examples/with-next/package.json",
"chars": 416,
"preview": "{\n \"name\": \"with-next\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"dependencies\": {\n \"@urql/core\": \"^6.0.1\",\n \"@"
},
{
"path": "examples/with-next/tsconfig.json",
"chars": 575,
"preview": "{\n \"compilerOptions\": {\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n "
},
{
"path": "examples/with-pagination/README.md",
"chars": 1343,
"preview": "# With Pagination (in React)\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-pag"
},
{
"path": "examples/with-pagination/index.html",
"chars": 766,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-pagination/package.json",
"chars": 352,
"preview": "{\n \"name\": \"with-pagination\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n \"depen"
},
{
"path": "examples/with-pagination/src/App.jsx",
"chars": 422,
"preview": "import React from 'react';\nimport { Client, Provider, cacheExchange, fetchExchange } from 'urql';\n\nimport PaginatedNpmSe"
},
{
"path": "examples/with-pagination/src/PaginatedNpmSearch.jsx",
"chars": 1782,
"preview": "import React, { useState } from 'react';\nimport { gql, useQuery } from 'urql';\n\nconst limit = 5;\nconst query = 'graphql'"
},
{
"path": "examples/with-pagination/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-pagination/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-react/README.md",
"chars": 1044,
"preview": "# With React\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-react\">\n <img\n "
},
{
"path": "examples/with-react/index.html",
"chars": 761,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-react/package.json",
"chars": 347,
"preview": "{\n \"name\": \"with-react\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n \"dependenci"
},
{
"path": "examples/with-react/src/App.jsx",
"chars": 405,
"preview": "import React from 'react';\nimport { Client, Provider, cacheExchange, fetchExchange } from 'urql';\n\nimport PokemonList fr"
},
{
"path": "examples/with-react/src/PokemonList.jsx",
"chars": 623,
"preview": "import React from 'react';\nimport { gql, useQuery } from 'urql';\n\nconst POKEMONS_QUERY = gql`\n query Pokemons {\n pok"
},
{
"path": "examples/with-react/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-react/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-react-native/App.js",
"chars": 421,
"preview": "import React from 'react';\nimport { Client, Provider, cacheExchange, fetchExchange } from 'urql';\n\nimport PokemonList fr"
},
{
"path": "examples/with-react-native/README.md",
"chars": 947,
"preview": "# With React Native\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-react-native"
},
{
"path": "examples/with-react-native/app.json",
"chars": 68,
"preview": "{\n \"name\": \"withReactNative\",\n \"displayName\": \"withReactNative\"\n}\n"
},
{
"path": "examples/with-react-native/index.js",
"chars": 187,
"preview": "/**\n * @format\n */\n\nimport { AppRegistry } from 'react-native';\nimport App from './App';\nimport { name as appName } from"
},
{
"path": "examples/with-react-native/package.json",
"chars": 536,
"preview": "{\n \"name\": \"with-react-native\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"android\": \"react-native ru"
},
{
"path": "examples/with-react-native/src/screens/PokemonList.js",
"chars": 1121,
"preview": "import React from 'react';\nimport { SafeAreaView, StyleSheet, Text, FlatList, View } from 'react-native';\nimport { gql, "
},
{
"path": "examples/with-refresh-auth/README.md",
"chars": 1612,
"preview": "# With Refresh Authentication\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-re"
},
{
"path": "examples/with-refresh-auth/index.html",
"chars": 768,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-refresh-auth/package.json",
"chars": 391,
"preview": "{\n \"name\": \"with-refresh-auth\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n \"dep"
},
{
"path": "examples/with-refresh-auth/src/App.jsx",
"chars": 732,
"preview": "import React, { useState, useEffect } from 'react';\nimport { Provider } from 'urql';\n\nimport client from './client';\n\nim"
},
{
"path": "examples/with-refresh-auth/src/authStore.js",
"chars": 467,
"preview": "const TOKEN_KEY = 'token';\nconst REFRESH_TOKEN_KEY = 'refresh_token';\n\nexport const saveAuthData = ({ token, refreshToke"
},
{
"path": "examples/with-refresh-auth/src/client.js",
"chars": 2437,
"preview": "import { Client, fetchExchange, cacheExchange, gql } from 'urql';\nimport { authExchange } from '@urql/exchange-auth';\n\ni"
},
{
"path": "examples/with-refresh-auth/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-refresh-auth/src/pages/LoginForm.jsx",
"chars": 2638,
"preview": "import React from 'react';\nimport { gql, useMutation } from 'urql';\n\nconst LOGIN_MUTATION = gql`\n mutation Login($input"
},
{
"path": "examples/with-refresh-auth/src/pages/Profile.jsx",
"chars": 616,
"preview": "import React from 'react';\nimport { gql, useQuery } from 'urql';\n\nconst PROFILE_QUERY = gql`\n query Profile {\n me {\n"
},
{
"path": "examples/with-refresh-auth/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-retry/README.md",
"chars": 1846,
"preview": "# Integrating `@urql/exchange-retry`’s retryExchange\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tre"
},
{
"path": "examples/with-retry/index.html",
"chars": 761,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-retry/package.json",
"chars": 385,
"preview": "{\n \"name\": \"with-retry\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\"\n },\n \"dependenci"
},
{
"path": "examples/with-retry/src/App.jsx",
"chars": 766,
"preview": "import React from 'react';\nimport { Client, fetchExchange, Provider } from 'urql';\nimport { retryExchange } from '@urql/"
},
{
"path": "examples/with-retry/src/Color.jsx",
"chars": 802,
"preview": "import React from 'react';\nimport { gql, useQuery } from 'urql';\n\nconst RANDOM_COLOR_QUERY = gql`\n query RandomColor {\n"
},
{
"path": "examples/with-retry/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-retry/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-solid/.eslintrc.js",
"chars": 77,
"preview": "module.exports = {\n rules: {\n 'react/react-in-jsx-scope': 'off',\n },\n};\n"
},
{
"path": "examples/with-solid/README.md",
"chars": 605,
"preview": "# URQL with Solid\n\nThis example demonstrates how to use URQL with Solid.js.\n\n## Features\n\n- Basic query with `createQuer"
},
{
"path": "examples/with-solid/index.html",
"chars": 303,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-solid/package.json",
"chars": 355,
"preview": "{\n \"name\": \"with-solid\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\",\n \"build\": \"vit"
},
{
"path": "examples/with-solid/src/App.jsx",
"chars": 434,
"preview": "/** @jsxImportSource solid-js */\nimport {\n createClient,\n Provider,\n cacheExchange,\n fetchExchange,\n} from '@urql/so"
},
{
"path": "examples/with-solid/src/PokemonList.jsx",
"chars": 647,
"preview": "/** @jsxImportSource solid-js */\nimport { Suspense, For } from 'solid-js';\nimport { gql } from '@urql/core';\nimport { cr"
},
{
"path": "examples/with-solid/src/index.jsx",
"chars": 154,
"preview": "/** @jsxImportSource solid-js */\nimport { render } from 'solid-js/web';\nimport App from './App';\n\nrender(() => <App />, "
},
{
"path": "examples/with-solid/vite.config.js",
"chars": 133,
"preview": "import { defineConfig } from 'vite';\nimport solid from 'vite-plugin-solid';\n\nexport default defineConfig({\n plugins: [s"
},
{
"path": "examples/with-solid-start/.gitignore",
"chars": 28,
"preview": "/.output\n/.vinxi\n\n.DS_Store\n"
},
{
"path": "examples/with-solid-start/README.md",
"chars": 1180,
"preview": "# URQL with SolidStart\n\nThis example demonstrates how to use URQL with SolidStart.\n\n## Features\n\n- Basic query with `cre"
},
{
"path": "examples/with-solid-start/app.config.ts",
"chars": 88,
"preview": "import { defineConfig } from '@solidjs/start/config';\n\nexport default defineConfig({});\n"
},
{
"path": "examples/with-solid-start/package.json",
"chars": 482,
"preview": "{\n \"name\": \"with-solid-start\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"start\":"
},
{
"path": "examples/with-solid-start/src/app.tsx",
"chars": 644,
"preview": "import { Router, action, query } from '@solidjs/router';\nimport { FileRoutes } from '@solidjs/start/router';\nimport { Su"
},
{
"path": "examples/with-solid-start/src/entry-client.tsx",
"chars": 143,
"preview": "// @refresh reload\nimport { mount, StartClient } from '@solidjs/start/client';\n\nmount(() => <StartClient />, document.ge"
},
{
"path": "examples/with-solid-start/src/entry-server.tsx",
"chars": 548,
"preview": "// @refresh reload\nimport { createHandler, StartServer } from '@solidjs/start/server';\n\nexport default createHandler(() "
},
{
"path": "examples/with-solid-start/src/routes/index.tsx",
"chars": 3023,
"preview": "import { Suspense, For, Show, createSignal } from 'solid-js';\nimport { createAsync, useAction, useSubmission } from '@so"
},
{
"path": "examples/with-solid-start/tsconfig.json",
"chars": 356,
"preview": "{\n \"compilerOptions\": {\n \"jsx\": \"preserve\",\n \"jsxImportSource\": \"solid-js\",\n \"module\": \"ESNext\",\n \"moduleRe"
},
{
"path": "examples/with-subscriptions-via-fetch/README.md",
"chars": 1412,
"preview": "# With Subscriptions via Fetch\n\n<p>\n <a\n href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with"
},
{
"path": "examples/with-subscriptions-via-fetch/index.html",
"chars": 779,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-subscriptions-via-fetch/package.json",
"chars": 549,
"preview": "{\n \"name\": \"with-subscriptions-via-fetch\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"server\": \"node "
},
{
"path": "examples/with-subscriptions-via-fetch/server/graphql-yoga.js",
"chars": 224,
"preview": "const { createYoga } = require('graphql-yoga');\nconst { createServer } = require('http');\nconst { schema } = require('./"
},
{
"path": "examples/with-subscriptions-via-fetch/server/schema.js",
"chars": 993,
"preview": "const {\n GraphQLList,\n GraphQLObjectType,\n GraphQLSchema,\n GraphQLString,\n} = require('graphql');\n\nconst Alphabet = "
},
{
"path": "examples/with-subscriptions-via-fetch/src/App.jsx",
"chars": 744,
"preview": "import React from 'react';\nimport { Client, Provider, fetchExchange } from 'urql';\n\nimport { cacheExchange } from '@urql"
},
{
"path": "examples/with-subscriptions-via-fetch/src/Songs.jsx",
"chars": 966,
"preview": "import React from 'react';\nimport { gql, useQuery, useSubscription } from 'urql';\n\nconst LIST_QUERY = gql`\n query List_"
},
{
"path": "examples/with-subscriptions-via-fetch/src/index.jsx",
"chars": 162,
"preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport App from './App';\n\ncreateRoot(document"
},
{
"path": "examples/with-subscriptions-via-fetch/vite.config.js",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/with-svelte/.gitignore",
"chars": 41,
"preview": "/node_modules/\n/public/build/\n\n.DS_Store\n"
},
{
"path": "examples/with-svelte/README.md",
"chars": 1054,
"preview": "# With Svelte\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-svelte\">\n <img\n"
},
{
"path": "examples/with-svelte/index.html",
"chars": 296,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-svelte/package.json",
"chars": 392,
"preview": "{\n \"name\": \"with-svelte\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"scripts\": {\n \"start\": \"vite\",\n \"build\": \"vi"
},
{
"path": "examples/with-svelte/src/App.svelte",
"chars": 326,
"preview": "<script>\n import { setContextClient, Client, cacheExchange, fetchExchange } from \"@urql/svelte\";\n import PokemonList f"
},
{
"path": "examples/with-svelte/src/PokemonList.svelte",
"chars": 864,
"preview": "<script>\n import { getContextClient, gql, queryStore } from \"@urql/svelte\";\n\n let skip = 0;\n $: pokemons = queryStore"
},
{
"path": "examples/with-svelte/src/main.js",
"chars": 103,
"preview": "import App from './App.svelte';\n\nvar app = new App({\n target: document.body,\n});\n\nexport default app;\n"
},
{
"path": "examples/with-svelte/vite.config.mjs",
"chars": 180,
"preview": "import { defineConfig } from 'vite';\nimport { svelte } from '@sveltejs/vite-plugin-svelte';\n\n// https://vitejs.dev/confi"
},
{
"path": "examples/with-vue3/.gitignore",
"chars": 35,
"preview": "node_modules\n.DS_Store\ndist\n*.local"
},
{
"path": "examples/with-vue3/README.md",
"chars": 1109,
"preview": "# With Vue 3\n\n<p>\n <a href=\"https://stackblitz.com/github/urql-graphql/urql/tree/main/examples/with-vue3\">\n <img\n "
},
{
"path": "examples/with-vue3/index.html",
"chars": 293,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/with-vue3/package.json",
"chars": 375,
"preview": "{\n \"name\": \"with-vue3\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"vite\",\n \"build\": \"vite"
}
]
// ... and 471 more files (download for full content)
About this extraction
This page contains the full source code of the urql-graphql/urql GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 671 files (2.5 MB), approximately 682.2k tokens, and a symbol index with 524 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.