Skip Navigation

Scott Spence

Loading...

GraphQL Code Generator with SvelteKit and TypeScript Example

6 min read

I’ve started using TypeScript on every new project I start with Svelte. My buddy Jamie Barton has helped me out with getting set up with the GraphQL Code Generator so that data is typed in the project.

This is a just so you know kind of guide as there is KitQL as an alternitive which will generate all the types you need for your GraphQL schema for use in your SvelteKit projects.

In this guide I’ll be setting up a SvelteKit skeleton project with a GraphCMS backend to demonstrate how to set up with GraphQL Code Generator. I’ll be using the GraphCMS Blog Starter template in the examples here. You can generate your own from the starter section on the app.graphcms.com page or clone the project I’m using with this link.

Setup SvelteKit project

Usual fare with this, use the CLI to create a new project. So from the command line I’ll scaffold out a new SvelteKit skeleton project. One thing to note is that the @next isn’t needed anymore, this doesn’t mean that SvelteKit is at v1 though like I have said in the past.

npm init svelte sveltekit-graphql-codegen

I’ll chose the following options from the CLI prompt, yes to all the things:

✔ Which Svelte app template? › Skeleton project
✔ Use TypeScript? … Yes
✔ Add ESLint for code linting? … Yes
✔ Add Prettier for code formatting? … Yes
✔ Add Playwright for browser testing? … Yes

I’ll change directory (cd) into the newly created project and I’ll install graphql-request and graphql as regular dependencies. I’ve seen peer dependency issues in the past and GraphQL Code Generator flat out doesn’t work if they’re installed as dev dependencies for some reason.

Yes, I’m using pnpm for the installation of the dependencies, you do you though, use npm, yarn, whatever, live your life!

pnpm i graphql-request graphql

I’ll then install the dev dependencies needed, I’ll list them here and you can copy them from the code block below if you need it!

  • @graphql-codegen/cli
  • @graphql-codegen/typescript
  • @graphql-codegen/typescript-operations
  • @graphql-codegen/typed-document-node
  • @graphql-typed-document-node/core
pnpm i -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node @graphql-typed-document-node/core

Setup GraphQL Code Generator

Now that I’ve got all the dependencies I need I’ll need to configure GraphQL Code Generator.

touch codegen.yml .env

I’m also creating a .env file to store the VITE_GRAPHQL_API that’s going to store the endpoint of my GraphQL API as I’m not a fan of hardcoding anything that can be a variable.

In the codegen.yml file I’ll add the following:

schema: ${VITE_GRAPHQL_API}
documents:
  - '**/*.{graphql,gql}'
generates:
  src/lib/graphql/types.ts:
    plugins:
      - typescript
      - typescript-operations
      - typed-document-node

The codegen.yml file here is telling the GraphQL Code Generator to create the types.ts file in the src/lib/graphql folder. It will run the plugins of typescript, typescript-operations and typed-document-node against the schema for any documents (GraphQL files) matching **/*.{graphql,gql}.

Now the GraphQL Code Generator is going to look for a .graphql or .gql file, so I’ll create a all-posts.graphql file that will query for all the posts.

# make the directory
mkdir -p src/lib/graphql
# create the file
touch src/lib/graphql/all-posts.graphql

I’ll add the following to the all-posts.graphql file:

query AllPosts {
  posts {
    title
    slug
    date
    excerpt
    tags
  }
}

In my .env file I’ll add the endpoint of my GraphQL API, I’m using a GraphCMS project.

VITE_GRAPHQL_API=https://api-eu.graphcms.com/v2/project-id/master

I’ll need to let the GraphQL Code Generator know where to get the config for the variable I’m using in the .yml file. I’ll do that with creating a generate script in the package.json file.

I’ll also tack on the generate script at the end of the prepare script so that if there’s any changes to the .graphql files it’ll run the generate script after pnpm install has run.

"scripts": {
  "generate": "graphql-codegen --require dotenv/config",
  "prepare": "svelte-kit sync && pnpm run generate",
}

Ok now I’m ready to go! Run the generate script and I get the following CLI output:

> graphql-codegen --require dotenv/config

  ✔ Parse configuration
  ❯ Generate outputs
  ✔ Parse configuration
  ✔ Generate outputs

Now I can go over to the src/lib/graphql folder and I can see the types.ts file has been generated.

Using the generated types

ℹ️ On opening the types.ts file there might me a lint warning on the @graphql-typed-document-node/core import, this is because TypedDocumentNode needs to be imported as a type:

import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'

What I’m looking for in the types.ts file is the AllPostsDocument, it’s right at the bottom of the file. If we take a look at that here:

export const AllPostsDocument = {
  kind: 'Document',
  definitions: [
    {
      kind: 'OperationDefinition',
      operation: 'query',
      name: { kind: 'Name', value: 'AllPosts' },
      selectionSet: {
        kind: 'SelectionSet',
        selections: [
          {
            kind: 'Field',
            name: { kind: 'Name', value: 'posts' },
            selectionSet: {
              kind: 'SelectionSet',
              selections: [
                {
                  kind: 'Field',
                  name: { kind: 'Name', value: 'title' },
                },
                {
                  kind: 'Field',
                  name: { kind: 'Name', value: 'slug' },
                },
                {
                  kind: 'Field',
                  name: { kind: 'Name', value: 'date' },
                },
                {
                  kind: 'Field',
                  name: { kind: 'Name', value: 'excerpt' },
                },
                {
                  kind: 'Field',
                  name: { kind: 'Name', value: 'tags' },
                },
              ],
            },
          },
        ],
      },
    },
  ],
} as unknown as DocumentNode<AllPostsQuery, AllPostsQueryVariables>

And contrast the selectionSet in the AllPostsDocument export to the query in the all-posts.graphql file:

query AllPosts {
  posts {
    title
    slug
    date
    excerpt
    tags
  }
}

I can see that it’s matching the posts fields in the AllPosts query.

Use types in my code

So in this example I’m going to write out the query to the API in the src/routes/index.svelte file for brevity. This can be abstracted out into other files if you want.

Now if I make a request to the GraphQL API with graphql-request I can use the AllPostsDocument for the query rather than writing out the query in backticks. Something like this:

const client = new GraphQLClient(import.meta.env.VITE_GRAPHQL_API)

const { posts } = await client.request(AllPostsDocument)

The graphql-request can now take the generics for the DocumentNode (AllPostsDocument) query and variables, and I can assign the type of a Post[] array to the destructured property from it like this:

const client = new GraphQLClient(import.meta.env.VITE_GRAPHQL_API)

const { posts } = (await client.request<
  AllPostsQuery,
  AllPostsQueryVariables
>(AllPostsDocument)) as { posts: Post[] }

Pretty neat, now I can return the props from the script context="module" to the page and assign the Post[] type to it:

<script lang="ts">
  export let posts: Post[]
</script>

Here’s what the full src/routes/index.svelte file looks like with the imported types from the types.ts file:

<script context="module" lang="ts">
  import { GraphQLClient } from 'graphql-request'
  import {
    AllPostsDocument,
    type AllPostsQuery,
    type AllPostsQueryVariables,
    type Post,
  } from '../lib/graphql/types'

  export const load = async () => {
    const client = new GraphQLClient(import.meta.env.VITE_GRAPHQL_API)

    const { posts } = (await client.request<
      AllPostsQuery,
      AllPostsQueryVariables
    >(AllPostsDocument)) as { posts: Post[] }

    return { props: { posts } }
  }
</script>

<script lang="ts">
  export let posts: Post[]
</script>

<pre>{JSON.stringify(posts, null, 2)}</pre>

I’m JSON stringifying the posts array in a <pre> tag so that I can see the data is being returned from the API as I expect.

I can now take this further and work with the data being returned from the API by adding it to some markup on the page.

Conclusion

I’ve set up and configured the GraphQL Code Generator and plugins on a project and used the generated types in my code.

I can now use typesafe code in my Svelte project!

If you want to check out how to do this with KitQL then check out my guide on Getting Started with KitQL and GraphCMS for more information.

Copyright © 2017 - 2022 - All right reserved Scott Spence