Use Apollo Client with SvelteKit to Query a GraphQL API
Tags: svelte, webdev, graphql, javascript
Published: 2021-08-09 08:48:42

---
title: "Use Apollo Client with SvelteKit to Query a GraphQL API"
published: "true"
description: "Use Apollo Client with SvelteKit to Query a GraphQL API: we use the GitHub API to query our repos and learn a bit of SvelteKit along the way."
tags: "svelte, webdev, graphql, javascript"
canonical_url: "https://rodneylab.com/use-apollo-client-sveltekit/"
cover_image: "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/woxwl982d99dinzrd1n8.png"
---

## ✨ Use Apollo Client with SvelteKit

In this post we look at how to use Apollo Client with SvelteKit.  We will query the GitHub GraphQL API to pull out some data from your repos.  If you are new to SvelteKit, you will find this post useful, as we use a few of SvelteKit's idiosyncrasies.  These help Svelte offer an improved developer experience. If you come from a React background, then you might well already be familiar with Apollo Client, so I hope you can leverage that knowledge here to get to learn SvelteKit a little quicker.  Instead of building yet another todo app, you will create some code in this tutorial which you can leverage to generate a Projects page on your personal developer blog.

![Use Apollo Client with SvelteKit: What we're Building - screen capture showing an array of boxes stacked vertically. Each box contains details of a Git Hub repo such as name, number of stars and last updated date.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19htlblyvjozj7wyihad.png)

To follow along, you might find a GitHub account useful. If you don't already have one, just hop over to <a aria-label="Open the Git hub page to create a new account if you do not yet have one" href="https://github.com/">github.com and click the **Sign up for GitHub** button</a>.  We won't look in too much detail at what GraphQL is here.  You might be new to GraphQL, though that is not a barrier. You will <a aria-label="Learn about Graph Q L" href="https://graphql.org/learn/">find this Introduction to GraphQL useful</a>. If you're ready let's get started.

## 🔧 GitHub GraphQL API

The GitHub GraphQL API let's you query your repo and other users' public repos.  Here we will just be pulling data about your own repo, such as description, last commit and stars.  You can use this code on your blog Projects page.  Using GitHub to source the information you only ever have to update content in one place.  You are kind of using GitHub as a Content Management System (CMS) for you Projects page.  For sparkles, you could pull the repo code languages and tags, and, add filters so future employers can see exactly what you coded in each language.  There is so much magic you can do with this API.  Your imagination is the limit!

### GitHub GraphQL API Explorer 

You don't have to do this step for the tutorial, though you might find it interesting.  GitHub has a <a aria-label="Open the Git Hub Graph Q L A P I Explorer" href="https://docs.github.com/en/graphql/overview/explorer">GraphiQL explorer for the GitHub API</a>.  You can use it to check what GraphQL queries you can make as well as get documentation.  If you are extending this project for you own needs, you can generate your queries and mutations in GraphiQL and then paste them into your code editor once you are happy with them.  For the Explorer to work, you will need to Sign in to your account and then authorise it to access your GitHub account.

Although you don't need to authorise the Explorer get going on this project, you will need to generate a GitHub personal Access Token.  You can see <a aria-label="Open Git Hub to create a personal access token" href="https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token">full instructions on the GitHub page</a>.  In short, once logged in to GitHub, click your profile icon in the top right corner and select **Settings**.  From the options which appear on the left of the screen, select **Developer settings**.  Next select **Personal access tokens** followed by clicking the **Generate new token** button.  As the **note**, you can use `sveltekit-graphql-github`.  For this project you will only need the `public_repo` permission under **repo**.  Finally scroll down to the bottom of the screen and select **Generate token**.  Make a note of the token which the console displays, you will need this shortly.

Now we have a GitHub personal access token.  Why don't we create our SvelteKit project?

## 🧱 Building our SvelteKit App to Query the GitHub API

If you were creating a blog site, you would start with the <a aria-label="Open the Svelte Kit Blog starter post" href="https://rodneylab.com/sveltekit-blog-starter/">SvelteKit Blog Starter</a>.  However we are just building a simple (literally) single page app, so we will start from scratch.  You might find this useful if this is your very first SvelteKit project (if it is your first, also take a <a aria-label="Read the post on getting started with Svelte Kit" href="https://rodneylab.com/getting-started-with-sveltekit/">look at the guide on Getting Started with SvelteKit</a>).  We start by spinning up a skeleton site.  If you prefer yarn or npm, swap out the `pnpm` command:

```shell
pnpm init svelte@next sveltekit-graphql-github && cd $_
pnpm install
pnpm run dev
```
The app should be up and running now on your machine at <a aria-label="Open you running SvelteKit local server" href="http://localhost:3000">http://localhost:3000</a>.  If something else is already running on port `3000`, don't worry, we'll see how to change ports in a moment. We need a few packages for this project, let's install them all at once:

```shell
pnpm i -D @apollo/client @fontsource/fira-sans @fontsource/merriweather
  @sveltejs/adapter-static@next dayjs env-cmd graphql node-fetch sass 
	svelte-preprocess
```

What do we have here? `@apollo/client`, `graphql` and `node-fetch` will be used to make GraphQL queries.  We will use `@fontsource/fira-sans`, `@fontsource/merriweather`, `dayjs`, `sass` and `svelte-preprocess` for styling and formatting. `@sveltejs/adapter-static@next` builds a static SvelteKit site. `env-cmd` is a handy utility for keeping our secrets secret.  Speaking of which, next we will add some environment variables to the app.

### Environment Variables

Our app will have a client part and a server part.  We will build a static site, which means (in production) the server code only runs once when the static site is being generated.  Only the server needs to know our secrets for this project, so we do not prefix them with `VITE_` and we use `env-cmd` to access them.  There is a little more <a aria-label="Learn more about Svelte Kit environment variables" href="https://rodneylab.com/getting-started-with-sveltekit/#sveltekitEnvironmentVariables">explanation of environment variables in the Getting Started with SvelteKit</a> post.  Anyway, create a `.env` file in the project's root folder and paste in GitHub personal access token from the previous section:

```shell
GRAPHQL_ENDPOINT="https://api.github.com/graphql"
GITHUB_PERSONAL_ACCESS_TOKEN="PASTE_YOUR_TOKEN_IN_HERE"
```

The GraphQL endpoint is not really secret in this case, but if you were querying another API which was private, you would definitely not want to add it to you committed repo code.  To be able to access the environment variables, we need to modify our npm scripts in `package.json`:

```json
{
	"name": "sveltekit-graphql-github",
	"version": "0.0.1",
	"scripts": {
		"dev": "env-cmd svelte-kit dev -p 3000",
		"build": "env-cmd svelte-kit build",
		"preview": "svelte-kit preview -p 3000",
		"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
		"format": "prettier --write --plugin-search-dir=. ."
	},
```

Also change the ports in lines `5` and `7` if you need to. Next, we will set up the backend and then add the front end afterwards and finally style it.

## Apollo Client Set Up

This Apollo Client code is based on a <a aria-label="Open the Stack Overflow Question" href="https://stackoverflow.com/questions/67135169/how-to-initialize-apolloclient-in-sveltekit-to-work-on-both-ssr-and-client-side">Stack Overflow answer</a>.  As the solution suggests, we will only import the parts of Apollo Client which we need.  Make a `src/lib/utilities` folder in your project and add an `apolloClient.js` file with this code:

```javascript
import fetch from 'node-fetch';
import { ApolloClient, HttpLink } from '@apollo/client/core/core.cjs.js';
import { InMemoryCache } from '@apollo/client/cache/cache.cjs.js';
import { setContext } from '@apollo/client/link/context/context.cjs.js';
 
class Client {
	constructor() {
				if (Client._instance) {
							}
		Client._instance = this;
		
		this.client = this.setupClient();
	}
 
	setupClient() {
		const link = new HttpLink({
			uri: process.env['GRAPHQL_ENDPOINT'],
			fetch
		});
		const authLink = setContext((_, { headers }) => {
			return {
				headers: {
					...headers,
					authorization: `Bearer ${process.env['GITHUB_PERSONAL_ACCESS_TOKEN']}`
				}
			};
		});
		const client = new ApolloClient({
			credentials: 'include',
			link: authLink.concat(link),
			cache: new InMemoryCache()
		});
		return client;
	}
}
```

We have modified the code from the Stack Overflow question to authorise us on the GitHub API.  If you use this code on another project using an API which does not ned authorisation, remove lines `20`&ndash;`27` and also change line `30` to read `link,`.

### Query Endpoint

We will configure our app so the user's browser queries an endpoint on our app's server.  That endpoint will respond with the GitHub data the client needs to render.  This is not quite how things happen in our case as are generating a static app, but this explanation should give a clearer picture of what we are doing here.  <a aria-label="Open an article on SvelteKit File based routing" href="https://rodneylab.com/sveltekit-file-based-routing/">SvelteKit uses slug-based routing</a>.  The client will send a `POST` request to the endpoint `/query/repositories.json`, so we need to create a file at `src/routes/query/repositories.json.js` to answer that request.  Create a file at that location and paste this code into it:


```javascript
import { client } from '$lib/utilities/apolloClient.js';
import { gql } from '@apollo/client/core/core.cjs.js';
 
export async function post(request) {
	try {
		const { limit } = request.body;
		const query = gql`
			query RepositoriesQuery($limit: Int) {
				viewer {
					login
					name
					repositories(first: $limit, orderBy: { field: STARGAZERS, direction: DESC }) {
						nodes {
							id
							createdAt
							name
							description
							resourcePath
							pushedAt
							stargazerCount
						}
					}
				}
			}
		`;
		const { data } = await client.query({
			query,
			variables: { limit }
		});
		return {
			body: JSON.stringify({ data })
		};
	} catch (err) {
		console.error('Error: ', err);
		return {
			status: 500,
			error: 'Error receiving data'
		};
	}
}
```

We only need to respond to `POST` requests so our file at this endpoint only contains a `post` function,

- in line `6` we destructure the `limit` variable which we will pass in a JSON post from the client.  This is the number of posts we want to receive,

- lines `7`&ndash;`25` contain the actual query, notice we use the `limit` variable to supply the `first` parameter in our query (line `12`).  You can go town here and add other parameters you want to query for, like repo code languages and tags,

- lines `26`&ndash;`29` is where we use Apollo Client to make the query. If you used hooks with Apollo Client in React, it might be some time since you have last seen this syntax!  We don't need mutations here, but if you do need a mutation in one of your projects, the equivalent code would look like this:

```javascript
const { data } = await client.mutate({
	mutation,
	variables: { limit }
});	
```

You would need to define your mutation GraphQL in a similar way to the query we have above.

- finally in lines `30`&ndash;`32`, we convert the data to a JSON string and respond to the request.

We go client side next!

### Browser Client Page

We only have a single page for this app.  First replace the svelte code in `src/routes/index.svelte` with this, then we will take a closer look:

```html
<script context="module">
	export const prerender = true;
	export async function load({ fetch }) {
		const url = '/query/repositories.json';
		const res = await fetch(url, {
			method: 'POST',
			credentials: 'same-origin',
			headers: {
				'Content-Type': 'application/json'
			},
			body: JSON.stringify({
				limit: 5
			})
		});
		const { data } = await res.json();
		if (res.ok) {
			return {
				props: { data }
			};
		}
		
		return {
			status: res.status,
			error: new Error(`Couldn't load ${url}`)
		};
	}
</script>
 
<script>
	import dayjs from 'dayjs';
	import relativeTime from 'dayjs/plugin/relativeTime.js';
	export let data;
	
	dayjs.extend(relativeTime);
 
	const { login: user, repositories: repoNodes } = data.viewer;
	const { nodes: repos } = repoNodes;
	const repoCount = repos.length;
 
	const fromNow = (date) => {
		const daysJSDate = dayjs(date);
		return daysJSDate.fromNow();
	};
</script>
 
<div class="container">
	<div class="content">
		<h1 class="heading">{`${user}'s GitHub`}</h1>
		
		{#if repoCount > 0}
			{#each repos as repo, index}
				<article aria-posinset={index + 1} aria-setsize={repoCount} class="repo">
					<h2>
						{repo.name}
					</h2>
					<span>
						<span class="meta">Updated {fromNow(repo.pushedAt)}</span>
						{#if repo.stargazerCount > 0}<span class="meta stars">🌟 {repo.stargazerCount}</span>{/if}
					</span>
					{#if repo.description != null}
					<p><a href={`https://github.com/${repo.resourcePath}`}>{repo.description}</a></p>
					{/if}
				</article>
			{/each}
		{:else}
			<p>No repos found :/</p>
		{/if}
	</div>
</div>
```

If this is the first svelte code you're looking at, you can see it looks like a kind of a superset of HTML.

The code has three blocks.  We will add a fourth block for styling in a moment.  The first block is a Javascript module.  It contains our load function.  For SvelteKit the loader runs before the page renders.  Here it is used to get the data from the server endpoint we just created.  The page should render in your browser (albeit unstyled).  Do you see a list of your public repos?  Try changing the limit in line `12` from `5` to `1` and saving.   As long as you have more than one public repo in your GitHub, you should see we pull back less data in the GraphQL query and only that data is rendered.

#### Prerendering

Going back to line `2`, this doesn't have an impact when we are running in dev mode.  When we build the site though, the `prerender` instruction tells SvelteKit to generate a static site.  This means we pull all the data during the build process and never update it.  If we add new repos and want them to appear on our page, we just have to rebuild it.  Prerendering can generally be used where the same data is presented to every site visitor.  The prerendered page should load quicker since we do not need to query for data while the page is loading up, it will already be encoded in the site's HTML.  For the prerendered site, the load function only runs during build.

In lines `17`&ndash;`19` you see we return a `data` prop.  This is available in the next script block, which runs when the page renders.  In line `32` (next block) you see the way we import a prop is by using the `export let` syntax.  See the <a aria-label="Learn about Svelte props" href="https://svelte.dev/tutorial/declaring-props">Svelte tutorial for more on props</a>.  This is confusing initially, though you will soon get used to it.  The rest of the block is everyday JavaScript you would see in other frameworks.

#### Rendered Code

The rendered content is in the third block.  You can see a few Svelte features here:

- line `50` opens an `if` block. This is <a aria-label="Learn about if blocks in Svelte from the Svelte tutorial" href="https://svelte.dev/tutorial/if-blocks">how we can do conditional rendering in Svelte</a>.  We render the code if lines `51`&ndash;`64` if there are some repos in the response.  Otherwise we render the paragraph in line `67`.

- lines `51`&ndash;`64` show how to loop.  Looking at line `51`, the `repos` variable refers to an array which is defined in the previous `script` block.  The `repo` and `index` variables are temporary loop variables and used to get data from each element as we loop over the array (think something line `{repos.map((repo, index)) => <>{renderMe}</>)}` if you are used to React).

The remaining code has further examples of conditional rendering and accessing Javascript variables.

### Let's add some Sass

I love to use Sass for styling, though you can use tailwind or other tools with SvelteKit.  There are a couple of things we need to do to set up Sass.  First we will configure SvelteKit to use the preprocessor.  Edit `sveltekit.config.js`:

```javascript
/** @type {import('@sveltejs/kit').Config} */
import adapter from '@sveltejs/adapter-static';
import preprocess from 'svelte-preprocess';
 
const config = {
	preprocess: [
		preprocess({
			scss: {
				prependData: "@import 'src/lib/styles/variables.scss';"
			}
		})
	],
	kit: {
		adapter: adapter(),
		// hydrate the <div id="svelte"> element in src/app.html
		target: '#svelte'
	}
};
```

Before we look at styling, we also have some code here which tells SvelteKit to generate a static site.  You can deploy this site to <a aria-label="Open docs on deploying a static Svelte Kit site to Cloud flare Pages" href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site">Cloudflare Pages</a>, <a aria-label="See how to deploy a static Svelte Kit site to Netlify" href="https://rodneylab.com/use-netlify-to-host-your-sveltekit-site/">Netlify</a>, <a aria-label="Open post on deploying static Svelte Kit sites to Render" href="https://rodneylab.com/sveltekit-hosting/">Render</a> or other providers.  We just need to import the static adapter in line `2` and use it in line `14`. 

Here as well as setting up the preprocessor, in line `9` we make some variables available.  To start create this file at `src/lib/styles/variables.scss`, then paste in the code from <a aria-label="Find the variables.scss file content on the Rodney Lab Git Hub repo" href="https://raw.githubusercontent.com/rodneylab/sveltekit-graphql-github/main/src/lib/styles/variables.scss">variables.scss in the GitHub repo</a>.  Now create <a aria-label="Find the variables.scss file content on the Rodney Lab Git Hub repo" href="https://raw.githubusercontent.com/rodneylab/sveltekit-graphql-github/main/src/lib/styles/index.scss">index.sccs</a>, <a aria-label="Find the variables.scss file content on the Rodney Lab Git Hub repo" href="https://raw.githubusercontent.com/rodneylab/sveltekit-graphql-github/main/src/lib/styles/normalise.css">normalise.css</a> and <a aria-label="Find the variables.scss file content on the Rodney Lab Git Hub repo" href="https://raw.githubusercontent.com/rodneylab/sveltekit-graphql-github/main/src/lib/styles/styles.scss">styles.scss</a> in the same folder and follow the links to find the code you need to paste in.

We will need to import those styles so they are available for our page.  On a larger site you would create a layout component and import this data there to make it available to every page on the site.  Even though we have a single page on our site, we will do the same, so you can see how it's done.  Create a default layout file at `src/routes/__layout.svelte`:


```javascript
<script>
	import '$lib/styles/normalise.css';
	import '$lib/styles/index.scss';
	import '@fontsource/merriweather/400.css';
	import '@fontsource/merriweather/700.css';
	import '@fontsource/fira-sans/400.css';
</script>
 
<slot />
```

Here we import our the fonts we want to self host (we installed them earlier) as well as the styles.  This is a layout file and the `<slot />` element in line `9` is a placeholder for our content.  If you are using Svelte for the first time, add a `<h1>` element above it and a paragraph below so you can really see it's a layout template.  On a larger site, we would add any headers and footers which appear on every page to this file.

Finally, to complete style, paste these style at the bottom of `src/routes/index.svelte`

```html
<style lang="scss">
	.container {
		background: $color-theme-4;
		border: solid $color-theme-3 $spacing-px;
		border-radius: $spacing-1;
		margin: 0;
		width: 70%;
		margin: $spacing-12 auto;
		padding: $spacing-2 $spacing-4;
 
		p {
			font-size: $font-size-2;
			margin-top: $spacing-8;
		}
	}
	.heading {
		color: $color-theme-3;
	}
 
	.content {
		margin: $spacing-12;
		color: $color-theme-3;
	}
 
	.repo {
		border: solid $color-theme-3 $spacing-px;
		border-radius: $spacing-1;
		background-color: $color-theme-5;
		margin: $spacing-6 $spacing-0;
		padding: $spacing-4;
		color: $color-theme-3;
 
		h2 {
			margin-top: $spacing-0;
			margin-bottom: $spacing-4;
			color: $color-theme-3;
			font-size: $font-size-4;
		}
 
		.stars {
			font-weight: $font-weight-bold;
		}
	}
 
	.repo:hover {
		background-color: $color-theme-3;
		color: $color-theme-5;
 
		h2 {
			color: $color-theme-5;
		}
 
		a {
			color: $color-theme-4;
		}
 
		.meta {
			border: solid $color-theme-4 $spacing-px;
			padding: #{$spacing-1 - $spacing-px} #{$spacing-2 - $spacing-px};
		}
	}
 
	.meta {
		font-size: $font-size-1;
		background-color: $color-theme-3;
		color: $color-theme-4;
		padding: $spacing-1 $spacing-2;
		border-radius: $spacing-1;
	}
 
	@media screen and (max-width: $desktop-breakpoint) {
		.container {
			width: 95%;
		}
		.content {
			margin: $spacing-12 $spacing-2;
		}
		.repo {
			margin: $spacing-10 $spacing-0;
		}
	}
</style>
```

We tell SvelteKit our styles are using scss in line `71` and then have access to the variable defined in the global styles we created a moment ago.

## 🔥 How Does in Look?

Normally at this stage in our tutorials, we run some tests.  I think all you need to do is refresh your browser and you should see your repos nicely styled.  Let me know if something is not working, or if I haven't explained some part well.  That way I can update the tutorial for anyone else following later.


## 🙌🏽 Use Apollo Client with SvelteKit: Wrapup

That's it for this post.  We have seen:

- how to build a SvelteKit site from scratch,
- that GraphQL offers a convenient way to access GitHub meta,
- how to use Apollo Client with SvelteKit to query a GraphQL API.

As always suggestions for improvements, together with requests for explanations and feedback are more than welcome.  Also let me know what other features you would like implemented on the starter.  The full code for this tutorial on <a aria-label="Open the tutorial code on Git Hub" href="https://github.com/rodneylab/sveltekit-graphql-github">how to use Apollo Client with SvelteKit is on the Rodney Lab GitHub repo</a>.  There is also a <a aria-label="Open the demo site" href="https://sveltekit-graphql-github.rodneylab.com/">live demo site running on Render</a>.

## 🙏🏽 Use Apollo Client with SvelteKit: Feedback

Have you found the post useful? Would you like to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a couple of dollars, rupees, euros or pounds, please <a aria-label="Support Rodney Lab via Buy me a Coffee" href="https://rodneylab.com/giving/">consider supporting me through Buy me a Coffee</a>.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via <a aria-label="D M Rodney Lab on Twitter" href="https://twitter.com/messages/compose?recipient_id=1323579817258831875">@askRodney</a> on Twitter and also <a aria-label="Contact Rodney Lab via Telegram" href="https://t.me/askRodney">askRodney on Telegram</a>. Also, see <a aria-label="Get in touch with Rodney Lab" href="https://rodneylab.com/contact">further ways to get in touch with Rodney Lab</a>. I post regularly on <a aria-label="See posts on svelte kit" href="https://rodneylab.com/tags/sveltekit/">SvelteKit</a> as well as other topics. Also <a aria-label="Subscribe to the Rodney Lab newsletter" href="https://rodneylab.com/about/#newsletter">subscribe to the newsletter to keep up-to-date</a> with our latest projects.