SvelteKit GraphQL Queries using fetch Only
Tags: svelte, graphql, webdev, javascript
Published: 2021-10-04 09:21:17

---
title: "SvelteKit GraphQL Queries using fetch Only"
published: "true"
description: "SvelteKit GraphQL queries using fetch only: how you can drop Apollo client and urql dependencies altogether to make your Svelte app leaner."
tags: "svelte, graphql, webdev, javascript"
canonical_url: "https://rodneylab.com/sveltekit-graphql-queries-fetch/"
cover_image: "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ns4jbw5nncmlxdat1koa.png"
---

## 😕 Why drop Apollo Client and urql for GraphQL Queries?

In this post we'll look at how you can perform SvelteKit GraphQL queries using fetch only.  That's right, there's no need to <a aria-label="See how to use Apollo Client with SvelteKit" href="https://rodneylab.com/use-apollo-client-sveltekit/">add Apollo client</a> or urql to your Svelte apps if you have basic GraphQL requirements.  We will get our GraphQL data from the remote API using just fetch functionality.  You probably already know that <a aria-label="Read M D N documents on the fetch A P I" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">the fetch API is available in client code</a>.  In SvelteKit it is also available in <a aria-label="Learn more about load function is Svelte Kit docs" href="https://kit.svelte.dev/docs#loading">load functions</a> and server API routes.  This means you can use the code we produce here to make GraphQL queries directly from page components or any server route.

We will use a currency API to pull the latest exchange rates for a few currencies, querying initially from a server API route.  This will be super useful for a backend dashboard on your platform.  You can use it to track payments received in foreign currencies, converting them back to your local currency be that dollars, rupees, euros, pounds or even none of those! This will be so handy if you are selling courses, merch or even web development services globally.  Once the basics are up and running we will add an additional query from a client page and see how easy Svelte stores make it to update your user interface with fresh data.

If that all sounds exciting to you, then let's not waste any time!

## ⚙️ SvelteKit GraphQL Queries: Setup

We'll start by creating a new project and installing packages:

```shell
pnpm init svelte@next sveltekit-graphql-fetch && cd $_
pnpm install
```

When prompted choose a **Skeleton Project** and answer **Yes** to TypeScript, ESLint and Prettier.

### API Key

We will be using the <a aria-label="Open S W O P A P I documentation" href="https://swop.cx/documentation">SWOP GraphQL API</a> to pull the latest available currency exchange rates.  To use the service we will need an API key.  There is a free developer tier and you only need an email address to sign up.  Let's <a aria-label="Sign up or a S W O P A P I account" href="https://swop.cx/account/register/developer">go to the sign up page now, sign up</a>, confirm our email address and then make a note of our new API key.

![SvelteKit GraphQL Queries: Getting an API Key.  Sign up form for API key.  Title is Getting Started.  There are boxes to enter an email address and password as well as a start now submit button.  Additional information includes a statement that no credit card is required.  There is a checkbox to agree to the Terms and Conditions.  Finally there is a link to sign if if you already have an account](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cop2lumantrd8ycj5hf2.png)

### Configuring `dotenv`

Let's configure `dotenv` now so we can start using the API quick sticks.  Install the `dotenv` package and the following font which we will use later:

```shell
pnpm install -D dotenv @fontsource/source-sans-pro
```

Next edit `svelte.config.js` to use `dotenv`:

```javascript
import 'dotenv/config';
import preprocess from 'svelte-preprocess';
 
/** @type {import('@sveltejs/kit').Config} */
const config = {
  // Consult https://github.com/sveltejs/svelte-preprocess
  // for more information about preprocessors
  preprocess: preprocess(),
 
  kit: {
    // hydrate the <div id="svelte"> element in src/app.html
    target: '#svelte'
  }
};
 
export default config;
```

Finally, create a `.env` file in the project root folder containing your API key:

```plaintext
SWOP_API_KEY="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
```

With the preliminaries out of the way let's write our query.

## 🧱 API Route

To create a GraphQL query using fetch, basically all you need to do is create a query object and a variables object, convert them to a string and then send them as the body to the right API endpoint.  We will use `fetch` to do the sending as it is already included in SvelteKit though you could choose `axios` or some other package if you wanted to.  In addition to the body, we need to make sure we include the right auth headers (as you would with Apollo client or urql).

That's enough theory.  If you want to do some more reading, <a aria-label="Read more about graph q l queries with fetch" href="https://www.netlify.com/blog/2020/12/21/send-graphql-queries-with-the-fetch-api-without-using-apollo-urql-or-other-graphql-clients/">Jason Lengstorf from Netlify wrote a fantastic article</a> with plenty of extra details.

Let's write some code.  Create a file at `src/routes/query/fx-rates.json.ts` and paste in the following code:

```typescript
import type { Request } from '@sveltejs/kit';
  
export async function post(
  request: Request & { body: { currencies: string[] } }
): Promise<{ body: string } | { error: string; status: number }> {
  try {
    const { currencies = ['CAD', 'GBP', 'IDR', 'INR', 'USD'] } = request.body;
  
    const query = `
      query latestQuery(
        $latestQueryBaseCurrency: String = "EUR"
        $latestQueryQuoteCurrencies: [String!]
      ) {
        latest(
          baseCurrency: $latestQueryBaseCurrency
          quoteCurrencies: $latestQueryQuoteCurrencies
        ) {
          baseCurrency
          quoteCurrency
          date
          quote
        }
      }
    `;
 
    const variables = {
      latestQueryBaseCurrency: 'EUR',
      latestQueryQuoteCurrencies: currencies
    };
 
    const response = await fetch('https://swop.cx/graphql', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `ApiKey ${process.env['SWOP_API_KEY']}`
      },
      body: JSON.stringify({
        query,
        variables
      })
    });
    const data = await response.json();
 
    return {
      body: JSON.stringify({ ...data })
    };
  } catch (err) {
    const error = `Error in /query/fx-rates.json.ts: ${err}`;
    console.error(error);
    return {
      status: 500,
      error
    };
  }
}
```

### What this Code Does

This is code for a fetch API route making use of SvelteKit's router.  To invoke this code from a client, we just send a `POST` request to `/query/fx-rates.json` with that path being derived from the file's path.  We will do this together shortly, so just carry on if this is not yet crystal clear.

You can see in lines `9`&ndash;`24` we define the GraphQL query.  This uses regular GraphQL syntax.  Just below we define our query variables.  If you are making a different query which does not need any variables, be sure to include an empty variables object.

In line `31` you see we make a fetch request to the SWOP API.  Importantly, we include the `Content-Type` header, set to `application/json` in line `34`.  The rest of the file just processes the response and relays it back to the client.

Let's create a store to save retrieved data next.

## 🛍 Store

We will create a store as our &ldquo;single source of truth&rdquo;.  Stores are an idiomatic Svelte way of sharing app state between components. We won't go into much detail here and you can <a aria-label="Learn more about Svelte stores in the Svelte tutorial" href="https://svelte.dev/tutorial/writable-stores">learn more about Svelte stores in the Svelte tutorial</a>.

To build the store, all we need to do is create the following file.  Let's do that now, pasting the content below into `src/lib/shared/stores/rates.ts` (you will need to create new folders):


```
import { writable } from 'svelte/store';
 
const rates = writable([]);
 
export { rates as default };
```

Next, we can go client side to make use of SvelteKit GraphQL queries using fetch only.

## 🖥 Initial Client Code: SvelteKit GraphQL queries using fetch

We are using TypeScript in this project, but very little, so hopefully you can follow along even though you are not completely familiar with TypeScript.  Replace the content of `src/routes/index.svelte` with the following:

```html
<script context="module">
  export const load = async ({ fetch }) => {
    try {
      const response = await fetch('/query/fx-rates.json', {
        method: 'POST',
        credentials: 'same-origin',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ currencies: ['CAD', 'GBP', 'IDR', 'INR', 'USD'] })
      });
      return {
        props: { ...(await response.json()) }
      };
    } catch (error) {
      console.error(`Error in load function for /: ${error}`);
    }
  };
</script>
 
<script lang="ts">
  import '@fontsource/source-sans-pro';
  import rates from '$lib/shared/stores/rates';
  export let data: {
    latest: { baseCurrency: string; quoteCurrency: string; date: Date; quote: number }[];
  };
  rates.set(data.latest);
  let newCurrency = '';
  let submitting = false;
</script>
 
<main class="container">
  <div class="heading">
    <h1>FX Rates</h1>
  </div>
  <ul class="content">
    {#each $rates as { baseCurrency, quoteCurrency, date, quote }}
      <li>
        <h2>{`${baseCurrency}\${quoteCurrency}`}</h2>
        <dl>
          <dt>
            {`1 ${baseCurrency}`}
          </dt>
          <dd>
            <span class="rate">
              {quote.toFixed(2)}
              {quoteCurrency}
            </span>
            <details><summary>More information...</summary>Date: {date}</details>
          </dd>
        </dl>
      </li>
    {/each}
  </ul>
</main>
```

With TypeScript you can define variable types alongside the variable. So in line `25`, we are saying that data is an object with a single field; `latest`.  `latest` itself is an array of objects (representing currency pairs in our case).  Each of these objects has the following fields: `baseCurrency`, `quoteCurrency`, `date` and `quote`.  You see the type of each of these declared alongside it.

### What are we Doing Here?

The first `<script>` block contains a load function. In SvelteKit, load functions contain code which runs before the initial render.  It makes sense to call the API route we just created from here.  We do that using the fetch call in lines `4`&ndash;`11`.  Notice how the url matches the file path for the file we created.  The JSON response is sent as a prop (from the return statement in lines `12`&ndash;`14`).

Another interesting line comes in the second `<script>` block.  Here at line `23`, we import the store we just created.  Line `24` is where we import the props we mentioned as a `data` prop.  The types come from the object we expect the API to return.  It is not too much hassle to type this out for a basic application like this one.  For a more sophisticated app, you might want to generate types automatically.  We will have to look at this in another article, so this one doesn't get too long.

Next we actually make use of the store.  We add the query result to the store in line `27`.  We will actually render whatever is in the store rather than the result of the query directly.  The benefit of doing it that way is that we can easily update what is rendered by adding another other currency pair to the store (without any complex logic for merging what is already rendered with new query results).  You will see this shortly.

This should all work as is.  Optionally add a little style before continuing:

#### Optional Styling

<details>
<summary>Optional Styling</summary>
```html
<style>
  :global body {
    margin: 0px;
  }
 
  .container {
    display: flex;
    flex-direction: column;
    background: #ff6b6b;
    min-height: 100vh;
    color: #1a535c;
    font-family: 'Source Sans Pro';
  }
 
  .content {
    margin: 3rem auto 1rem;
    width: 50%;
    border-radius: 1rem;
    border: #f7fff7 solid 1px;
  }
 
  .heading {
    background: #f7fff7;
    text-align: center;
    width: 50%;
    border-radius: 1rem;
    border: #1a535c solid 1px;
    margin: 3rem auto 0rem;
    padding: 0 1.5rem;
  }
 
  h1 {
    color: #1a535c;
  }
 
  ul {
    background: #1a535c;
    list-style-type: none;
    padding: 1.5rem;
  }
 
  li {
    margin-bottom: 1.5rem;
  }
 
  h2 {
    color: #ffe66d;
    margin-bottom: 0.5rem;
  }
 
  dl {
    background-color: #ffe66d;
    display: flex;
    margin: 0.5rem 3rem 1rem;
    padding: 1rem;
    border-radius: 0.5rem;
    border: #ff6b6b solid 1px;
  }
 
  .rate {
    font-size: 1.25rem;
  }
  dt {
    flex-basis: 15%;
    padding: 2px 0.25rem;
  }
 
  dd {
    flex-basis: 80%;
    flex-grow: 1;
    padding: 2px 0.25rem;
  }
 
  form {
    margin: 1.5rem auto 3rem;
    background: #4ecdc4;
    border: #1a535c solid 1px;
    padding: 1.5rem;
    border-radius: 1rem;
    width: 50%;
  }
  input {
    font-size: 1.563rem;
    border-radius: 0.5rem;
    border: #1a535c solid 1px;
    background: #f7fff7;
    padding: 0.25rem 0.25rem;
    margin-right: 0.5rem;
    width: 6rem;
  }
  button {
    font-size: 1.563rem;
    background: #ffe66d;
    border: #1a535c solid 2px;
    padding: 0.25rem 0.5rem;
    border-radius: 0.5rem;
    cursor: pointer;
  }
 
  .screen-reader-text {
    border: 0;
    clip: rect(1px, 1px, 1px, 1px);
    clip-path: inset(50%);
    height: 1px;
    margin: -1px;
    width: 1px;
    overflow: hidden;
    position: absolute !important;
    word-wrap: normal !important;
  }
 
  @media (max-width: 768px) {
    .content,
    form,
    .heading {
      width: auto;
      margin: 1.5rem;
    }
  }
</style>
```
</details>

Ok let's take a peek at what we have so far by going to <a href="http://localhost:3000/">localhost:3000/</a>.

![SvelteKit GraphQL Queries: Story so Far.  Image shows top part of a screenshot of how the app should look now, with styling applied.  There is an FX Rates title and below a block containing two currency pair results: EUR\CAD and EUR\GBP.  Eash shows the value of 1 euro in the curreny and a More information link](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8suhpivwxnrvky7uc9pz.png)

## 🚀 SvelteKit GraphQL queries using fetch: Updating the Store

Finally we will look at how updating the store updates the user interface  We will add a form in which the user can add a new currency.  Edit `src/routes/index.svelte`:

```html
<script lang="ts">
  import '@fontsource/source-sans-pro';
  import rates from '$lib/shared/stores/rates';
  export let data: {
    latest: { baseCurrency: string; quoteCurrency: string; date: Date; quote: number }[];
  };
  rates.set(data.latest);
  let newCurrency = '';
  let submitting = false;
 
  async function handleSubmit() {
    try {
      submitting = true;
      const response = await fetch('/query/fx-rates.json', {
        method: 'POST',
        credentials: 'same-origin',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ currencies: [newCurrency] })
      });
      const responseData = await response.json();
      const rate = responseData.data.latest[0];
      submitting = false;
      rates.set([...$rates, rate]);
      newCurrency = '';
    } catch (error) {
      console.error(`Error in handleSubmit function on /: ${error}`);
    }
  }
</script>
 
<main class="container">
  <div class="heading">
    <h1>FX Rates</h1>
  </div>
  <ul class="content">
    {#each $rates as { baseCurrency, quoteCurrency, date, quote }}
      <li>
        <h2>{`${baseCurrency}\${quoteCurrency}`}</h2>
        <dl>
          <dt>
            {`1 ${baseCurrency}`}
          </dt>
          <dd>
            <span class="rate">
              {quote.toFixed(2)}
              {quoteCurrency}
            </span>
            <details><summary>More information...</summary>Date: {date}</details>
          </dd>
        </dl>
      </li>
    {/each}
  </ul>
 
  <form on:submit|preventDefault={handleSubmit}>
    <span class="screen-reader-text"
      ><label for="additional-currency">Additional Currency</label></span
    >
    <input
      bind:value={newCurrency}
      required
      id="additional-currency"
      placeholder="AUD"
      title="Add another currency"
      type="text"
    />
    <button type="submit" disabled={submitting}>Add currency</button>
  </form>
</main>
```

In line `82` you see using Svelte it is pretty easy to link the value of an input to one of our TypeScript or javascript variables.  We do this with the `newCurrency` variable here.  In our `handleSubmit` function, we call our API route once more, this time only requesting the additional currency.  In line `45` we see updating state is a piece of cake using stores.  We just spread the current value of the rate store (this is nothing more than an array of the existing five currency objects) and tack the new one on the end.

Try this out yourself, adding a couple of currencies.  The interface should update straight away.

## 🙌🏽 SvelteKit GraphQL queries using fetch: What Do You Think?

In this post we learned:
- how to do SvelteKit GraphQL queries using fetch instead of Apollo client or urql,

- a way to get up-to-date currency exchange rate information into your site backend dashboard for analysis, accounting and so many other uses,

- how stores can be used in Svelte to update state.

There are some restrictions on the base currency in SWOP's developer mode.  The maths (math) to convert from EUR to the desired base currency isn't too complicated though.  You could implement a utility function to do the conversion in the API route file.  If you do find the service useful, or and expect to use it a lot, consider supporting the project by upgrading your account.

As an extension you might consider pulling historical data from the SWOP API, this is not too different, to the GraphQL query above. Have <a aria-label="Open the S W O P Graph Q L playground" href="https://swop.cx/account/dashboard/playground">play in the SWOP GraphQL Playground to discover more of the endless possibilities</a>.  Finally you might also find the <a aria-label="open the Purchasing Power A P I Git Hub repo" href="https://github.com/rwieruch/purchasing-power-parity">Purchasing Power API</a> handy if you are looking at currencies.  This is not a GraphQL API though it might be quite helpful for pricing your courses in global economies you are not familiar with.

 Is there something from this post you can leverage for a side project or even client project? 
I hope so! Let me know if there is anything in the post that I can improve on, for any one else creating this project.  You can leave a comment below, <a aria-label="tweet Rodney Lab" href="https://twitter.com/intent/user?screen_name=askRodney">@ me on Twitter</a> or try one of the other contact methods listed below.

You can see the <a aria-label="Open the Rodney Lab Git Hub repo" href="https://github.com/rodneylab/sveltekit-graphql-fetch">full code for this SvelteKit GraphQL queries using fetch project on the Rodney Lab Git Hub repo</a>.

## 🙏🏽 SvelteKit GraphQL queries using fetch: Feedback

Have you found the post useful? Do you have your own methods for solving this problem? Let me know your solution. 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 few dollars, 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="Reach out to me 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 <Link aria-label="Get in touch with Rodney Lab" to="/contact">further ways to get in touch with Rodney Lab</Link>. I post regularly on <Link aria-label="See posts on svelte kit" to="/tags/sveltekit/">SvelteKit</Link> as well as other topics. Also <Link aria-label="Subscribe to the Rodney Lab newsletter" to="/about/#newsletter">subscribe to the newsletter to keep up-to-date</Link> with our latest projects.