How to Guide: Using Sapper with TakeShape
Tags: tutorial, webdev, javascript, svelte
Published: 2021-01-27 19:13:03

---
title: How to Guide: Using Sapper with TakeShape
published: true
description:
tags: tutorial, webdev, javascript, svelte
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/bgbju3x5rn54vsiqpky5.png
canonical_url: https://www.takeshape.io/articles/using-takeshape-with-sapper/
---

In this article, we will discuss how to use TakeShape with Sapper, an application framework powered by Svelte.

If you want to jump right into the code, check out the [GitHub Repo here](https://github.com/lelouchB/sapper-takeshape-example).

And here's a link to the deployed version:  [https://sapper-takeshape-example.vercel.app/](https://sapper-takeshape-example.vercel.app/)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0osbzxjw23q5ctyzrbjs.gif)

## Prerequisites

- Knowledge of HTML, CSS, JavaScript
- Basics of [Svelte](https://svelte.dev/) and [GraphQL](https://graphql.org/learn/)
- [Node/NPM](https://nodejs.org/en/) installed on your local dev machine
- Any code editor of your choice

## What is Svelte?

[Svelte](https://svelte.dev/) is a tool for building fast web applications,  similar to JavaScript frameworks like React and Vue, svelte aims to make building slick interactive user interfaces easy. But there's a crucial difference.

According to official docs:

> Svelte converts your app into ideal JavaScript at *build time*, rather than interpreting your application code at *run time*. This means you don't pay the framework's abstractions' performance cost, and you don't incur a penalty when your app first loads.

## What is Sapper?

[Sapper](https://sapper.svelte.dev/docs) is a  framework built on top of Svelte and has taken inspiration from [Next.js](https://github.com/zeit/next.js). Sapper helps you create SEO optimized Progressive Web Apps (PWAs) with file system based routing, similar to [Next.js](https://nextjs.org/docs/routing/introduction).

## How to Setup and Install a Sapper project

This tutorial uses `sapper-template` to quickly set up the initial Sapper project, which is also the preferred way to initialize a Sapper project.

In your project's root directory, run the following commands in the terminal.

```bash
npx degit "sveltejs/sapper-template#webpack" sapper-takeshape-example
cd sapper-takeshape-example
npm install
npm run dev
```

The last command `npm run dev` will start the development server on port 3000. Head over to [http://localhost:3000/](http://localhost:3000/).

Here is how your app will look.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/j9d3cxu9so4npimovznb.png)

## How to Generate TakeShape API Keys

If haven't already, create a free developer account on [TakeShape](https://app.takeshape.io/signup).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pod7jb2whld2pf96fmqb.png)

Create a new project and configure it as shown below.

Give a name to your project; this tutorial uses a project named  `sapper-takeshape-example`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bq9ks4b8xqmkyj7kfd70.png)

Now, click on **Create Project**.

On your TakeShape dashboard, head over to the **Post** tab. You will see the sample blog posts present in this project.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3v4i4tbrtdltgpqwgw40.png)

The next step is to generate API keys to authenticate your Sapper project with TakeShape. Click on the triple dots present next to your project's name on the dashboard.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/k6wq40256x4svnfsce9o.png)

In the drop-down menu, click on **API Keys**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9dctiqmzv6hh38owxoc8.png)

Click on **New API Key**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gd6db89fsivnpvuk5x40.png)

Name this API key, and since you will only use this on the client-side to read the blog posts, you can set **Permissions** to **Read**. Click on **Create API Key**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/70x7ox77y6smbptspp4u.png)

Copy the API key to a secure location; remember you will only see them once.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bqkgchbejjazqymlolmi.png)

***Note:** These credentials belong to a deleted project; hence I have not hidden them throughout this tutorial to give you a better understanding of the process and steps. **You should never disclose your private API keys to anyone.***

On the **API Keys** page, you will also see your TakeShape project id, i.e., the value between `/project/` and `/v3/graphql` in your API endpoint; copy this project id.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/opwm1lt7dpsr7vs3cjtq.png)

In your project's root directory, run the following command to create a new file named `.env` to securely store this API key.

```bash
touch .env
```

In your `.env` file, add the environment variables.

```jsx
# .env
TAKESHAPE_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TAKESHAPE_PROJECT="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

To access these environment variables, you need to install the `dotenv` package, which loads environment variables from the `.env` file.

Run the following command in the terminal to install the `dotenv` package in your project.

```bash
npm install dotenv
```

Now you also need to configure Sapper to use these environment variables. Modify your `src/server.js` file like this.

```jsx
require("dotenv").config();

import sirv from "sirv";
import polka from "polka";
import compression from "compression";
import * as sapper from "@sapper/server";

const {PORT, NODE_ENV} = process.env;

const dev = NODE_ENV === "development";

polka() // You can also use Express
    .use(
        compression({threshold: 0}),
        sirv("static", {dev}),
        sapper.middleware()
    )
    .listen(PORT, (err) => {
        if (err) console.log("error", err);
    });
```

In the above code, you have imported the `dotenv` package at the top of the `server.js` file.

```jsx
require("dotenv").config();
```

Restart your development server by closing it using `Ctrl + C` and starting it again using `npm run dev`.

## How to Display Posts on the Blog Page

With your development server still running, head over to [http://localhost:3000/blog](http://localhost:3000/blog). You will see a page similar to this, which lists all the posts with their links.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/h6mznu8c4fu73gd18sun.png)

These are the sample blog posts that come with the sapper-template and are present in `src/routes/blog/_posts.js`. You need to update this `/blog` route to show posts fetched from TakeShape.

Every `post` in the `posts` array has a title and a slug, shown on the blog page. You need to create a similar GraphQL query that fetches the title and slug of each post.

On your TakeShape dashboard, click on **API Explorer**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qqh4s9pb06kx0nw5cwra.png)

Here is how this **API Explorer** will look.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/z3cbktve4suius36ulhg.png)

Copy and Paste the following GraphQL query in the left tab.

```graphql
query AllPosts {
  allPosts: getPostList {
    items {
      _id
      title
      slug
    }
  }
}
```

Run this query; you will see an output similar to this.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n0rt27tfmvd3brjvdzow.png)

In Sapper, Page is a  Svelte component written in `.svelte` files. Server routes are modules written in `.js` files that export functions corresponding to HTTP methods like `get`, `post`, etc. Each function receives HTTP request and response objects as arguments, plus a next function.

The `index.json.js` file under `routes/blog` directory is a server route, which currently fetches data from the `posts` array in the `_posts.js` file. You need to update this server route to fetch posts from TakeShape.

You will need to install the `node-fetch` package to make the API requests. Run the following command in the terminal to install `node-fetch`.

```bash
npm install node-fetch
```

Update `src/routes/blog/index.json.js` file like this and restart your development server.

```jsx
const fetch = require("node-fetch");

export async function get(req, res) {
    const {TAKESHAPE_API_KEY, TAKESHAPE_PROJECT} = process.env;

    const data = await fetch(
        `https://api.takeshape.io/project/${TAKESHAPE_PROJECT}/v3/graphql`,
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${TAKESHAPE_API_KEY}`,
            },
            body: JSON.stringify({
                query: `
  					  query AllPosts {
  						  allPosts: getPostList {
  							  items {
  							  _id
  							  title
  							  slug
  							  }
  						  }
  					  }
    `,
            }),
        }
    );
    const response = await data.json();
    const posts = await JSON.stringify(response.data.allPosts.items);

    res.writeHead(200, {
        "Content-Type": "application/json",
    });

    res.end(posts)
}
```

In the above code, you first import the `node-fetch` package.

```jsx
const fetch = require("node-fetch");
```

Then inside the `get` function, you extract the environment variables from `process.env`.

```jsx
const { TAKESHAPE_API_KEY, TAKESHAPE_PROJECT } = process.env;
```

Now, you make the **POST** request to TakeShape using the `fetch` method.

```jsx
const data = await fetch(
    `https://api.takeshape.io/project/${TAKESHAPE_PROJECT}/v3/graphql`,
    {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${TAKESHAPE_API_KEY}`,
        },
        body: JSON.stringify({
            query: `
  					  query AllPosts {
  						  allPosts: getPostList {
  							  items {
  							  _id
  							  title
  							  slug
  							  }
  						  }
  					  }
    `,
        }),
    }
);
```

You pass the TakeShape API key under `Authorization` in headers. The GraphQL query inside the `body` is the same as discussed above in the API Explorer.

Finally, you return the posts in the response using `res.end(posts)`.

```jsx
const response = await data.json();
const posts = await JSON.stringify(response.data.allPosts.items);

res.writeHead(200, {
    "Content-Type": "application/json",
});

res.end(posts);
```

In Sapper, Page component have a optional `preload` function that runs before the component is created. As the name suggests this function, preloads the data that the page depends upon.

Preload is the Sapper equivalent to `getInitialProps` in Next.js or `asyncData` in Nuxt.js. You can read more about [Preloading here](https://sapper.svelte.dev/docs#Preloading).

Open `src/routes/blog/index.svelte` file in your code editor. Since `index.json`  route is inside `blog` directory, it can also be referenced as `blog.json`.

You fetch data from `blog.json` route using `this.fetch`. This method is quite similar to `fetch` API with some additional features like requesting data based on user's session.  You can read more about `this.fetch` [here](https://sapper.svelte.dev/docs#this_fetch).

```jsx
<script context="module">
export function preload() {
    return this.fetch(`blog.json`)
        .then((r) => r.json()).then((posts) => {
            return {posts};
        });
}
</script>
```

In Svelte, you can iterate over any array or array like value using an `#each` block as shown here. Here `(post._id)` is the key that uniquely identifies each post. You can read more about `#each` block [here](https://svelte.dev/docs#each).

```jsx
<ul>
 {#each posts as post (post._id)}
 	 <li><a rel="prefetch" href="blog/{post.slug}">{post.title}</a></li>
 {/each}
</ul>
```

You don't need to make any other change in `index.svelte` file except for adding a key in the `#each` block like shown above.

Navigate to [http://localhost:3000/blog](http://localhost:3000/blog) in your browser; you will notice that posts have been updated.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zux4pwqb5xow1cm5hnxl.png)

You can now delete the `_posts.js` file in the `routes/blog` directory.

Since the individual post routes don't exist yet so these links will result in a 404 error, you will create them in the next section.

## How to Create Dynamic Routes for Posts

In Sapper, you can create dynamic routes by adding brackets to a page name, `([param])`, where the `param` is the dynamic parameter that is the `slug` of the article.

You will notice that a file named `[slug].svelte` already exists in the `src/routes/blog` directory.

You need to update the server route used in this file so when a user clicks on a post, the data corresponding to that post is fetched and is displayed with the `blog/[slug]` route.

Update `blog/[slug].json.js` file like this.

```jsx
const fetch = require("node-fetch");

export async function get(req, res, next) {
    const {slug} = req.params;

    const {TAKESHAPE_API_KEY, TAKESHAPE_PROJECT} = process.env;
    const data = await fetch(
        `https://api.takeshape.io/project/${TAKESHAPE_PROJECT}/v3/graphql`,
        {
            method: "post",
            headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${TAKESHAPE_API_KEY}`,
            },
            body: JSON.stringify({
                query: `
          query PostBySlug($slug: String) {
            post: getPostList(where: {slug: {eq: $slug}}) {
            items {
              _id
              title
              deck
              bodyHtml
            }
            }
          }`,
                variables: {
                    slug: slug,
                },
            }),
        }
    );

    const response = await data.json();
    const post = JSON.stringify(response.data.post.items[0]);

    res.writeHead(200, {
        "Content-Type": "application/json",
    });

    res.end(post);
}
```

The above code is quite similar to the server route code discussed in the last section, with few key differences.

This route fetches individual post data based on the `slug` provided, which is accessed via `req.params`.

```jsx
  const { slug } = req.params;
```

The GraphQL query in the above code fetches post matching the `slug` using `where: { slug: { eq: $slug } }`. In the query, `bodyHtml` corresponds to the HTML body of the post and `deck` is the short excerpt of the post.

```graphql
query PostBySlug($slug: String) {
  post: getPostList(where: { slug: { eq: $slug } }) {
    items {
      _id
      title
      deck
      bodyHtml
    }
  }
}
```

The `slug` is made available to the GraphQL query via `variables`.

```graphql
variables: {
  slug: slug,
},
```

Update `blog/[slug].svelte` file like this.

```jsx
<script context="module">
export async function preload({params}) {
    const res = await this.fetch(`blog/${params.slug}.json`);
    const data = await res.json();

    if (res.status === 200) {
        return {post: data};
    } else {
        this.error(res.status, data.message);
    }
}
</script>

<script>
    export let post;
</script>

<style>
    .content :global(h2) {
        font-size: 1.4em;
        font-weight: 500;
    }

    .content :global(pre) {
        background-color: #f9f9f9;
        box-shadow: inset 1px 1px 5px rgba(0, 0, 0, 0.05);
        padding: 0.5em;
        border-radius: 2px;
        overflow-x: auto;
    }

    .content :global(pre) :global(code) {
        background-color: transparent;
        padding: 0;
    }

    .content :global(ul) {
        line-height: 1.5;
    }

    .content :global(li) {
        margin: 0 0 0.5em 0;
    }
</style>

<svelte:head>
	<title>{post.title}</title>
	<meta name="Description" content={post.deck}>
</svelte:head>

<h1>{post.title}</h1>

<div class="content">
	{@html post.bodyHtml}
</div>
```

The `preload` function takes two arguments, `page` and `session`. Here `page` is an object equivalent to  `{ host, path, params, query }` and `session` is used to pass data such as environment variables from the server.

In the above `preload` function, you access the `page` object's  `params` property and pass the `slug` of the page to the server route.

If you  `console.log()` the `page` object, you will see all the data available via the `page` object. Here is how this will look like.

```jsx
{
  host: 'localhost:3000',
  path: '/blog/jump-aboard-new-treasure-island-edition',
  query: {},
  params: { slug: 'jump-aboard-new-treasure-island-edition' }
}
```

The post is returned based on the status code of the response. `this.error` is a method in Sapper for handling errors and invalid routes. You can read more about it [here](https://sapper.svelte.dev/docs#this_error).

```jsx
	if (res.status === 200) {
			return { post: data };
		} else {
			this.error(res.status, data.message);
		}
```

You only need to update `post.body` to `post.bodyHtml` in the `div` with `class="content"` in  `[slug].svelte` file like.

```jsx
<div class="content">
	{@html post.bodyHtml}
</div>
```

In Svelte, you can render HTML directly into a component using the `@html` tag like shown above. You can read more about this tag [here](https://svelte.dev/tutorial/html-tags).

And its done.

Try clicking on any of the posts on `/blog` route or head over to [http://localhost:3000/blog/jump-aboard-new-treasure-island-edition](http://localhost:3000/blog/jump-aboard-new-treasure-island-edition). You will see a page similar to this.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/edh86hresvupsfn79p5x.png)

You can view the finished website [here](https://sapper-takeshape-example.vercel.app/) and the code for the project [here](https://github.com/lelouchB/sapper-takeshape-example).

## Conclusion

In this article, you learned how to use TakeShape with Sapper, an application framework powered by Svelte. We saw how simple it is to integrate TakeShape with Sapper.

We also discussed how to use API Explorer in TakeShape and how to use the `preload` function in Sapper.

With just a few simple modifications and updates in your Sapper project, you can easily achieve a perfect Lighthouse score. Amazing, Right!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/thdngb58c4vh3empe2pa.png)

Here are some additional resources that can be helpful.

- [TakeShape Docs](https://www.takeshape.io/docs/quickstart/)
- [Sapper Docs](https://sapper.svelte.dev/docs)
- [Svelte Tutorial](https://svelte.dev/tutorial/basics)
- [How to deploy Sapper](https://sapper.svelte.dev/docs#Deployment)

Happy coding!