Get Started with the PERN Stack: An Introduction and Implementation Guide

Rita Alves
9 min readMar 2, 2023

--

Choosing a web development stack depends on your final product’s desired features and capabilities as well as your development team’s qualifications. The PERN Stack is one of the most popular stacks out there — and this article will introduce you to its basics and how you can use it to build an application.

What is the PERN Stack

Alongside MERN, LAMP, MEAN, and MEVN, the PERN Stack is one of many useful sets of technologies to build web or mobile applications. As the acronym suggests, it is comprised of four key components: PostgreSQL, Express, React, and Node.js. When the components are combined, you can build a full-stack application with CRUD operations.

Let’s go through each component and understand a little bit more about them:

  • PostgreSQL is an open-source object-relational database management system (ORDBMS) that supports both SQL (relational) and JSON (non-relational) querying. It’s ACID-compliant and table-based, with complete constraints, triggers, and roles. These features are what allow for the creation of relationships between data. This, in turn, leads to benefits such as extensibility, scalability, reliability, performance, and robustness.
  • Express is a popular and free JavaScript web framework designed for use with Node.js. Its purpose is to build web applications and, especially, APIs, quickly and easily. It provides a set of features for the routing, middleware and handling of HTTP requests and responses. In the PERN Stack, this component handles the application back-end.
  • React is a JavaScript library for building user interfaces. In this stack, this component is responsible for the front-end.
  • Node.js is a JavaScript runtime for developing server-side and networking applications, such as web servers, easily.

Here’s an illustration that shows the flow of data in the PERN Stack:

Understanding the Data Flow in the PERN Stack

How to implement the PERN Stack

Now that everything is explained and you understand this stack better, let’s move on to the practical part: A step-by-step example of how to implement the PERN Stack.

Prerequisites:

First, make sure you have the right tools. The following is a list of software you need to complete this implementation successfully:

If you already have all this, you’re set to begin! In the following section, you have a guide with all the necessary and essential steps to implement PERN Stack. So, let’s get on this journey!

Step 1: Project Structure

Build a new folder for the project

Let’s start this by creating a new folder for the project.

This main folder should have two folders in it: /server and /client, which, as their name tells, one will have the files related to the server (API) and the other one will have the files related to the client (UI).

This will help keep your project clean and organized.

Initialize the project

Server: Initialize the project using npm init in the server folder to create a Node.js project. Next, install the required dependencies for this basic PERN Stack example using npm:

  • express: to use the Express web framework in the project;
  • pg: to use PostgreSQL in the project;
  • dotenv: to securely store and manage environment variables in the project, while separating them from code.

Client: Initialize the project using npx create-react-app on the client folder. After that, we need to install the required dependencies, this time for the front-end:

  • axios: to enable making HTTP requests from the front-end.

When all of this is done, you’ll have something like this:

Step 2: Database

Now that we have the project base, we need a database to store our data, so let’s use PostgreSQL.

Set up PostgreSQL

To simplify this example, we need a PostgreSQL container instead of configuring it manually. To do so, we need to use a Docker PostgreSQL image:

docker run --name <container_name> -e POSTGRES_USER=<postgres_user> -e POSTGRES_PASSWORD=<postgres_password> -p 5432:5432 -d postgres 

Create the database and populate it

Create the database and populate it with the desired tables, data, and relationships.

This can be done in an easy way:

  • Create a SQL script that runs and populates the database. To do this, I used the script below to create a table with a few random items (as an example, I used some products):

After that, copy the SQL script to the inside of the container and execute it:

docker cp ./<path_to_file>/init.sql <container_name>:/tmp/init.sql
docker exec -ti <container_name> /bin/bash -c "psql -U <postgres_user> -d <postgres_database> -f /tmp/init.sql"

Alternatively, if you want, you can do it by:

  • Establishing a bash session with docker exec -ti <container_id> bash and then use psql;
  • Using pgAdmin.

Step 3: Building the API

All great! Time to build our API!

The API starting point is index.js, the file you need to create in the server folder. This file will define all the routes and import express. Additionally, it also has the necessary configurations set so that Node.js server runs on the desired port and starts listening for incoming requests.

Create the API

Change the file index.js to create the API.

Run the API

Now, if you run node index.js or npx nodemon index.js on our terminal, the following message will show up on your console:

Server listening on the port 9000

Test the API

To test if the API is working use Postman to make the request. In this case, a simple GET request to http://localhost:9000/ should return the string “Hello World!”.

Note that this is just the basis of PERN Stack. Normally, a web application requires numerous requests that can make projects a little bit confusing. To make bigger projects more organized, and less confusing, there are some optional files that can help us fix this problem, such as:

  • Routes: Forward the requests to the appropriate controller functions, to handle them;
  • Controllers: Contain functions to handle specific routes and determine the appropriate response to send back to the user;
  • Services: Contain functions to handle possible errors;
  • Database files: Contain functions to perform CRUD operations in a database.

Step 4: Configure PostgreSQL connection

Create a connection between the database and the API

Now that we have built the API and everything is working as expected, it is necessary to create a connection between the database and the API.

For that, we need to create a file for this matter (db.config.js), a configuration file in which you connect to PostgreSQL with the help of a connection string.

Below we are using pg to create a connection pool, which is basically a cache of database connections maintained for efficient reuse in future database requests.

To check if the connection is working, let’s create a route for the products, in this case, on index.js. See how to do it below:

Step 5: Externalize Environment Variables

If you already tried to test the request above, you noticed that it won’t work on its own. Oh no! Why? Now what?

This happened because we are using the dotenv library, as you can see in the first line of code of db.config.js — “require(‘dotenv’).config”. This library loads variables from a .env file, which we haven’t yet set up, into process.env.

This is an important step because this kind of configuration needs to be stored outside of our code.

Create a .env file

To do so, create a .env file on /server folder and put the value of the variables there.

Now, make a GET request to http://localhost:9000/products and make sure your server is running. You’ll see the inserted products.

Step 6: Make API calls from UI

To deliver responses to client requests, our UI needs to get data from the API by making API calls. We have two options to do this:

  • Axios (i.e., the option chosen for this article);
  • Fetch.

Change client package.json

To let the client-side use the server-side for requests, add the code line “proxy”: “http://localhost:9000” to the client package.json file.

Modify client App.js file

Now, let’s modify the App.js file in the /client folder to allow the UI to get the data from the API and display it to the client.

This file imports axios, so we can communicate with server-side from client-side. Then, it also imports useState and useEffect:

  • useState: A React hook that allows declaring a state variable and its setter function to update the state;
  • useEffect: A React hook that allows declaring an effect that should run after every render, in this case, or when certain values change.

To make the response data more visually appealing, I decided to display it in a table, then I installed bootstrap (npm i bootstrap), imported it into the project, and used some of its classes.

Run the client-side

To run the client-side, just run npm start on the terminal.

By following all these steps above (from 1 to 6), you’ll get something like the image below, which is the result of this PERN Stack implementation: a table with the products available in the database.

Result of this PERN Stack implementation example

Step 7: Building UI

You have the basis for building an application with the PERN Stack. As for how to build your UI, this part is up to you. However, here are some tips about how to organize your project using:

  • Components: reusable code for creating UI;
  • Pages: represent different routes or sections of the application, and are responsible for rendering the content for a specific route;
  • Contexts: way to pass data through the component tree;
  • Services: API requests.

BONUS Content: How to Dockerize everything

Finally, to end this example, since I was using docker images, it makes sense to create images for both front-end and back-end.

Step 1: Create Dockerfiles

Create a Dockerfile in each directory,/server and /client. These files will be used to build both Docker images, which can be then run as Docker containers.

Dockerfile for server-side:

Dockerfile for client-side:

Step 2: Create .dockerignore files

Next, for a cleaner installation of Node packages, let’s create a .dockerignore file in both /server and /client folders. In this case, this file will ignore the node_modules directory from being copied to the containers, since we already run npm install on Dockerfile.

.dockerignore for both:

Step 3: Build the docker images

You now have the required files, but you need to build the images by running the next commands on the folders /server and /client, respectively:

docker build -t server:latest .
docker build -t client:latest .

Step 4: Create containers

And then run the images to create the containers:

docker run --name <container_name> -p 9000:9000 -d server
docker run --name <container_name> -p 3000:3000 -d client

Now that you have all the containers needed for the PERN Stack to work, you’ll notice nothing is working because the connections between the containers are broken: postgresql ← ❌ → server-side and server-side ← ❌ → client-side.

To fix this, we could inspect the container’s IP addresses with docker inspect <container-name> and change the .env file (PG_HOST) from the server-side and package.json (proxy) from the client-side with the respective values.

However, there's a better way to do it…

Step 5: Create docker bridge network

Create a docker bridge network, so that all containers share the same network.

docker network create <network_name>

Step 6: Connect the containers

Connect the three containers to the network by running the following command for each one.

docker network connect <network_name> <container_name>

Step 7: Last configurations

Change the .env file (server-side) and package.json (client-side) with the name of the containers to connect. And finally, re-build and rerun the containers.

Now, just check if everything is working!

Now that all the steps are made: CONGRATS! You now have a basic dockerized PERN Stack application working!

This guide provided you with the essentials for how to build an application with the PERN Stack, but you can explore and do much more, without any limits. I’ll leave a link here with some examples of what you can do with the PERN Stack. Feel free to explore and expand on this basic example.

Keep in mind that this is just a basic example and that things may vary depending on your needs, requirements, and desires (e.g., you could use Nginx to expose your project or use Git for version control in a larger project).

Remember, the possibilities are endless and the sky is the limit! 🚀

If you enjoy working at a large scale on projects with global impact and if you like a real challenge, feel free to reach out to us at xgeeks! We are growing our team, and you might be the next one to join this group of talented people 😉

Check out our social media channels if you want to get a sneak peek of life at xgeeks! See you soon!

--

--