How to Use TypeScript with Svelte
In this post, we’ll explore getting set up with TypeScript in a SvelteKit project.
But first, why would you use TypeScript in a project? TypeScript is a typed superset of JavaScript that compiles it down to JavaScript.
As the name implies, TypeScript allows for type safety in your code, TypeScript doesn't stop you from writing bugs, though, it helps with identifying type errors. You can still write logical errors, like:
function add(a: number, b: number) {return a - b}
But it does allow you to catch type errors at compile time rather than run-time, let’s check out the TypeScript example from the last example and remove the types and correct the logic error, without the type safety of TypeScript we can add anything to the function and JavaScript thanks to type coercion will run it:
function add(a, b) {return a + b}add(“yo”, “you”)// result yoyou
This will be two projects:
Example using graphql-request
- Generate types with GraphQL code gen
- How to use the types with SvelteKit
- Store data locally in a Svelte store
2.Same as the basic example but using KitQL
- It does all the other points for you!
To follow along with this post, you’ll need the following:
- A SvelteKit project npm init svelte to scaffold out a new project or node.new/sveltekit in a browser
- A GraphQL endpoint to query against, you can get started with this Graphflix project if you want
Using graphql-requestAnchor
Start by installing the dependencies for graphql-request
and graphql
pnpm i graphql-request graphql
Then install the GraphQL code gen packages as developer dependencies with your package manager of choice, I’ll be using pnpm in the examples you can use npm or yarn if you please.
pnpm i -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node @graphql-typed-document-node/core
Once the dependencies are installed create a codegen.yml
file at the root of the project:
schema: <https://api-eu.hygraph.com/v2/project-id/master>documents:- '**/*.{graphql,gql}'generates:src/lib/graphql/types.ts:plugins:- typescript- typescript-operations- typed-document-node
The codegen.yml
file is telling the GraphQL code generator to generate the types.ts file in the src/lib/graphql
folder. It will run the typescript, typescript-operations, and typed-document-node against the schema for anything matching the documents matching **/*.{graphql.gql}
.
Create a GraphQL query, this can be at the root of the project or in a file structure that makes sense to you, as the types.ts file is being output to src/lib/graphql In the examples I’ll create the file in there so it’s co-located with the types.
Create a simple query to get all the episodes from your Graphlix API endpoint.
query GetEpisodes {episodes {titlelengthvideo {id}actors {name}show {title}}}
Create a script in the package.json to run graphql-codegen
:
"generate": "graphql-codegen",
Run the generate script:
pnpm run generate
Now check out the generated file, this will contain GetEpisodesDocument which can be used in a graphql-request client instead of a GraphQL query.
In the src/routes/index.svelte
to validate the types create a call to the GraphQL endpoint by creating a new graphql-request
client.
Note: This can be abstracted out into a shadow endpoint, for demonstration purposes, all the code is co-located.
<script context="module" lang="ts">import {GetEpisodesDocument,type Episode,type GetEpisodesQuery,type GetEpisodesQueryVariables,} from '$lib/graphql/types'import { GraphQLClient } from 'graphql-request'export const load = async () => {const client = new GraphQLClient('<https://api-eu.hygraph.com/v2/project-id/master>')const { episodes } = await client.request<GetEpisodesQuery,GetEpisodesQueryVariables>(GetEpisodesDocument)return {props: {episodes,},}}</script><script lang="ts">export let episodes: Episode[]</script><pre>{JSON.stringify(episodes, null, 2)}</pre>
With the client, you can use the TypeScript generic <>
passing in the GetEpisodesQuery
and the GetEpisodesQueryVariables
, for the client query. There’s no need to enter a GraphQL in template literals, and can instead use the type document node of GetEpisodesDocument
.
Taking this a step further, the data queried from the endpoint can be stored locally, so there are not as many round trips to the endpoint for the data.
This can be done by utilizing Svelte stores, a Svelte store is a writable object with subscribe method that will allow any part of the project to get updated data when the store is updated.
Create a Svelte stores folder in src, then in the folder, create an episodes.ts
file, in the file, add the following:
import type { GetEpisodesQuery } from '$lib/graphql/types'import { writable } from 'svelte/store'export const episodesStore = writable([] as GetEpisodesQuery['episodes'])
This creates a Svelte writable, exported as episodeStore
, and assigns the type of episodes in the GetEpisodesQuery type from the GraphQL Code Generator types file.
In the load function of the src/routes/index.svelte
file change the load function to update the episodeStore. There’s now no need to return the props for the page to use as the episodesStore can be used to access the data.
export const load = async () => {const client = new GraphQLClient('<https://api-eu.hygraph.com/v2/project-id/master>')const { episodes } = await client.request<GetEpisodesQuery,GetEpisodesQueryVariables>(GetEpisodesDocument)episodesStore.set(episodes)return {}}
In the body of the index page now, there are no props being imported; instead, the episodesStore is being accessed with the $ subscribing to any changes.
<script lang="ts">// export let episodes: Episode[];</script><pre>{JSON.stringify($episodesStore, null, 2)}</pre>
The data doesn’t need to be loaded on route changes and can be accessed on the store with the $ to subscribe for changes.
Using KitQLAnchor
Ok, now to KitQL, which is a set of tools to help build in an efficient and fast way. KitQL comes with a lot of the previous GraphQL Code Generator plugins. To get set up, scaffold a new SvelteKit project with the npm init svelte command.
Install dependencies:
pnpm @kitql/all-in graphql
Then create a .graphqlrc.yaml
file at the root of the project and add the following config:
projects:default:schema: '<https://api-eu.hygraph.com/v2/project-id/master>'documents:- '**/*.{graphql,gql}'extensions:endpoints:default:url: '<https://api-eu.hygraph.com/v2/project-id/master>'codegen:generates:./src/lib/graphql/_kitql/graphqlTypes.ts:plugins:- typescript- typescript-operations- typed-document-node- typescript-document-nodes./src/lib/graphql/_kitql/graphqlStores.ts:plugins:- '@kitql/graphql-codegen'config:importBaseTypesFrom: $lib/graphql/_kitql/graphqlTypesconfig:useTypeImports: true
You can see from the codegen config here that it is very similar to the graphql-request
example in the previous section.
This time around, in the package.json scripts, add a config for the .graphqlrc.yaml
file:
"generate": "graphql-codegen --config ./.graphqlrc.yaml",
Now creating the same query as in the previous example to query for the episodes in the Hygraph Graphflix API so the generate script can pick it up and run the plugins against it.
query GetEpisodes {episodes {titlelengthvideo {id}actors {name}show {title}}}
The file can go in the project's root for now, as running the generated script will create the folder structure when it’s run. It can then be moved into the src/lib/graphql
folder.
Now the folder structure has been generated add a kitQLClient.ts
file to /src/lib/graphql
and add the following config:
import { KitQLClient } from '@kitql/client'export const kitQLClient = new KitQLClient({url: '<https://api-eu.hygraph.com/v2/project-id/master>',credentials: 'omit',headersContentType: 'application/json',logType: ['client', 'server', 'operationAndvariables'],})Now in the src/routes/index.svelte file add the following:<script context="module" lang="ts">import { KQL_GetEpisodes } from '$lib/graphql/_kitql/graphqlStores'export const load = async ({ fetch }) => {await KQL_GetEpisodes.queryLoad({ fetch })return {}}</script><script lang="ts">let episodes = $KQL_GetEpisodes.data?.episodes</script><pre>{JSON.stringify(episodes, null, 2)}</pre>
KitQL will generate all the stores and types needed for the get episodes query. In the example, you can see that an episodes variables were created but the KitQL store can be used inline and use the store directly:
<pre>{JSON.stringify($KQL_GetEpisodes.data?.episodes, null, 2)}</pre>
That’s it, we’ve gone through a couple of ways to use TypeScript with GraphQL in your SvelteKit projects.