This page looks best with JavaScript enabled

Create a simple to do app using Svelte and GraphQL

 ·   ·  ☕ 6 min read

Let’s create a simple ‘to do’ application using Svelte and GraphQL. The objective is to demonstrate getting started on a GraphQL query and insert operation in a Svelte component.

If you are new to Svelte, you may want to go over what Svelte can do from the official site, or see Svelte quick overview.

Also, we will not quite dwell on the backend server in this post - you can follow along if you have any GraphQL backend. If not, I suggest you get started with a GraphQL setup of your own in the next 15 min. I will use the data structure and statements from the server setup described in that post.

I gather you would have installed degit to make your svelte life easier.

Create a new project using degit.

1
2
3
degit sveltejs/template svedo
cd svedo
npm i

This will copy svelte template and install all required components.

We will also install Apollo client for GraphQL. Go back to command window and do another npm.

1
npm i --save apollo-boost graphql svelte-apollo

Run your application and say ‘hello’ to the world.

1
npm run dev

Open the project folder in VS Code and start changing stuff.

main.js

Change your main.js for fun.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import App from "./App.svelte";

const app = new App({
  target: document.body,
  props: {
    name: "Mr. Anderson",
  },
});

export default app;
App.svelte

Open App.svelte. There are two main things we do here -

  1. Point Apollo client towards our GraphQL server URL
  2. Create a Todo Svelte component (we will create this next)

Also, centre the whole thing. Never trust developers who don’t put content at the centre.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<script>
  import ApolloClient from "apollo-boost";
  import { setClient } from "svelte-apollo";
  import Todo from "./Todo.svelte";

  export let name;

  const client = new ApolloClient({
    uri: "http://localhost:5050/graphql",

    onError: ({ networkError, graphQLErrors }) => {
      console.log("graphQLErrors", graphQLErrors);
      console.log("networkError", networkError);
    },
  });

  setClient(client);
</script>

<style>
  h1 {
    color: purple;
  }

  h5 {
    color: grey;
  }

  .centrify {
    text-align: center;
  }
</style>

<div class="centrify">
  <h5>Hello {name}!</h5>
  <Todo />
</div>

You might remember from the server setup post linked earlier that we are running GraphQL server at port 5050. Other code is largely self explanatory and not that important.

Todo.svelte

Create a new file Todo.svelte in the same directory as App.svelte. We will code most of our application here.

First: write the GraphQL queries - one for query and one for insert, and get the client ready.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<script>
  import { getClient, query, mutate } from "svelte-apollo";
  import { gql } from "apollo-boost";

  const GETTODO = gql`
    {
      allTodos {
        nodes {
          id
          title
          done
        }
      }
    }
  `;

  const ADDTODO = gql`
    mutation($todoEdit: String!) {
      createTodo(input: { todo: { title: $todoEdit, done: false } }) {
        todo {
          id
          title
          done
        }
      }
    }
  `;
</script>

Next: add functionality to fetch to-do from server. You can incrementally add code to perform query and update operations within the <script> block.

1
2
const client = getClient();
const todoOp = query(client, { query: GETTODO });

Add a function to insert a to-do record, and declare a variable which will hold the to-do value temporarily.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let todoEdit = "";
function addTodo() {
  const todoAdd = mutate(client, {
    mutation: ADDTODO,
    variables: {
      todoEdit,
    },
  })
    .then((data) => {
      todoEdit = "";
      todoOp.refetch();
    })
    .catch((e) => {
      console.error("error: ", e);
    });
}

Typically, we would want to just take the input, add to the list of to-dos, and call it a day. Here, we are ’re-fetching’ the results after insert just because we can.

PostGraphile passes back the the field values in update operation back in its response, other technologies/frameworks may not. This is where re-fetch has a role to play. Depending on your use case, you may or may not want to throw a query at server immediately after the update (or do it in one go on the server, which is the sane thing to do).

Add the HTML to the same Todo.svelte file to render the results fetched from server -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<div style="text-align:center">
  <h2>Svedos</h2>

  {#await $todoOp}

  <p>.. loading</p>

  {:then data}
    {#each data.data['allTodos']['nodes'] as todo, i}
    <p class:done="{todo.done}">{todo.title}</p>
    {/each}
  {:catch e}
    {e}
  {/await}
</div>

We do the following tasks in the above block -

  1. Wait for the fetch operation from the function. The function gets executed when the component gets loaded. The Todo.svelte component loads when called by App.svelte, which itself is called by main.js
  2. A helpful message ...loading is displayed till the data is fetched
  3. Once data is fetched, we use the standard GraphQL node traversal to fetch nodes from result and display them on the page using a {#each} loop.
  4. Catch any exceptions

By now, you should see the to-do records fetched and displayed on the page.

Add a form element before or after the list displayed in the previous section. This comprises of just a single input box and a submit button.

1
2
3
4
<form on:submit|preventDefault="{addTodo}">
  <input placeholder="new todo" bind:value="{todoEdit}" />
  <button method="submit">Submit</button>
</form>

Bind the input box to the (temporary) to-do variable we had declared earlier. We prevent default action on button click so that the page does not go into wild, unnecessary refresh.

And, there it is - your beautiful looking app that can insert or query to-do’s.

svelte-graphql-todo-app

Your app can now insert to-do records and fetch those to-do’s - what more can a human ask for?

Full Code for Todo.svelte

Copy/paste was never made more easier (until next section - you can just clone the repository from Github).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<script>
  import { getClient, query, mutate } from "svelte-apollo";
  import { gql } from "apollo-boost";

  const GETTODO = gql`
    {
      allTodos {
        nodes {
          id
          title
          done
        }
      }
    }
  `;

  const ADDTODO = gql`
    mutation($todoEdit: String!) {
      createTodo(input: { todo: { title: $todoEdit, done: false } }) {
        todo {
          id
          title
          done
        }
      }
    }
  `;

  let todoEdit = "";
  const client = getClient();
  const todoOp = query(client, { query: GETTODO });

  function addTodo() {
    const todoAdd = mutate(client, {
      mutation: ADDTODO,
      variables: {
        todoEdit,
      },
    })
      .then((data) => {
        todoEdit = "";
        todoOp.refetch();
      })
      .catch((e) => {
        console.error("error: ", e);
      });
  }
</script>

<style>
  .done {
    text-decoration: line-through;
  }
</style>

<div style="text-align:center">
  <h2>Svedos</h2>
  <form on:submit|preventDefault="{addTodo}">
    <input placeholder="new todo" bind:value="{todoEdit}" />
    <button method="submit">Submit</button>
  </form>

  {#await $todoOp}

  <p>.. loading</p>

  {:then data} {#each data.data['allTodos']['nodes'] as todo, i}
  <p class:done="{todo.done}">{todo.title}</p>
  {/each} {:catch e} {e} {/await}
</div>

Final final code

Find all code on GitHub

Stay in touch!
Share on

Prashanth Krishnamurthy
WRITTEN BY
Prashanth Krishnamurthy
Technologist | Creator of Things