Deploy NestJS with Prisma to Heroku

Deploy a NestJS application with Prisma 2.0 to Heroku and connect to a PostgreSQL database.

Authors
Marc Stammerjohann Marc Stammerjohann
Published at

You will learn how to deploy a NestJS application with Prisma 2.0 to Heroku ๐Ÿš€. Also we will create a PostgreSQL database on Heroku and connect it with Prisma.

Check out my previous tutorial to create a PrismaService for your Nest application.

Preparing Nest application for Heroku

Let's get started by preparing our Nest application to run on Heroku.

First, we modify main.ts to use the port provided by Heroku as an environment variable. CORS can also be enabled for web or mobile applications making requests to the Nest application.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  await app.listen(process.env.PORT || 3000);
}
bootstrap();

Heroku takes care of installing our node_modules for us. Before Heroku can start our Nest application using npm run start:prod, we need to generate the PrismaClient and build our app. We add "postinstall": "npx prisma generate && npm run build", to the scripts in our package.json which generates a new PrismaClient and performs the app build, creating our dist folder.

Heroku needs to know how to execute our Nest application via a Procfile. Create a Procfile in the root folder with our start script web: npm run start:prod. Now Heroku will install our dependencies, generate Prisma Client and build the application in the postinstall script and then start the application.

New Heroku app and CLI

Next, sign up or log into your Heroku account. Create a new Heroku app by clicking on New and then Create new app.

Choose an app name to identify your app and the name is also used as your default api endpoint at https://your-app-name.herokuapp.com/. You can also choose between two regions for your app United States and Europe.

Create new Heroku app

Heroku let's you configure a custom domain in your app settings.

Alright, our heroku app is set up.

Deploy using Heroku Git

We install the Heroku CLI and deploy our Nest application by pushing to Heroku git.

heroku login

cd your-nest-app
heroku git:remote -a your-app-name

git push heroku master

After pushing our current application to Heroku, we see the following output in our terminal or in the Activity tab of our Heroku app.

First app build on Heroku

Heroku prints Build succeeded! and our application link at the end like https://nestjs-prisma-heroku.herokuapp.com/.

Let's visit our app by either clicking on the link or on Open app in the toolbar.

I am seeing Hello World!. The Nest app has been successfully deployed to Heroku ๐ŸŽ‰

Hello World!

In the next step we will setup a PostgreSQL database and use Prisma Migrate to apply our database schema.

Setup PostgreSQL and use Prisma Migrate

Navigate to Resources tab on Heroku and search for the Heroku Postgres addon.

Add Heroku Postgres addon

We select a plan for our Postgres database, we start with the Hobby Dev - Free plan and click Provision. We can upgrade the database plan later at anytime.

Select Postgres plan

Our database has been setup and it appears in the addon list.

Open Postgres Dashboard

To connect Prisma to the database, we need to provide the database connection URL found in the Settings of our database. Select Heroku Postgres and switch to the Settings tab and View Credentials.... Copy the whole URI starting with postgres://.

View Postgres Credentials

.env file support is included in Prisma 2.0. Hence, we provide the database connection URL as the environment variable DATABASE_URL.

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

Paste the URI from the Heroku Postgres dashboard into the prisma/.env file.

DATABASE_URL=postgres://ytodrxnfzdnxlr:005777fd...

Do not commit .env files into version control.

Right now our database is empty and has no tables.

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model NationalPark {
  id      Int     @id @default(autoincrement())
  name    String
  country Country @relation(fields: [countryId], references: [id])
  countryId Int
}

model Country {
  id   Int    @id @default(autoincrement())
  name String @unique
  parks NationalPark[]
}

Let's use Prisma Migrate to save and apply our migrations for the following schema.

npx prisma migrate dev

We can use Prisma Studio to view if our migration was successful. Run and open studio at http://localhost:5555.

npx prisma studio

Prisma Studio with NationalPark and Country Table

Our migration was successful ๐ŸŽ‰ We see the NationalPark and Country table was created in our database. We can use Prisma Studio to create our test data, start creating a new Country and then new NationalParks as they require a connection to a country.

Since we have our database ready, we create two REST endpoints to query all NationalParks and to create a new NationalPark in our Nest application.

Prisma CRUD operations in Nest

Before we implement our CRUD operations in Nest, we need to generate a new PrismaClient whenever we make a change to our schema.prisma or our .env file. Run npx prisma generate and now we have access to the CRUD operations of our models.

Prisma Client CRUD operations

Find Many National Parks

We setup the GET endpoint for all NationalParks at /nationalParks.

import { Controller, Get } from '@nestjs/common';
import { PrismaService } from './prisma/prisma.service';

@Controller()
export class AppController {
  constructor(private readonly prisma: PrismaService) {}

  @Get('nationalParks')
  getNationalParks() {
    return this.prisma.nationalPark.findMany();
  }
}

Start the Nest app locally in dev mode npm run start:dev and try the request at http://localhost:3000/nationalParks.

Query all National Parks without Country

I have added one national park via Prisma Studio, but we don't see the Country in the response. To return the countries in the national park response we include country in the findMany() query using the include keyword.

@Get('nationalParks')
getNationalParks() {
  return this.prisma.nationalPark.findMany({ include: { country: true } });
}

Query all National Parks with Country

Awesome, our response now includes Country.

Create New National Park

We can query our national parks, but we also want to add new national parks. Create the NationalParkDto class with the two properties name for the national park and country as the country name.

export class NationalParkDto {
  name: string;
  country: string;
}

We use this DTO class in the POST endpoint for creating a new national park.

@Post('nationalPark')
createNationalPark(@Body() nationalParkDto: NationalParkDto) {
}

As we need a country id to connect to a national park we use prisma.country.findOne to see if this country already exists in our database. Use async/await to find the country as PrismaClient CRUD operations always return Promise's.

@Post('nationalPark')
async createNationalPark(@Body() nationalParkDto: NationalParkDto) {
  const country = await this.prisma.country.findOne({
    where: { name: nationalParkDto.country },
  });

  if (country) {
    // create national park and connect country
    });
  } else {
    // create national park and create country
  }
}

When we create our national park we have two options for how to create the connection to a country. If the country exists we use connect using the country id country: { connect: { id: country.id } }. Otherwise we create the country alongside the national park country: { create: { name: nationalParkDto.country } }. Let's also return the created NationalPark including the Country in our response. Our POST endpoint looks like this:

@Post('nationalPark')
async createNationalPark(@Body() nationalParkDto: NationalParkDto) {
  const country = await this.prisma.country.findOne({
    where: { name: nationalParkDto.country },
  });

  if (country) {
    return this.prisma.nationalPark.create({
      data: {
        name: nationalParkDto.name,
        country: { connect: { id: country.id } },
      },
      include: { country: true },
    });
  } else {
    return this.prisma.nationalPark.create({
      data: {
        name: nationalParkDto.name,
        country: { create: { name: nationalParkDto.country } },
      },
      include: { country: true },
    });
  }
}

Yeah! ๐ŸŽ‰ The request works locally.

Create new National Park

We are ready to push our Nest application to Heroku again to expose the two new REST endpoints.

Push to Heroku and test new Endpoints

Run git push heroku master in your Nest application and wait for the build to succeed. Also, we need to see if the environment variable DATABASE_URL is added to the Heroku app. Head over to the Settings tab and click on Reveal Config Vars. DATABASE_URL has already been added when we installed the Heroku Postgres addon. If you like to change your database you can update the URL here.

DATABASE_URL environment variable on Heroku

Our new endpoints have been successfully deployed. Time to test it out https://nestjs-prisma-heroku.herokuapp.com/nationalParks.

Query all National Parks with Country on Heroku

Create new National Park on Heroku

To wrap up, we have successfully deployed ๐Ÿš€ our Nest application ๐Ÿ˜ป on Heroku and connected Prisma to a PostgreSQL database.

Sponsor us

Did you find this post useful? We at notiz.dev write about our experiences developing Apps, Websites and APIs and develop Open Source tools. Your support would mean a lot to us ๐Ÿ™. Receive a reward by sponsoring us on Patreon or start with a one-time donation on GitHub Sponsors.

Table of Contents

Top of Page Comments Related Articles

Related Posts

Find more posts like this one.

Authors
Marc Stammerjohann
October 17, 2022

Codegen REST API types and requests for Angular

Automatic code generation from OpenAPI 3 for Angular
Angular NestJS Read More
Authors
Marc Stammerjohann
July 27, 2022

Downloading files with NestJS

Setup type-safe endpoints for downloading files in your NestJS application.
NestJS Read More
Authors
Marc Stammerjohann
July 08, 2022

Maizzle: Craft beautiful HTML emails with Tailwind CSS

Send beautiful HTML emails via NestJS crafted with Maizzle and Tailwind CSS
Maizzle Tailwind CSS NestJS Read More
Authors
Marc Stammerjohann
November 22, 2021

Prisma Migrate: Deploy Migration with Docker

Perform database migration with Prisma Migrate using Docker
Prisma Docker Read More
Authors
Marc Stammerjohann
August 26, 2021

NestJS: Type-safe File Uploads

Learn how to apply Swagger decorators for type-safe file upload endpoints.
NestJS Read More
Authors
Marc Stammerjohann
July 27, 2022

OpenAPI for your REST APIs in NestJS

Setup Swagger to generate an OpenAPI documentation for your REST endpoints.
NestJS Read More
Authors
Marc Stammerjohann
July 08, 2022

Send Emails with NestJS

Create Email Templates and send them with nodemailer from your Nest application
NestJS Read More
Authors
Marc Stammerjohann
September 18, 2020

DBML generator for Prisma

Visualize Prisma Schema as Entity-Relationship Diagram
Prisma Read More

Sign up for our newsletter

Sign up for our newsletter to stay up to date. Sent every other week.

We care about the protection of your data. Read our Privacy Policy.