Skip to content

Instantly share code, notes, and snippets.

@sastan
Last active January 15, 2023 00:15
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sastan/85cf3d011b152a80d3a5933a09df21f6 to your computer and use it in GitHub Desktop.
Save sastan/85cf3d011b152a80d3a5933a09df21f6 to your computer and use it in GitHub Desktop.
sveltekit + urql (extended version)
<script context="module">
import { get, readable } from 'svelte/store'
import { createClient, operationStore } from '@urql/svelte'
import { browser, dev } from '$app/env'
/**
* @type {import('@sveltejs/kit').Load}
*/
export async function load({ fetch, context }) {
const client = await createClient({
// Pass in the fetch from sveltekit to have access to serialized requests during hydration
fetch,
dev: browser && dev,
})
return {
context: {
...context,
client,
// Works just like query from @urql/svelte
query: async (query, variables, context, normalize) => {
const store = operationStore(query, variables, context)
const result = await client.query(store.query, store.variables, store.context).toPromise()
Object.assign(get(store), result)
// Allow to access deep nested object directly at data
if (normalize) {
const { subscribe } = store
return Object.create(store, {
subscribe: {
enumerable: true,
value: readable(store, (set) => {
const unsubscribe = subscribe((result) => {
if (result.data) {
Object.assign(result.data, normalize(result.data, result))
}
set(result)
})
return unsubscribe
}).subscribe,
},
})
}
return store
},
},
props: { client }
}
}
</script>
<script>
import { setClient } from '@urql/svelte'
/**
* @type {import('@urql/svelte').Client}
*/
export let client
setClient(client)
</script>
<slot />
query GetTodos {
viewer {
todos {
id
title
}
}
}
overwrite: true
schema: 'http://localhost:5000/-/graphql'
documents: 'src/**/*.graphql'
generates:
./graphql.schema.json:
plugins:
- introspection
src/lib/graphql.ts:
plugins:
- add:
content:
- '// THIS FILE IS GENERATED, DO NOT EDIT!'
- '/* eslint-disable */'
- typescript
src/:
preset: near-operation-file
presetConfig:
baseTypesPath: ~$lib/graphql
extension: .gql.ts
plugins:
- add:
content:
- '// THIS FILE IS GENERATED, DO NOT EDIT!'
- '/* eslint-disable */'
- typescript-operations
- typed-document-node
config:
# addOperationExport: true
flattenGeneratedTypes: true
config:
# use "import type" instead of "import"
useTypeImports: true
# turn enums into types
enumsAsTypes: true
omitOperationSuffix: true
dedupeOperationSuffix: true
exportFragmentSpreadSubTypes: true
experimentalFragmentVariables: true
addUnderscoreToArgsType: true
# onlyOperationTypes: true
preResolveTypes: true
namingConvention: keep
scalars:
UnsignedInt: number
URL: string
JSON: any # string | number | boolean | null | Array<Scalars['JSON']> | Scalars['JSONObject']
JSONObject: Record<string, any>
Date: string
DateTime: string
hooks:
afterAllFileWrite:
- prettier --write
<script context="module">
// Generated with @graphql-codegen/cli
import * as gql from '_index.gql'
export async function load({ context }) {
return {
props: {
todos: await context.query(
gql.GetTodosDocument,
{ /* variables */},
{ additionalTypenames: [ /* ... */ ] },
// Result merged in to data -> todos.data.todos
(data) => data.viewer
),
},
}
}
</script>
<script>
import { query } from '@urql/svelte'
/**
* @type {import('./types').OperationStore<gql.GetTodosDocument, gql.GetTodos['viewer']['todos']>}
*/
export let todos
// Setup subscription for the lifetime of the component
query(todos)
// Data is normalized
assert($todos.data.viewer.todos === $todos.data.todos)
</script>
{
"// Only fields relevant for example included": "",
"scripts": {
"graphql": "graphql-codegen --config codegen.yml"
},
"devDependencies": {
"@graphql-codegen/add": "^2.0.2",
"@graphql-codegen/cli": "1.21.3",
"@graphql-codegen/introspection": "1.18.1",
"@graphql-codegen/near-operation-file-preset": "^1.17.13",
"@graphql-codegen/typed-document-node": "^1.18.4",
"@graphql-codegen/typescript": "^1.21.1",
"@graphql-codegen/typescript-operations": "^1.17.15",
"typescript": "^4.0.0"
},
"type": "module",
"engines": {
"node": ">= 12.17.0"
},
"dependencies": {
"@urql/svelte": "^1.2.0",
"graphql": "^15.5.0"
}
}
import type * as urql from '@urql/svelte'
import type { TypedDocumentNode } from '@graphql-typed-document-node/core'
export type OperationStore<
T extends TypedDocumentNode = any,
Normalized = object
> = T extends TypedDocumentNode<infer Data, infer Vars>
? urql.OperationStore<Data & Normalized, Vars>
: never
@rogueyoshi
Copy link

in the component do you still want to check for $todos.fetching and $todos.error with this solution?

@sastan
Copy link
Author

sastan commented Dec 18, 2021

Everything should be fetched -> no for $todos.fetching . But it could have an error -> yes to $todos.error

@thenbe
Copy link

thenbe commented Dec 26, 2021

Thanks for this gist.

Using this pattern, how would I hide the network requests in the browser's devtools? There is an authorization header in there and I'm not sure how to go about safely hiding it. The sveltekit docs say to use an endpoint (file ending with .json.ts), but how do I use it with urql? I'm not sure what question/concept to google. What am I missing?

@rogueyoshi
Copy link

@ambiguous48 this solution is actually a workaround for a svelte limitation that's about to be addressed sveltejs/kit#2979

@souravjamwal77
Copy link

Hi Everyone, I have a graphql backend from which I can get the token, refreshToken using mutation but how do I pass that to each request query?
How do I persist session and how exactly does login/authentication flow will work for a sveltekit app? Please, if anyone has any ideas do let me know.

@kakashy
Copy link

kakashy commented Apr 15, 2022

Hey all,
context as a load parameter was renamed to stuff, see this SK issue.

An updated gist can be found here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment