Create a Fullstack Blog App with Next.js, Prisma 2 and Docker- Part I Setup the Repo and Configure the Backend
Tags: javascript, fullstack, tutorial, react
Published: 2019-07-08 15:42:26


This post will go through how to use Docker to create a full stack javascript application with Next.js for server side rendered goodness and Prisma 2 backed GraphQL server all orchestrated by Docker-Compose.

**Why Docker?**

Docker has revolutionized web development by separating out different parts of your technology stack into separate Docker Containers. By developing your code in Docker, you can ensure that it will work exactly the same in your development environment as it does in production.

![Architectural drawing with Docker, Prisma 2, and Next.js.](https://www.codemochi.com/static/830e0429aee7223aae900ed1157ddd57/65f42/architecture.png)

**How is it organized?**

We run Next.js in a frontend container, GraphQL Yoga connected to Prisma in a backend container, and the Prisma Studio UI in a third container. We read from a MySQL database in a fourth container that we make available to the Prisma UI and the backend server.

![Our finished blog application](https://www.codemochi.com/static/38f05743b628162730a007394db815e8/9424f/finished-app.png)

**What are we building?**

We are building a blog web app based on the example blog project that comes with the Prisma 2 CLI. Here are the actions we can perform from the backend:

_Queries:_

- Read all published blog posts
- Read all draft blog posts
- Read all users

_Mutations:_

- Create a user
- Create a blog draft
- Publish a blog
- Delete a blog

Obviously, in a real application, you’d never allow anyone to see all the users or unpublished blog posts- but doing this for here so we can see all the posts and users as they are created and modified right from our Next.js website.

**Where's the code?**

[We have the final code posted over on Github.](https://github.com/CaptainChemist/blog-prisma2)

#Table of Contents:

**Part I- Set up the Repo and Configure the Backend (this post)**

1. Create the base repo
2. Create the Next.js frontend
3. Create the boilerplate Prisma 2 backend
4. Dockerize our web app

   a. Create a docker-compose file

   b. Add Dockerfiles for each container

5. Configure the backend

   a. Switch database from SQLite to MySQL

   b. Remove unused backend boilerplate code

   c. Update backend queries in Prisma Nexus

6. Verify our Docker-Compose cluster works

**Part II- Configure the Frontend**

1.  Add GraphQL fragments, queries and resolvers
2.  Add GraphQL-Codegen for generating React Components
3.  Add Apollo and create HOC for Next.js
4.  Add React-Apollo to project root
5.  Install Antd for a beautiful site layout

**Part III- Build the UI**

1. Create the Main Layout
2. Create a Users Layout Component
3. Create a Signup User Component
4. Create a Feed Layout Component
5. Create a New Draft Component
6. Create a Publish Draft Button Component
7. Create a Delete Post Button Component

Below we will go through Part I of this outline- be sure to skip to the end for videos that walk through this whole process.

#Part I- Setup the Repo and Configure the Backend

**1. Create the base repo**

First let's create a project and set the correct version of node using nvm. If you haven't installed nvm yet, its a tool that allows you to switch between different versions of node and npm. You can [check it out here.](https://github.com/nvm-sh/nvm)

```
mkdir blog-prisma2
cd blog-prisma2/
nvm use 10.15.3
```

Now we can initialize the project with `npm` and `git`. As you might know from you [previous blog post](https://www.codemochi.com/blog/git-branches-for-organized-code/) I'm a huge stickler for good branching practices, so let's create a staging and feature branch now.

```
npm init -y
git init
git checkout -b staging
git checkout -b base
```

Let's now create a `.gitignore` in the root of our project and add all the file names and folders that we won't want committed in our repo. It's important that we do this before running any `npm install` commands because the number of files in `node_modules` is enormous and we don't want to commit those.

```.gitignore
logs
*.log
npm-debug.log*

pids
*.pid
*.seed

build/Release
**/node_modules

.DS_Store
.next/
```

Now run these commands to make our first commit. Feel free to use Git Kraken or your favorite git tool:

```
git add .gitignore
git commit -am 'added gitignore'
```

**2. Create the Next.js frontend**

Now let's create a folder and make a new npm project within that folder. Then we can add react, next.js, a css loader for next.js and all the typescript dependencies.

```
mkdir frontend
cd frontend
npm init -y
npm install --save next react react-dom @zeit/next-css
npm install --save-dev @types/node @types/react @types/react-dom typescript
```

Now we can tell typescript to actually run using a `tsconfig.json` file. Make `frontend/tsconfig.json`:

```json
{
  "compilerOptions": {
    "allowJs": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "lib": ["dom", "es2017"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "esnext"
  },
  "exclude": ["node_modules"],
  "include": ["**/*.ts", "**/*.tsx"]
}
```

Toward the end of this tutorial we will use `antd` for all of our styling so let's go ahead and add css support so we can use the stylesheet when we get to that point. Create a next-config file and add the css plugin to it:

make `frontend/next.config.js`:

```js
const withCSS = require('@zeit/next-css')
module.exports = withCSS({})
```

Now we can make a `frontend/next-env.d.ts` file which contains a reference for the next.js types used in our project:

```ts
/// <reference types="next" />
/// <reference types="next/types/global" />
```

Now that we have these base files, we can actually start creating our react components. First to get organized, make `frontend/components` and `frontend/pages` folders. Then create `frontend/pages/index.tsx`:

```tsx
import * as React from 'react'
import { NextPage } from 'next'

const IndexPage: NextPage = () => {
  return <h1>Index Page</h1>
}

export default IndexPage
```

Next.js uses the convention that React components in the pages directory are routes for the website. The index page represents the `/` route and in our React component above, we are just creating a page that simply displays an h1 heading.

Now we need to add the next.js scripts to the `frontend/package.json` file so that we can easily start and stop our frontend server:

```json
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "type-check": "tsc"
  },
```

Next.js 8.1.1 natively supports typescript which we will need for our project. Make a quick check that your version in your `frontend/package.json` file is up to speed. As of early July 2019, doing an `npm install` of Next.js will yield 8.1.0 so I needed to manually modify the version in `package.json` to:

```json
"next": "^8.1.1-canary.61",
```

If you need to update it, make sure you run an `npm install` after editing the `package.json` file so that you fetch the latest version.

Next, start the server by running `npm run dev`. Go to `http://localhost:3000` and confirm that the index page comes up:

![Base Next.js website running on our computer.](https://www.codemochi.com/static/98df867411fedcd6234f2b88ebc69bd6/feedf/starter-frontend.png)

Once you are satisfied that it is running, stop the frontend server by pressing ctrl+c.

**3. Create the boilerplate Prisma 2 backend**

Now we have to make our backend. Navigate back to the `blog-prisma2` directory and initialize Prisma 2.

```
npm install -g prisma2
prisma2 init backend
  // Select SQLite
  // Select Photon and Lift
  // Select Typescript
  // Select GraphQL Boilerplate
```

This process will create a folder called backend which it will clone a blog backend into using Prisma 2. We could just start it up and move from there but we will instead start the process to get it integrated into docker right off the bat so we don't have to mess with SQLite at all and will instead hook our backend up to a MySQL database from the start.

**4. Dockerize our web app**

_a. Create a docker-compose file._

Now we want to dockerize our application. Create `docker-compose.yml` in the root of the project.

```yaml
version: '3.7'
services:
  mysql:
    container_name: mysql
    ports:
      - '3306:3306'
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: prisma
      MYSQL_ROOT_PASSWORD: prisma
    volumes:
      - mysql:/var/lib/mysql
  prisma:
    links:
      - mysql
    depends_on:
      - mysql
    container_name: prisma
    ports:
      - '5555:5555'
    build:
      context: backend/prisma
      dockerfile: Dockerfile
    volumes:
      - /app/prisma
  backend:
    links:
      - mysql
    depends_on:
      - mysql
    container_name: backend
    ports:
      - '4000:4000'
    build:
      context: backend
      dockerfile: Dockerfile
    volumes:
      - ./backend:/app
      - /app/node_modules
      - /app/prisma
  frontend:
    container_name: frontend
    ports:
      - '3000:3000'
    build:
      context: frontend
      dockerfile: Dockerfile
    volumes:
      - ./frontend:/app
      - /app/node_modules
      - /app/.next

volumes: #define our mysql volume used above
  mysql:
```

Let's take a look over this file. It's divided into 4 services: mysql, prisma, backend, and frontend. We've already created our frontend server and tested it outside a docker container and now we will move it in. The prisma container is for the Prisma Studio UI, the backend is our backend server, and mysql is our database. Here are the key fields in each service and what they do:

- `container_name` this is what we will call our container- for simplicity have it match the service name.

- `image` if we are downloading an image from docker hub, we will put it here.

- `build` if we are _not_ downloading from docker hub we will build our image and this block gives instructions about which folder is the working directory for the build and what the name of our `Dockerfile` is (which we will create below).

- `environment` Any environmental variables go here.

- `restart` Ensures that we restart our container if it dies.

- `links` and `depends_on` sets up a connection between the two containers and specifies that a particular container should wait on a different container before starting.

- `volumes` specifies which kind of volumes the containers should create. One that has a `:` in it means that we are creating a link between a folder on our computer and a path in our container. The kind without a colon just means that it will save that folder during the build step so we can use it when the container runs. This is important for `node_modules` for example because we want to make sure that our docker container keeps that folder during the `npm install` step of the build phase because that folder is needed to run our application.

Now let's add some scripts to make our life easier in the _base_ (not frontend or backend) `package.json` file:

```json
    "start": "docker-compose up",
    "build": "docker-compose build",
    "stop": "docker-compose down",
    "clean": "docker system prune -af",
    "clean:volumes": "docker volume prune -f",
    "seed": "docker exec -it prisma npm run seed",
```

_b. Add Dockerfiles for each container._

Now we need to create `Dockerfile`s for the frontend, backend, and prisma containers. These files contain the steps needed to standup a server. Add the following three Dockerfiles:

`frontend/Dockerfile`:

```Dockerfile
FROM node:10.16.0

RUN mkdir /app
WORKDIR /app

COPY package*.json ./
RUN npm install

CMD [ "npm", "run", "dev" ]
```

`backend/Dockerfile`:

```Dockerfile
FROM node:10.16.0
RUN npm install -g --unsafe-perm prisma2

RUN mkdir /app
WORKDIR /app

COPY package*.json ./
COPY prisma ./prisma/

RUN npm install
RUN prisma2 generate

CMD [ "npm", "start" ]
```

`backend/prisma/Dockerfile`:

```Dockerfile
FROM node:10.16.0
RUN npm install -g --unsafe-perm prisma2

RUN mkdir /app
WORKDIR /app

COPY ./ ./prisma/

CMD [ "prisma2", "dev"]
```

All of them start with a `FROM` block which is the image that we are pulling. In our case, we are using the official release of Node.js. Then, we create an `app` folder that we copy the `package.json` and `package-lock.json` into so that we can run an `npm install` to get all of our packages.

We copy the `prisma` folder into our backend server so that we can generate a prisma dependency that is built from our `prisma.project` file. The cool thing here is that as we modify our schema, the dependency that is generated will change to match it. The prisma container requires the prisma folder in order to perform migrations against our database to create all the different tables needed to match our schema.

Our frontend `Dockerfile` is simpler because it only needs to install all the package dependencies and doesn't need to know about the `prisma` folder.

**Configure the backend**

_a. Switch database from SQLite to MySQL_

We have our docker-compose file, but one thing you'll notice is that we use MySQL in this file while we specify SQLite in the Prisma set up step. Let's fix that by updating `backend/prisma/project.prisma` file. Update the datasource db block with this:

```project.prisma
datasource db {
  provider = "mysql"
  url      = "mysql://root:prisma@mysql:3306/prisma"
}
```

Note that we are giving it a connection string with a password and database name that we specified in the `docker-compose.yml` file.

_b. Remove unused backend boilerplate code_

Now we can delete the following file that we won't be using for our project.

```
backend/src/permissions/*
backend/src/resolvers/*
backend/src/utils.ts
backend/README.md`
```

_c. Update backend queries in Prisma Nexus_

In the backend/src/index.ts file, add a users query:

```tsx
t.list.field('users', {
  type: 'User',
  resolve: (parent, args, ctx) => {
    return ctx.photon.users.findMany({})
  },
})
```

In the same file, add a boolean input to the `feed` query called `published` where we can either specify if we want published or non published posts. Make sure to add the `booleanArg` to the import for `@prisma/nexus` at the top of the file:

```tsx
import {
  idArg,
  makeSchema,
  objectType,
  stringArg,
  booleanArg,
} from '@prisma/nexus'
// Lines of code omitted for brevity...
//
//
t.list.field('feed', {
  type: 'Post',
  args: {
    published: booleanArg(),
  },
  resolve: (parent, { published }, ctx) => {
    return ctx.photon.posts.findMany({
      where: { published },
    })
  },
})
```

**6. Verify our Docker-Compose cluster works**

We use `npm run build` to build our images, then `npm start` to start up our project. We can stop our project with `npm run stop` and clean our volumes, images and containers using the clean commands.

Going forward, if we install new package dependencies using npm, we need to stop our project and re-build it to ensure that our docker images are up to date. When in doubt an `npm stop` followed by `npm run clean` should do the trick.

Now we need to build our images to make sure they work. From the root of the project type:

```
npm run build
```

Now that we've build the image, let's start it all up `npm start`. Start by going to `http://localhost:3000`. We should see our Next.js app:

![Base Next.js website running in docker.](https://www.codemochi.com/static/98df867411fedcd6234f2b88ebc69bd6/feedf/starter-frontend.png)

Now let's go to `http://localhost:4000`, we should see our backend GraphQL playground:

![GraphQL Playground website running in docker.](https://www.codemochi.com/static/dd1bd839fd2dfb5ff37a76141fe378d9/feedf/starter-backend.png)

Finally, lets go to `http://localhost:5555`, we should see the Prisma Studio application:

![Prisma Studio website running in docker.](https://www.codemochi.com/static/3c1505c84ddff2ed9a995e40d2ef8c81/feedf/starter-prisma.png)

Awesome! What we've done is make a starter docker-compose environment where we are running 3 webservers and a database server that we can start with a single command. Now let's save our work in git and then simplify it all so we have a good place to build off of.

```
git add .
git commit -am 'added base projects'
```

Now we have made all the changes we need for the backend. In Part II of this post, we will move onto the frontend.

**Video Series for Part I:**

_Architectural Overview_
{% youtube DmcgOzWy6xI %}

_What we are building_
{% youtube uMSi5eRE8tM %}

_Set up our project with Git and NPM_
{% youtube XWAZeyKVQLk %}

_Create Next.js Frontend_
{% youtube MBn6NWiRwy8 %}

_Create Prisma 2 Backend_
{% youtube bLQOftTh9Zg %}

_Create our Docker Compose_
{% youtube za25acTaGPQ %}

_Create our Dockerfiles_
{% youtube -IOjuo0VpdY %}

_Connect Prisma 2 to MySQL_
{% youtube VcrsVmTxHp0 %}

_Add new Queries to our Backend_
{% youtube s-GTfAF3XxY %}

_Start up our Docker Environment_
{% youtube tSXzPDwmpbM %}

There is more where that came from!
[Click here to give us your email and we'll let you know when we publish new stuff.](https://gmail.us20.list-manage.com/subscribe?u=37f38485b2c7cff2f3d9935b5&id=e3bc056dde) We respect your email privacy, we will never spam you and you can unsubscribe anytime.

Originally posted at [Code Mochi](https://www.codemochi.com/blog/2019-07-10-prisma-2-nextjs-docker/).