Developer Bliss- Serverless Fullstack React  with Prisma 2 and GraphQL
Tags: serverless, javascript, react, aws
Published: 2019-08-08 20:32:11

In this post, we will show how you can deploy a totally serverless stack using Prisma 2 and Next.js. This type of solution has only been recently available and while it is still in beta, it really represents a full stack developer's paradise because you can develop an app, deploy it, forget about worrying about any of the DevOps particulars and be confident that it will work regardless of load.

**Benefits:**

- One command to deploy the entire stack (Now)
- Infinitely scalable, pay for what you use (lambda functions)
- No servers to maintain (lambda functions)
- All the advantages of React (composability, reusability and strong community support)
- Server-side rendering for SEO (Next.js)
- Correctly rendered social media link shares in Facebook and Twitter (Next.js)
- Easy to evolve api (GraphQL)
- One Schema to maintain for the entire stack (Prisma 2)
- Secure secret management (Now)
- Easy to set up development environment with hot code reloading (Docker)
- Strongly typed (GraphQL and Typescript) that is autogenerated when possible (graphql-gen)

Before you start, you should go ahead and set up an RDS instance and [configured like our previous blog post](https://www.codemochi.com/blog/2019-08-07-setup-rds-for-zeit-now-deployments/).

**Videos:**

_I. Install Dependencies_
{% youtube wxUyz1obiAw %}

_II. Add Environmental Parameters_
{% youtube gsMgdMbRc_I %}

_III. Configure the Backend_
{% youtube OzaRwi2_vSA %}

_IV. Configure the Now Service_
{% youtube Kac8RJFtl3A %}

_V. Set up Now Secrets and Deploy!_
{% youtube 5do8C4rJDp0 %}

We will pick up from the example from our multi-part blog series [**[1]**](https://www.codemochi.com/blog/2019-07-08-prisma-2-nextjs-docker/), [**[2]**](https://www.codemochi.com/blog/2019-07-10-prisma-2-nextjs-docker/), [**[3]**](https://www.codemochi.com/blog/2019-07-10-prisma-2-nextjs-docker/). If you aren't interested in following along from the start, you can start by checking out the repo from the `now-serverless-start` tag:

```bash
git clone https://github.com/CaptainChemist/blog-prisma2
git fetch && git fetch --tags
git checkout now-serverless-start
```

**I. Install and clean up dependencies**

1. Upgrade to Next v9

In the `frontend/package.json` make sure that next has a version of "^9.02" or greater. Previously we were using a canary version of 8.1.1 for typescript support, but since the post version 9 of next was released so we want to make sure we can take advantage of all the latest goodies.

2. Install webpack to the frontend

As a precaution, you should install webpack to the frontend folder. I've seen inconsistent behavior with `now` where if webpack is not installed, sometimes the deploy will fail saying that it needs webpack. When I read online it sounds like it shouldn't be required so this is likely a bug, but it can't hurt to add it:

```bash
npm install --save-dev webpack
```

3. Remove the main block from `package.json` and `frontend/package.json`

When we generated our `package.json` files, it auto-populated the `main` field. Since we are not using this feature and don't even have an `index.js` file in either folder, we should go ahead and remove them. In `frontend/package.json` go ahead and remove line 5. We didn't use it previously and it has the potential to confuse the `now` service.

```json
"main": "index.js",
```

Also, do the same in the `package.json` in the root folder.

4. Install Prisma2 to the backend

Although we globally install prisma2 in our docker containers, we need to now add it to our backend package.json file so that when we use the now service it will be available during the build step up in AWS. Navigate to the `backend` folder and install prisma2:

```bash
npm install --save-dev prisma2
```

5. Install Zeit Now

We should install `now` globally so that we will be able to run it from the command line:

```bash
npm install -g now
```

**II. Add Environmental Variables**

1. Add a `.env` file to the root of your project. Add the following variables which we will use across our docker environment.

```
MYSQL_URL=mysql://root:prisma@mysql:3306/prisma
BACKEND_URL=http://backend:4000/graphql
FRONTEND_URL=http://localhost:3000
```

2. Modify the `docker-compose.yml` file to inject these new variables into our docker containers. This is what the updated file looks like:

`docker-compose.yml`

```yml
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
    environment:
      MYSQL_URL: ${MYSQL_URL}
    volumes:
      - /app/prisma
  backend:
    links:
      - mysql
    depends_on:
      - mysql
      - prisma
    container_name: backend
    ports:
      - '4000:4000'
    build:
      context: backend
      dockerfile: Dockerfile
      args:
        - MYSQL_URL=${MYSQL_URL}
    environment:
      MYSQL_URL: ${MYSQL_URL}
      FRONTEND_URL: ${FRONTEND_URL}
    volumes:
      - ./backend:/app
      - /app/node_modules
      - /app/prisma
  frontend:
    container_name: frontend
    ports:
      - '3000:3000'
    build:
      context: frontend
      dockerfile: Dockerfile
    environment:
      BACKEND_URL: ${BACKEND_URL}
    volumes:
      - ./frontend:/app
      - /app/node_modules
      - /app/.next

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

Let's take a look at the parts that were changed, below are the parts snipped out that we added to the above file:

```yml
prisma:
  environment:
    MYSQL_URL: ${MYSQL_URL}
### ..more lines ###
backend:
  build:
    context: backend
    dockerfile: Dockerfile
    args:
      - MYSQL_URL=${MYSQL_URL}
  environment:
    MYSQL_URL: ${MYSQL_URL}
    FRONTEND_URL: ${FRONTEND_URL}
### ..more lines ###
frontend:
  environment:
    BACKEND_URL: ${BACKEND_URL}
```

We added environment blocks to the prisma studio, backend, and frontend containers. Since we have the `.env` file, any variables that we define in the `.env` file, such as `VAR1=my-variable`, we can call it in the yml as \${VAR1} and that will be like we used the `my-variable` string directly in that spot of the yml file.

3. Dynamically set Backend url on the frontend

We need to set the uri that the frontend connects to dynamically instead of hardcoding it. In the `frontend/utils/init-apollo.js` we previously had this line which would connect to localhost if the request came from a user or from the backend if it came from the next.js server:

```js
uri: isBrowser ? 'http://localhost:4000' : 'http://backend:4000', // Server URL (must be absolute)
```

We need to still keep track of whether we are in the browser or server in the docker environment. In addition, though, we need to check whether we are in a docker environment or whether we are deployed via `now` into a lambda function.

We can access environment variables by using the `process.env.ENVIRONMENTAL_VARIABLE`. We check if the url matches our local environment url and if so, we know that we are in a docker environment. Now our logic is that if we are in a docker environment and the browser is making the request, we return the localhost, otherwise we pass the `BACKEND_URL` as the uri.

`frontend/utils/init-apollo.js`

```js
function create(initialState) {
  // Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
  const isBrowser = typeof window !== 'undefined'
  const isDocker = process.env.BACKEND_URL === 'http://backend:4000/graphql'
  return new ApolloClient({
    connectToDevTools: isBrowser,
    ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
    link: new HttpLink({
      uri:
        isDocker && isBrowser
          ? 'http://localhost:4000/graphql'
          : process.env.BACKEND_URL,
      credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
      // Use fetch() polyfill on the server
      fetch: !isBrowser && fetch,
    }),
    cache: new InMemoryCache().restore(initialState || {}),
  })
}
```

Now that should really be all that we need to do, but since Next.js is both rendered on the server and in the client, we won't have access to server environmental variables unless we take one more step. We need to expose the variable in our `frontend/next.config.js` file:

`frontend/next.config.js`

```js
const withCSS = require('@zeit/next-css')

module.exports = withCSS({
  target: 'serverless',
  env: {
    BACKEND_URL: process.env.BACKEND_URL,
  },
})
```

Note that due to how exactly Next.js handles process.env, you cannot destructure variables off of it. So the line below will _not_ work, we need to use the entire `process.env.BACKEND_URL` variable.

```js
const { BACKEND_URL } = process.env // NO!
```

**III. Configure our backend server**

1. Update the backend server to the `/graphql` backend and configure CORS

We updated the url above to the `/graphql` endpoint for the backend server. We are doing this because in `now` we will deploy our backend graphql server to `ourdomain.com/graphql`. We need to make this change in our `backend/src/index.ts` so that the server will be running at the `/graphql` endpoint instead of `/`.

In addition, while we are here, we will disable subscriptions and enable CORS. CORS stands for _cross origin resource sharing_ and it tells the backend server which frontend servers it should accept requests from. This ensures that if someone else stood up a frontend next server that pointed to our backend server that all requests would fail. We need this because you could imagine how damaging this could potentially be if someone bought a domain `crazyamazondeals.com` (I'm just making this up) and pointed their frontend server to the real backend server of amazon's shopping portal. This would allow a fake amazon frontend to gather all sorts of customer information while still sending real requests to amazon's actual backend server. Yikes!

In order to enable CORS we will pass in our frontend url. We will also enable credentials for future authentication-related purposes.

`backend/src/index.ts`

```ts
server.start(
  {
    endpoint: '/graphql',
    playground: '/graphql',
    subscriptions: false,
    cors: {
      credentials: true,
      origin: process.env.FRONTEND_URL,
    },
  },
  () => console.log(`🚀 Server ready`)
)
```

2. Update the `backend/prisma/project.prisma` file to use environmental variables and set our platform.

We can use the `env("MYSQL_URL")` which will take our `MYSQL_URL` environmental variable. Starting with prisma preview-3+ we need to specify which platforms that we plan to use with prisma2. We can use "native" for our docker work, but we need to use "linux-glibc-libssl1.0.2" for Zeit Now.

`backend/prisma/project.prisma`

```prisma
datasource db {
  provider = "mysql"
  url      = env("MYSQL_URL")
}

generator photon {
  provider  = "photonjs"
  platforms = ["native", "linux-glibc-libssl1.0.2"]
}
// Rest of file
```

3. Update the `backend/Dockerfile` to pass the environmental variable into the prisma2 generate. We first have to define a docker argument using `ARG` named `MYSQL_URL`. Then, we take the `MYSQL_URL` environmental variable and assign it to this newly created `ARG`.

We need the `MYSQL_URL` environment variable so that our url from the prisma file gets evaluated properly.

`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/

ARG MYSQL_URL
ENV MYSQL_URL "$MYSQL_URL"

RUN npm install
RUN prisma2 generate

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

Note that the only reason we have access to the `$MYSQL_URL` variable in this Dockerfile is due to an `args` block that we previously added to the docker-compose.yml file. Adding variables to the `environment` block of docker-compose is only accessible during the runtime of the containers, not the building step which is where we are at when the Dockerfile is being executed.

```yml
backend:
  build:
    context: backend
    dockerfile: Dockerfile
    args:
      - MYSQL_URL=${MYSQL_URL}
```

**IV. Add our Now Configuration**

1. Create now secrets

Locally, we have been using the `.env` file to store our secrets. Although we commit that file to our repo, the only reason why we can do that is because there are no sensitive environmental variables there. Ensure that if you ever add real secrets to that file, such as a stripe key, you need to never commit that to github or else you risk them being compromised!

For production, we need a more secure way to store secrets. `Now` provides a nice way to do this:

```bash
now secret add my_secret my_value
```

`Now` will encrypt and store these secrets on their servers and when we upload our app we can use them but we won't be able to read them out even if we try to be sneaky and read it out using console.logs. We need to create variables for the following variables that were in our `.env` file:

```
MYSQL_URL=mysql://user:password@your-mysql-database-url:3306/prisma
BACKEND_URL=https://your-now-url.sh/graphql
FRONTEND_URL=https://your-now-url
```

Note that by default `your-now-url` will be `yourProjecFoldername.yourNowUsername.now.sh` but you can always skip this step for now, get to Step V of this tutorial, deploy your site and then look at where it deploys to because it will be the last line of the console output. Then you come back to this step and add the now secrets and redeploy the site.

2. Add a `now.json` file to the root directory

We need to create a `now.json` file which will dictate details about how we should deploy our site. The first part of it has environmental variables for both the build and the runtime. We will be using secrets that we created in the previous step by using the `@our-secret-name`. If you forget what names you used, you can always type `now secrets ls` and you will get the names of the secrets (but critically _not_ the secrets themselves).

Next we have to define our build steps. In our case we have to build both our nextjs application and our graphql-yoga server. The nextjs is built using a specially designed `@now/next` builder and we can just point it to our `next.config.js` file which is in our `frontend` folder. Our other build will use the `index.ts` file in our `backend/src` directory and the builder is smart enough to compile the code down into javascript and deploy it to a lambda function.

Finally, we have to define our routes. The backend server will end up at the `/graphql` endpoint while the frontend directory will use everything else. This ensures that any page we go to under `ourdomain.com` will be forwarded onto the nextjs server _except_ the `/graphql` endpoint.

`now.json`

```json
{
  "version": 2,
  "build": {
    "env": {
      "MYSQL_URL": "@mysql_url",
      "BACKEND_URL": "@backend_url",
      "FRONTEND_URL": "@frontend_url"
    }
  },
  "env": {
    "MYSQL_URL": "@mysql_url",
    "BACKEND_URL": "@backend_url",
    "FRONTEND_URL": "@frontend_url"
  },
  "builds": [
    {
      "src": "frontend/next.config.js",
      "use": "@now/next"
    },
    {
      "src": "backend/src/index.ts",
      "use": "@now/node",
      "config": { "maxLambdaSize": "20mb" }
    }
  ],
  "routes": [
    { "src": "/graphql", "dest": "/backend/src/index.ts" },
    {
      "src": "/(.*)",
      "dest": "/frontend/$1",
      "headers": {
        "x-request-path": "$1"
      }
    }
  ]
}
```

3. Add a `.nowignore` file to the root directory

Finally, we can add our ignore file which will tell now which things it shouldn't bother to upload.

`.nowignore`

```
**/node_modules
.next
Dockerfile
README.MD
```

**V. Deploy our now full stack site**

This part is easy. Simply type `now` from the root folder and let it fly!

**There is more where that came from!**
I created an entire course about using Zeit Now + Next.js to build a recipe sharing application, so if you liked this go check it out!
![Next Chop Image](https://courses.codemochi.com/static/banner-7fcb9baf6ae2814c06c77c89c83d8c59.png)
[Frontend Serverless with React and GraphQL](https://courses.codemochi.com/frontend-serverless-with-react-and-graphql)



[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-08-12-prisma-2-now/).