How to deploy a Node.js and PostgreSQL App on Render

The news that Heroku is discontinuing its free tier has sparked widespread concern in the developer community. Since then, many developers have been looking for free alternative hosting platforms to host and test their web applications. To address these concerns, Render was created to provide developers with the quickest way to host their web apps, databases, cron jobs, and workers using a free tier account.

This tutorial will teach you how to deploy a Node.js and PostgreSQL App on Render.

What is Render?

Render is a unified cloud platform that allows you to build and run all of your apps and websites while providing free TLS certificates, a global CDN, DDoS protection, private networks, and Git auto deploys. In addition, Render allows you to host static sites, backend APIs, databases, cron jobs, and other types of applications in a single location.

Prerequisites

To follow this tutorial, ensure you have the following installed on your computer.

Also, the code for this tutorial is available on GitHub. Feel free to clone and follow along.

Set up Express app

If you’re setting up your project from scratch, create a new folder for the project and enter it. Then create a package.json file by running the following command in your terminal.

npm init -y

Next, create an src folder in the folder and in the src folder, create a server.ts file. We’ll use this file later to set up our Express server.

Set up TypeScript

Next, we’ll add TypeScript as a dev dependency. We’ll also install the Express and Node.js @types declaration packages, which provide type definitions in the form of declaration files. We’ll do that by running this command below:

npm i -D typescript @types/express @types/node

In the above command, we added the -D flag to install the packages as devDependencies. Create a tsconfig file in the project’s root directory and add the following configurations:

{
  "compilerOptions": {
    "lib": ["es5", "es6", "dom"],
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "rootDir": "./",
    "outDir": "./dist"
  }
}

Install dependencies

Now, let’s install the third-party dependencies required to run this application. First, we’ll install dotenv, pg, typeorm and their types by running this command:

npm install dotenv pg typeorm

We’ll use typeorm to connect to our database, create entities, and use the data it provides to perform CRUD operations. In the server.ts file, create an Express server with the code snippets below:

import  express, { Express } from 'express';
import * as dotenv from 'dotenv'
dotenv.config();

const app: Express = express();
const PORT = process.env.PORT;

app.use(express.json());

app.listen(PORT, ()=> console.log(`Server listening to port ${PORT}`));

In the above code snippet, we’ve set up an express server, used the dotenv package to load our environment variables, and set up middleware to parse the request payload.

Configure database

Let’s configure our application to connect to a PostgreSQL database using the typeorm package we installed. To start, create a config/dataSource.ts file and add the code snippets below:

import { DataSource } from "typeorm";
export const myDataSource = new DataSource({
  type: "postgres",
  host: "localhost",
  port: 5432,
  username: "<YOUR DB USERNAME>",
  password: "<YOUR DB PASSWORD>",
  database: "<YOUR DATABASE NAME>",
  entities: [__dirname + '/../**/*.entity.js'],
  synchronize: true,
});

In the above code snippet, we imported the typeorm DataSource class and passed the database credentials. Replace <YOUR DB USERNAME>, <YOUR DB PASSWORD>, and <YOUR DATABASE NAME> with your database username, password, and database name, respectively. We also specified the location for our entities and set synchronize to true to synchronize our database. Now, update the code in the server.ts file to initialize the data source with the code snippets below:

//...

myDataSource
    .initialize()
    .then(() => {
        console.log("Data Source initialized!")
    })
    .catch((err) => {
        console.error(err)
    })
//...

Create entity

With our database initialized, let’s create a task entity and define the structure of the entity class. To do that, create an entity/task.entity.ts file src folder and add these code snippets:

import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";

@Entity()
export class Task {
    @PrimaryGeneratedColumn()
    id: string;
    
    @Column()
    name: string;
    
    @Column()
    completed: boolean;
}

In the above code snippets, we imported the Entity, which will map the Task class to a database table, the Column decorator to define our entity properties, and the @PrimaryGeneratedColumn() to create a randomly generated id for each data in our table.

Create services

Now, let’s create some CRUD services to add and store some tasks in our database. To do that, make a service/taskService.ts and add the following code snippets:

import { Task } from "../entity/task.entity";
import { myDataSource } from "../config/dataSource";

export const getAll = async (): Promise<Task[]> => {
  return await myDataSource.getRepository(Task).find();
};

export const create = async (task: Task): Promise<Task> => {
  return await myDataSource.getRepository(Task).save(task);
};

export const updateOne = async (id: string): Promise<any> => {
  return await myDataSource
    .getRepository(Task)
    .update({ id }, { completed: true });
};

In the above snippet, we imported our task entity and the dataSource object we exported from our database. Then, we created a function to fetch all the tasks, get one task, and update a task using the getRepository function, which takes our entity id as a parameter. The getRepository function provides us with the methods to perform CRUD operations in our database.

Create controllers

Next, let’s create some controllers to handle the incoming requests on our application and return the required responses. Create a controllers/taskController.ts file in the src folder and add the code snippets below:

import { Request, Response } from "express";
import { create, getAll, updateOne } from "../service/taskService";

export const createTask = async (req: Request, res: Response) => {
  try {
    const { name } = req.body;
    const task = await create({ name, completed: false, id: null});
    res.status(200).json(task);
  } catch (e) {
    res.status(500).json("an error occurred");
    console.log(e);
  }
};

export const getAllTask = async (req: Request, res: Response) => {
  try {
    const tasks = await getAll();
    res.status(201).json(tasks);
  } catch (e) {
    res.status(500).json("an error occurred");
    console.log(e);
  }

};

export const updateTask = async (req: Request, res: Response) => {
  try {
    const tasks = await updateOne(req.params.id);
    res.status(201).json(tasks);
  } catch (e) {
    res.status(500).json("an error occurred");
    console.log(e);
  }
};

In the above code snippets, we imported the service functions we created and created the controller functions for each service.

Create routes

Now, let’s create the route handlers for the controllers we have created. To do that, create a routes/routeController.ts file in the src folder and add this code snippet:

import {
  createTask,
  getAllTask,
  updateTask,
} from "../controllers/taskController";
import express, { Router } from "express";

const router: Router = express.Router();

router.route("/task").get(getAllTask).post(createTask).put(updateTask);

export default router;

Then update the server.ts file to use the routes we’ve defined with the code snippet below:

//...
app.use('/api', router);
//...

Deploy on Render

At this point, our application is set. Let’s proceed to deploy it on Render. Sign up for free on Render with your GitHub, GitLab, or Gmail account to get started.

create a render account

Once you’ve signed up and confirmed your email, you’ll be redirected to your Render dashboard.

render dashboard

Then click on the New + button to select the service you want to host.

Host the database

We’ll start by hosting our Postgres database, so select PostgreSQL from the dropdown list.

host a database on render

Next, enter the details for the database. Enter the name and leave the type as free tier. Choose at least version 14.

set up database details

Then press the Create Database button and save the credentials in a safe place.

Host the app

With the database hosted, let’s proceed to hosting the application. Before that, update the code in the config/dataSource.ts file to use the credentials from Render:

import { DataSource } from "typeorm";
export const myDataSource = new DataSource({
  type: "postgres",
  host: process.env.HOSTNAME,
  port: 5432,
  username: process.env.USERNAME,
  password: process.env.PASSWORD,
  database: process.env.DATABASE_NAME,
  entities: [__dirname + '/../**/*.entity.js'],
  synchronize: true,
});

Then update the package.json to have the scripts below for use on the Render platform:

 ...
  "scripts": {
    "build": "tsc",
    "start": "node ./dist/src/server.js"
  },
  ...

Push the code to GitHub. Now click on the Web Service tab from your Render dashboard:

host an application on Render

Next, select the project’s GitHub repository and enter the following details:

  • Name: express-app-demo
  • Build Command: npm i; npm run build
  • Start Command: npm run start
  • Plan Type: Free

Then scroll down and click on the Advanced button, click on the Add Environment Variable and add the following database credentials from your Render database for:

HOSTNAME= <HOSTNAME>
USERNAME= <USERNAME>
PASSWORD= <PASSWORD>
DATABASE_NAME= <DATABASE_NAME>
PORT = 5432

Finally, click the Create Service button and wait for the application deployment to complete.

deploy app with Render

Once the deployment is finished, the application status will show Deploy succeeded.

deploy succeeded

Now, you can go ahead and test out the API with this URL: https://express-app-demo.onrender.com/api/task.

Please note that the URL used in this tutorial may be different from the one you will use in your own implementation. It’s recommended to test the API using your own URL to ensure proper setup and configuration.

Test the API

Finally, to demonstrate how to interact with a hosted API using cURL, let’s create and retrieve todos.

Use the following command to send a POST request to create a todo:

curl -X POST -H "Content-Type: application/json" -d '{"name":"my todo"}' <Your Render API URL>

After sending a POST request to create a todo, you can use the following command to send a GET request to retrieve the todo that was created:

curl -X GET <Your Render API URL> 

Conclusion

This tutorial taught us how to deploy a Node.js and PostgreSQL App on Render. First, we started by introducing Render. Then, as a demonstration, we built a Node.js RESTful web application to manage a task and deployed it on Render. 

Render is an exciting tool, and I recommend checking out the documentation to learn more. I hope you enjoyed this article and happy coding!

If you like articles like these, browse the Mattermost Library and continue your learning.