Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile

Creating a Quiz Application With Sapper and Prisma. Part 1: Environment

3.00/5 (1 vote)
16 Jan 2020CPOL8 min read 5.9K  
Find out how to easily create a single-page application using Sapper, Prisma, and GraphQL Yoga
This article shows how to create a full-fledged web application with a database, GraphQL, and a reactive frontend.

Introduction

Simultaneously developing both server and client sides of web applications can be a challenge. Developers need to understand many low-level concepts, and this can prove an obstacle when solving some problems.

However, in recent years, the tech community has come up with helpful tools that encapsulate advanced approaches and architecture solutions. By utilizing them, you can easily organize data storage, use ready-made abstractions to build the logic of web applications, and much more.

In my two-article series, I will show you how to easily create a single-page application using Sapper, Prisma, and GraphQL Yoga.

We will use Prisma and GraphQL Yoga to develop the server side, and Sapper to develop the client application.

In the first part, we will consider the technology stack and configure the environment for application development.

Technology Stack

Svelte

Svelte is a framework that makes applications truly reactive and reduces their load volume. One of Svelte’s most beneficial properties is that it does not perform additional calculations for an application's virtual model.

Sapper

Sapper is a framework that allows you to write applications of different sizes using Svelte. Sapper internally launches a server that distributes a Svelte application with optimizations and SEO-friendly server-side rendering.

In this article, we will not consider Svelte separately from Sapper.

Prisma

To create the backend and work with data, we will use Prisma and GraphQL Yoga.

Prisma is a set of utilities for design and working with relational databases. We will work with Prisma Server and Prisma CLI.

Prisma Server is a standalone server that displays graphql endpoint/graphql playground outside, and connects graphql queries to the database and correctly translates them to the database inside. Also, Prisma Server is used for database schema management and migration.

GraphQL Yoga

Since Prisma provides root access to the database for each client, yoga-server will be used on the surface of the application. GraphQL-yoga fully supports the GraphQL specification and comes with a convenient playground in which we will test our queries.

Infrastructure Deployment

This setup way assumes that `Docker` and `docker-compose` are already pre-installed in the system.

Launching Sapper

Use the following snippet from the official documentation to deploy the Sapper application locally:

Bash
```bash
npx degit "sveltejs/sapper-template#rollup" svelte-graphql-quiz

cd svelte-graphql-quiz

npm install
npm run dev
```

This command will create a Sapper project in the `svelte-graphql-quiz` directory, install the dependencies, and start the server in development mode.

Launching Prisma

The environment deployment process for Prisma requires more configurations as it affects more infrastructure components.

  1. prisma-cli

    The first step is to install prisma-cli. This is a command-line utility that allows you to manage the entire Prisma infrastructure.

    Using this utility, you can:

    • Deploy the data schema to prisma-server (and to the database, respectively)
    • Automatically generate a client for working with prisma-server, which we will use on a public server

    Follow the link to explore the features of the utility in more detail.

    Bash
    ```bash
    yarn global add prisma
    ```
  2. Prisma Configuration

    Next, we need to create a small configuration for `prisma-cli`. This configuration will provide the path to the data schema file (`datamodel.prisma`), the address of prisma-server (`endpoint`), the description of files that need to be generated, as well as show where they should be placed (`generate`).

    ```yml
    endpoint: http://localhost:4466
    datamodel: datamodel.prisma
    
    generate:
      - generator: javascript-client
        output: ./prisma
      - generator: graphql-schema
        output: ./prisma/schema.graphql
    ```
  3. docker-compose for prisma-server

    Prisma-server comes in the form of a docker image, which we will use in describing the server configuration. Also, next to the prisma-server configuration, we need to describe the database configuration, since prisma-server is directly connected to the database.

    We will use `docker-compose` to describe the configuration.

    In this snippet, we described all the configurations necessary to run the Prisma server and the Postgres database using Docker.

    ```yml
    # docker-compose.yml
    version: "3"
    services:
      prisma:
        image: prismagraphql/prisma:1.14
        restart: always
        ports:
          - "4466:4466"
        environment:
          PRISMA_CONFIG: |
            port: 4466
            databases:
              default:
                connector: postgres
                host: postgres
                port: 5432
                user: prisma
                password: prisma
                migrations: true
      postgres:
        image: postgres
        restart: always
        environment:
          POSTGRES_USER: prisma
          POSTGRES_PASSWORD: prisma
        volumes:
          - postgres:/var/lib/postgresql/data
    volumes:
      postgres:
    ```

    The next step is to launch containers:

    ```bash
    docker-compose up
    # docker-compose up -d # for launch in the background move
    ```

    Once the database is initialized and the server is started, yoga graphql playground, which also comes with the Prisma server, is available at the address `http://localhost: 4466`.

    But the schema is not available at this stage and we can’t send any GraphQL queries, since the prisma-server and the database are empty. They know nothing about either the data schema or the data itself.

  4. Data Schema

    Once part of the infrastructure is ready, we can begin to describe the data schema using a syntax similar to GraphQL.

    This way, we explicitly indicate entities on which CRUD operations can be performed through the GraphQL server. The database schema is automatically configured from this model.

    ```graphql
    # datamodel.prisma
    type QuizVariant {
      label: String!
    }
    
    type QuizResults {
      variant: QuizVariant! @relation(name: "Variant")
      count: Int!
    }
    
    type Quiz {
      participantCount: Int!
      variants: [QuizVariant!]! @relation(link: TABLE, name: "QuizVariants")
      results: [QuizResults!]! @relation(link: TABLE, name: "QuizResults")
    }
    ```

    In this schema, I created three entities (`Quiz`, `QuizResults`, `QuizVariant`);

    QuizVariant

    • An entity describing a variant from a quiz.
    • Contains one `label` field with the `String!` type.

    QuizResults

    • Contains quiz results tied to a specific answer variant.
    • The `variant` field - a reference to the `QuizVariant` entity.
    • The `count` field with the `Int!` type contains the number of results for the answer variant.

    Quiz

    • The main entity in the application.
    • Stores answer variants in the `variants` field.
    • Also contains results for each variant in the `results` field.

    You can find out more about the data schema syntax from the documentation.

  5. Data Schema Deploy

    We will deploy the data schema using the small and simple `prisma-cli` command:

    Bash
    ```bash
    prisma deploy
    ```

    Once the command is successfully executed, at the address `http://localhost: 4466`, we see that the GraphQL schema is available and we can use the CRUD operations `createQuiz`, `updateQuiz`, and others for each entity.

  6. Client Generation

    The last step in setting up the Prisma infrastructure is to generate a client to work with prisma-server.

    Bash
    ```bash
    prisma generate
    ```

    In the prisma configuration, we indicated that we need `javascript-client` and `graphql-schema`.

    After the successful completion of the `generate` command, we see generated files in the `./prisma` directory.

  7. Security

    At this stage, prisma is up and running.

    But one more important detail should not go unattended - security.

    As mentioned before, `prisma` has root access to the database and does not come with ACL systems. Therefore, the prisma-server, like the database in the correct configuration, must be placed in the private network.

    Also, it is advisable to configure access to Prisma via a token. You can find out more about this from the documentation.

GraphQL Yoga

To build the last part of the environment, we have to set up a GraphQL Yoga server.

For this, we will perform a little trick: create an instance of the GraphQL server. An important condition is that we initially launch a single web server for the GraphQL API and Sapper. Therefore, we describe the GraphQL Yoga server in the server Sapper file.

JavaScript
```js
// src/server.js
import { GraphQLServer } from "graphql-yoga";

import { prisma } from "../prisma/index";

const server = new GraphQLServer({
  typeDefs: "./prisma/schema.graphql",
  resolvers: {},
  context: req => ({
...req,
prisma
  })
});
```

When creating the server, we specified in the `typeDefs` field the schema that the `prisma generate` command generated for us. This schema is available on the GraphQL public server.

We also imported the client file `prisma/index` into the context of the resolver so that the client for prisma-server is available in each resolver.

GraphQLServer + Sapper

Sapper comes as middleware, while yoga comes as a standalone server with its own routing settings and an `express` instance inside, which in turn has its own request routing configuration.

We need a single web server. Therefore, we will create our own. Below is a server snippet with comments:

JavaScript
```js
// assign an empty `express` instance to the GraphQL server
server.express = express();

// create http server instance 
server.createHttpServer({
  endpoint: "/graphql",
  playground: "/playground"
})

// use general middleware for both GraphQL endpoint/playground and Sapper
server.express.use(compression({ threshold: 0 }), sirv("static", { dev }));

// create customized query routing based on GraphQL http server settings
server.express.use("/", (req, res, next) => {
  if (/^\/(playground|graphql).*/.test(req.url)) {
return;
  }
 
  // use Sapper for other queries
  return sapper.middleware()(req, res, next);
});

// launch a server
server.start(PORT, () => {
  console.log("server started");
});
```

Internal GraphQLServer uses a pre-configured Express server. Having reassigned an empty instance to it, we were able to do our own routing and sort out requests on Sapper (which processes all requests in general) and requests on GraphQL, or open a yoga playground.

Now if we run the command to start the Sapper server in development mode: `yarn dev`, then the GraphQL Yoga server will be available at the addresses `https://localhost:3000/playground` and graphql Endpoint will available at `https://localhost:3000/graphql`, and we will see the Sapper interface on the root path `https://localhost:3000`.

Resolvers for GraphQL Server

Since prisma-server will not be accessible on the web, we need to create our own handlers for GraphQL queries from the Sapper application.

Let's create a handler for `quizzes`, which will return a list of all quizzes by default.

JavaScript
```js
// src/resolvers/quizzes.jsx

export const Query = (_parent, _args, ctx) => {
  return ctx.prisma.quizzes();
}
```

Also, let's create a file in which we will describe the `Query` and `Mutation` structure of resolvers.

JavaScript
```js
// src/resolvers/index.js
import { Query as QuizzesQuery } from "./quizzes";

export default {
  Query: {
quizzes: QuizzesQueery
  },
 
  Mutation: {}
};
```

Next, update the GraphQL server constructor by adding our resolver object.

JavaScript
```js
// src/server.js
...
import resolvers from "./resolvers"
...
const server = new GraphQLServer({
...
  resolvers: resolvers
...
})
```

Now we can try to get the list of quizzes from the database through prisma-server which we access through the GraphQL Yoga server.

The example of a query that can be done in yoga playground `http://localhost:3000/playground`.

JavaScript
```graphql
{
  quizzes {
participantCount
  }
}
```

We should receive the following response to our query:

JavaScript
```json
{
  "quizzes": []
}
```

Apollo Client for Requests From Sapper

We built the infrastructure for working with data, set up the GraphQL server, and launched it together with Sapper. But the Sapper application still does not know anything about the GraphQL server.

Let's fix this!

Install apollo-client to work with the GraphQL server.

Bash
```bash
yarn add apollo-boost @apollo/react-hooks graphql svelte-apollo
```

Create an instance of the Apollo client:

JavaScript
```js
// src/client/apollo.js
import ApolliClient from 'apollo-boost';

const client = new ApolliClient({
  // specify the address of the graphql yoga server
uri: 'http://localhost:3000/graphql'
});

export default client; 
```

Apollo is ready to use! Let's test it.

Requesting Data From Sapper Via GraphQL

Now we are ready to send data from Sapper to GraphQL API. Let's try to write the first simple request.

We will use Sapper’s `preload` hook for data preloading and then present the result in the context of the `index` route.

JavaScript
```html
<!-- src/routes/index.svelte -->
<script context="module">
  import { gql } from "apollo-boost";
  import client from "../client/apollo";

  const QUIZES_QUERY = gql`
    {
      quizzes {
        participantCount
      }
    }
  `;

  export async function preload() {
    const {
      data: { quizzes }
    } = await client.query({ query: QUIZES_QUERY });

    return {
      quizzes
    };
  }
</script>

<script>
  export let quizzes;
</script>

<h3>Quizzes in service: {quizzes.length}</h3>
```

Now if we go to https://localhost:3000, we will see the following response from the server:

HTML
```html
<h3>Quizzes in service: 0</h3>
```

Infrastructure deployment was successfully completed.

Conclusion

This article shows how to create a full-fledged web application with a database, GraphQL, and a reactive frontend.

Using Prisma, we designed a data schema for GraphQL server and database, migrated this schema to prisma-server and database.

Having created a public GraphQL server for processing requests from the Sapper application, we wrote a simple handler that requests data via prisma-client.

We made a request to GraphQL yoga in Sapper's `index` route and showed the result.

Prisma is a tool for quick prototyping of data and APIs for this data.

Sapper offers a zero-configuration approach to the quick development of reactive web applications. By combining these two tools, we got a powerful stack for prompt development.

The article was written in co-authorship with Roman Senin, a former Software Developer at Clever Solution Inc and Opporty.

In the second article, we will describe the interface and business logic of our application.

History

  • 16th January, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)