{"id":13796493,"url":"https://github.com/DustinJSilk/qwik-urql","last_synced_at":"2025-05-13T00:30:53.280Z","repository":{"id":61113049,"uuid":"548302342","full_name":"DustinJSilk/qwik-urql","owner":"DustinJSilk","description":"Urql support for Qwik projects.","archived":true,"fork":false,"pushed_at":"2023-03-16T19:55:40.000Z","size":523,"stargazers_count":20,"open_issues_count":16,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-02T06:08:06.448Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DustinJSilk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-10-09T09:38:44.000Z","updated_at":"2024-09-27T18:17:22.000Z","dependencies_parsed_at":"2024-01-07T07:11:09.630Z","dependency_job_id":"ce1f53d7-1b20-4777-a0e7-d868b2a600a7","html_url":"https://github.com/DustinJSilk/qwik-urql","commit_stats":{"total_commits":106,"total_committers":5,"mean_commits":21.2,"dds":0.7075471698113207,"last_synced_commit":"b0b79d51ff086873a9c8d7cbbcdc726864db97fb"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DustinJSilk%2Fqwik-urql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DustinJSilk%2Fqwik-urql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DustinJSilk%2Fqwik-urql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DustinJSilk%2Fqwik-urql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DustinJSilk","download_url":"https://codeload.github.com/DustinJSilk/qwik-urql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225159867,"owners_count":17430199,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T23:01:11.013Z","updated_at":"2024-11-18T10:31:24.845Z","avatar_url":"https://github.com/DustinJSilk.png","language":"TypeScript","funding_links":[],"categories":["Libraries"],"sub_categories":[],"readme":"\n## Archive notice\n\n```\nThis project is being shutdown and archived. Since I started working with Qwik, I've realised that Qwik already solves\nmany of the same problems that GraphQL solves, and in a far better way. It's also proven to be near impossible to\nget a GraphQL library working properly with Qwik without sacraficing functionality from both Qwik and the GQL library.\nQwiks API has changed a lot since this project started, and I cant maintain it when I don't use nor recommend it.\n\nFor GraphQL servers written in other languages, I recommend switching to protocol buffers and exposing the data through\nQwiks native loaders, actions, and endpoint. GraphQL was fun while it lasted.\n```\n\n# Qwik Urql ⚡️\n\nA small library to use Urql with Qwik.\n\n- :white_check_mark: Query \u0026 mutation hooks\n- :white_check_mark: SSR\n- :white_check_mark: Lazy loaded client\n- :white_check_mark: Auth tokens\n- :white_check_mark: Abort signals\n- :white_check_mark: Re-execute queries (see example app buttons)\n- :white_check_mark: Reactive cache / watch for changes\n- :white_check_mark::eight_pointed_black_star: Optimistic response. Works except for the first optimistic update after SSR.\n- :hourglass: Code generators\n\n## Setup\n\nThis is the minimal setup required for standard Query/Mutations. [See the reactive cache section for watch queries](#reactive-cache)\n\nCreate a new file to hold your Urql client configuration under `src/client.ts` and export a factory for your client.\n\n```TypeScript\nimport { createClient } from '@urql/core';\n\nexport const clientFactory = () =\u003e {\n  return createClient({\n    url: 'http://localhost:3000/graphql',\n    exchanges: [/** ... */],\n  });\n};\n```\n\nNow provide the client in your root.tsx component and wrap the client in a QRL to ensure it is lazy loaded.\n\n```TypeScript\nimport { $, component$ } from '@builder.io/qwik';\nimport { clientFactory } from './client';\n\nexport default component$(() =\u003e {\n  return (\n    \u003cUrqlProvider client={$(clientFactory)}\u003e\n      \u003cQwikCity\u003e\n        \u003chead\u003e\u003c/head\u003e\n        \u003cbody lang='en'\u003e\n          ...\n        \u003c/body\u003e\n      \u003c/QwikCity\u003e\n    \u003c/UrqlProvider\u003e\n  );\n});\n```\n\n## Queries\n\nFirst compile the GQL and then call `useQuery`. The result is a Qwik\nResourceReturn which can be used with the `\u003cResource /\u003e` component.\n\n```TypeScript\nimport { component$, JSXNode, Resource, $ } from '@builder.io/qwik';\nimport { gql, OperationResult } from '@urql/core';\nimport { useQuery } from 'qwik-urql';\n\n// Create a GQL query. This will not get serialized because it is only\n// referenced in a QRL\nexport const query = gql`\n  query Item($id: String!) {\n    item(id: $id) {\n      id\n      title\n    }\n  }\n`;\n\n// Using a QRL we can drastically reduce the initial bundle size.\nexport const Query = $(() =\u003e query)\n\nexport default component$(() =\u003e {\n  const vars = useStore({ id: '...' })\n  const query = useQuery(Query, vars, { requestPolicy: 'network-only' });\n\n  return \u003cResource\n    value={query}\n    onPending={...}\n    onRejected={...}\n    onResolved={...}\n  /\u003e\n})\n```\n\n## Mutations\n\nThere are 2 hooks for running a mutation.\n\n- The `useMutationResource` works the exact same as `useQuery`. It will trigger\n  as soon as the component loads. You can then re-trigger it by changing the\n  input store.\n- The `useMutation` returns a store that includes the `data`, `errors`,\n  `loading` state, and a method to execute the mutation `mutate$`. This allows\n  you to delay the execution of the request until a user interaction happens.\n\n```TypeScript\nexport const mutation = gql`\n  mutation UpdateItem($id: String!, $title: String!) {\n    item(id: $id, title: $title) {\n      id\n      title\n    }\n  }\n`;\n\nexport const Mutation = $(() =\u003e mutation)\n\nexport default component$(() =\u003e {\n  // You can pass in variables during initialisation or execution\n  const initialVars = useStore({ id: '...' })\n  const { data, errors, loading, mutate$ } = useMutation(Mutation, initialVars);\n\n  return \u003c\u003e\n    { loading ? 'loading' : 'done' }\n    \u003cbutton onClick$={() =\u003e mutate$({ title: '...' })}\u003eMutate\u003c/button\u003e\n  \u003c/\u003e\n})\n```\n\n## SSR\n\nQwik doesn't hydrate on the client after SSR. This means we don't need to\nsupport the SSR exchange, everything works without it.\n\n## Reactive cache\n\nInstall the QwikExchange to enable subscriptions.\n\nTo set this up, add the `qwikExchange` to your client and make sure it is before\nthe cache exchange. All queries will be reactive by default.\n\nBy default, all queries will create subscriptions. This can be turned off\nper request by passing in `watch: false` to the query context, or pass in a\nglobal default to the UrqlProvider `options` prop.\n\n```TypeScript\nimport { createClient, dedupExchange, fetchExchange } from '@urql/core';\nimport { cacheExchange } from '@urql/exchange-graphcache';\nimport { qwikExchange, ClientFactory } from 'qwik-urql';\n\nexport const clientFactory: ClientFactory = ({ qwikStore }) =\u003e {\n  return createClient({\n    url: 'http://localhost:3000/graphql',\n    exchanges: [\n      qwikExchange(qwikStore),\n      dedupExchange,\n      cacheExchange({}),\n      fetchExchange,\n    ],\n  });\n};\n```\n\n## Authentication\n\n_Make sure you follow the latest recommendations by Urql._\n\nFirst update your clientFactory to include the Urql auth exchange. Notice the\nfactory now accepts an authTokens parameter which can be used when making your\nrequests.\n\n```TypeScript\nexport const clientFactory: ClientFactory = ({ authTokens }) =\u003e {\n  const auth = authExchange\u003cUrqlAuthTokens\u003e({\n    getAuth: async ({ authState }) =\u003e {\n      if (!authState) {\n        if (authTokens) {\n          return authTokens;\n        }\n\n        return null;\n      }\n\n      return null;\n    },\n    willAuthError: ({ authState }) =\u003e {\n      if (!authState) return true;\n      return false;\n    },\n    addAuthToOperation: ({ authState, operation }) =\u003e {\n      if (!authState || !authState.token) {\n        return operation;\n      }\n\n      const fetchOptions =\n        typeof operation.context.fetchOptions === 'function'\n          ? operation.context.fetchOptions()\n          : operation.context.fetchOptions || {};\n\n      return makeOperation(operation.kind, operation, {\n        ...operation.context,\n        fetchOptions: {\n          ...fetchOptions,\n          headers: {\n            ...fetchOptions.headers,\n            Authorization: authState.token,\n          },\n        },\n      });\n    },\n    didAuthError: ({ error }) =\u003e {\n      return error.graphQLErrors.some(\n        (e) =\u003e e.extensions?.code === 'FORBIDDEN'\n      );\n    },\n  });\n\n  return createClient({\n    url: 'http://localhost:3000/graphql',\n    exchanges: [dedupExchange, cacheExchange({}), auth, fetchExchange],\n  });\n};\n```\n\nAuthentication has to use cookies to allow authenticated SSR. To do this, you\nwill need to set a cookie after your user has logged in. This cookie then\nneeds to be read from the request headers and saved to a Qwik store. (I'll\ninclude an example of this with firebase soon)\n\nTo inject your auth tokens into the clientFactory, you need to provide them in\nyour `root.tsx`:\n\n```TypeScript\nimport { UrqlProvider } from 'qwik-urql';\n\nexport default component$(() =\u003e {\n  // Get access to your authentication tokens\n  const session = useCookie('session');\n\n  // Add them to a store\n  const authState = useStore({ token: session  });\n\n  return (\n    // Provide them to your entire app\n    \u003cUrqlProvider auth={authState} client={$(clientFactory)}\u003e\n      \u003cQwikCity\u003e\n        \u003chead\u003e\n          \u003cmeta charSet='utf-8' /\u003e\n          \u003cRouterHead /\u003e\n        \u003c/head\u003e\n        \u003cbody lang='en'\u003e\n          \u003cRouterOutlet /\u003e\n          \u003cServiceWorkerRegister /\u003e\n        \u003c/body\u003e\n      \u003c/QwikCity\u003e\n    \u003c/UrqlProvider\u003e\n  );\n});\n```\n\nYou should now receive auth tokens in your GQL server from both the frontend\nclient and from SSR clients.\n\n## Code generation\n\n#### **Coming soon.**\n\nI plan to create a code generate to convert `.graphql` files like this:\n\n```GraphQL\nquery Film($id: String!) {\n  film(id: $id) {\n    id\n    title\n  }\n}\n```\n\nInto something like this:\n\n```TypeScript\nimport { component$, JSXNode, Resource, $ } from '@builder.io/qwik';\nimport { gql, OperationResult } from '@urql/core';\nimport { useQuery } from 'qwik-urql';\n\nexport type FilmQueryResponse = {\n  film: {\n    title: string;\n    id: string;\n  };\n};\n\nexport type FilmQueryVars = {\n  id: string;\n};\n\nexport const filmQuery = gql`\n  query Film($id: String!) {\n    film(id: $id) {\n      id\n      title\n    }\n  }\n`;\n\nexport const FilmQuery = $(() =\u003e filmQuery)\n\nexport const useFilmQuery = (vars: FilmQueryVars) =\u003e {\n  return useQuery(FilmQuery, vars);\n};\n\nexport type FilmResourceProps = {\n  vars: FilmQueryVars;\n  onResolved$: (\n    value: OperationResult\u003cFilmQueryResponse, FilmQueryVars\u003e\n  ) =\u003e JSXNode;\n  onPending$?: () =\u003e JSXNode;\n  onRejected$?: (reason: any) =\u003e JSXNode;\n};\n\nexport const FilmResource = component$((props: FilmResourceProps) =\u003e {\n  const vars = props.vars;\n  const value = useFilmQuery(vars);\n\n  return (\n    \u003cResource\n      value={value}\n      onPending={props.onPending$}\n      onRejected={props.onRejected$}\n      onResolved={props.onResolved$}\n    /\u003e\n  );\n});\n```\n\nAnd then in your component all you need to import is this:\n\n```TypeScript\nconst vars = useStore({ id: '0' });\n\nreturn \u003cFilmResource\n  vars={vars}\n  onPending$={() =\u003e \u003cdiv\u003eLoading...\u003c/div\u003e}\n  onRejected$={() =\u003e \u003cdiv\u003eError\u003c/div\u003e}\n  onResolved$={(res) =\u003e (\n    \u003c\u003e{res.data ? res.data.film.title : 'No results'}\u003c/\u003e\n  )}\n/\u003e\n```\n\n## Example app\n\n**The example requires [this PR](https://github.com/BuilderIO/qwik/pull/1594) for authentication to work. To test\nauthentication you will need to build it yourself and update your node_modules\nuntil it is merged**\n\nAn example app is included in the repository.\nThe source code is found in `src/example`\n\n`pnpm start`\n\n## Development\n\nDevelopment mode uses [Vite's development server](https://vitejs.dev/). For Qwik during development, the `dev` command will also server-side render (SSR) the output. The client-side development modules loaded by the browser.\n\n```\nnpm run dev\n```\n\n\u003e Note: during dev mode, Vite will request many JS files, which does not represent a Qwik production build.\n\n## Production\n\nThe production build should generate the production build of your component library in (./lib) and the typescript type definitions in (./lib-types).\n\n```\nnpm run build\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDustinJSilk%2Fqwik-urql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDustinJSilk%2Fqwik-urql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDustinJSilk%2Fqwik-urql/lists"}