Build and deploy your own full-stack JavaScript project from scratch with React and PostgreSQL

Published: 2017-06-04 by Lars  guidedelivery

(updated on 2020-10-26)

Preconditions

This tutorial assumes only that you are able to use a text editor and a terminal (command line tool) on your computer. Familiarity with reading and writing JavaScript and SQL might be useful, but the instructions are meant to be detailed enough so that you can complete the tutorial even without knowing JavaScript or SQL yet.

If you get stuck you may refer to this project on GitHub which has a fully working example built with this guide.

Introduction

We will create a very simple web application that can display a list of things ("dreams") stored in a database. You will be able to run the application on your own computer while you are working on it, and you will later be able to deploy the application to the cloud so it can be accessed from other computers.

The application will have a database, a back-end and a front-end. The database will use PostgreSQL, the back-end will use Node.js and the front-end will use React. We will use Netlify to deploy the application to the cloud.

Database

We will use the ElephantSQL service to get a cloud hosted PostgreSQL database using their free tier. Sign up on elephantsql.com

When you have created a database instance, go to the "Browser" tab for the instance. Here you can create a table and insert some data by pasting this SQL script (replace with your own dreams) and then executing it:

drop table if exists dream;

create table dream (
  id bigserial primary key,
  title varchar(255)
);

insert into dream (title) 
values 
    ('Compose a tune'), 
    ('Visit Uruguay'), 
    ('Write a sci-fi novel');

On the "Details" tab of the instance you can find the database URL which we will need in a moment.

Back-end

Install the latest version of Node.js from nodejs.org.

Create a project directory somewhere on your computer and open a terminal in this directory, then run the following commands:

cd your-project-directory-name
mkdir server
cd server
npm init --yes

You have now created a server directory for the back-end code and initialized it with a package.json file.

Then, in your terminal, install the tools that we will depend on for the back-end:

npm install --save pg-promise

We will eventually deploy our application using Netlify, which provides a "function-as-a-service" (also called "serverless") platform. Thus, our back-end will consist of a single function per end-point. To use Netlify we need to install their command-line tool (CLI) globally on the machine and create an account. In the terminal:

npm install -g netlify-cli
netlify login

Follow the instructions to create an account on their free tier.

Verify that netlify has been configured correctly. In the terminal:

netlify status

It should show you something like

Email: your-email
Teams:
  your-user-name's team: Collaborator

Note down "your-user-name" as we will need it later.

Then create a new folder src inside the server directory. We need a single end-point, /dreams to return the list of dreams from the database. So we create a new file using the name in the end-point, dreams.js and add this code:

const db = require('./utils/db');

exports.handler = async (event) => {
  switch (event.httpMethod) {
    case 'GET':
      const rowList = await db.query('select * from dream order by title');
      return {
        statusCode: 200,
        headers: {'content-type': 'application/json'},
        body: JSON.stringify(rowList)
      };
    default:
      return { statusCode: 405, body: '' }
  }
}

We also need a file to create a shared database object, so inside the server directory we create a utils directory and in there a db.js file:

const env = require('../env.json')
const pgp = require('pg-promise')();
module.exports = pgp(env.DATABASE);

We tell the back-end to connect to our database using the URL we got on the ElephantSQL details page by creating a file next to dreams.js called env.json with this content:

{
  "DATABASE": "your PostgreSQL connection URL"
}

Since this file will contain the password, you should never commit this file, so let's make sure that Git will ignore it by listing that file name in a new file called .gitignore:

env.json

We also need to tell Netlify where to find the functions. Add a file netlify.toml at the root of your project:

[build]
  base = "./"
  functions = "./server/src"

We will specify in package.json how to start the server, so add this line to server/package.json right after the line "scripts": {:

"start": "netlify dev",

You can now test the back-end by first starting the server from the terminal:

npm start

And then open this URL in a browser: http://localhost:8888/.netlify/functions/dreams.

And it will return the data from our database as a JSON object:

[
  {
   "id":"1", 
   "title":"Compose a tune"
  }, 
  {
   "id":"2", 
   "title":"Visit Uruguay"
  }, 
  {
    "id":"3", 
    "title":"Write a sci-fi novel"
  }
]

When you change the back-end code, you will need to manually stop the server (Ctrl+C) and restart it (npm start).

Front-end

We will create a front-end application in React. Front-end applications need to be transpiled and bundled to work well in browsers, and we will use create-react-app to get a fully working setup of Webpack, Babel and other tools that handle this automatically.

You will want to keep the back-end running in its own terminal, so open up a new terminal for working with the front-end.

Use create-react-app to create a skeleton app, from the terminal:

cd your-project-directory-name
npx create-react-app app

Getting all the tools installed takes a couple of minutes.

Then run the app in development mode from the terminal:

cd app
npm start

This will open the app in a browser, and when you make changes to the source code of the app, Webpack ensures that it will automatically reload in the browser so you can see the effect of your changes.

We want our app to fetch and display data from our back-end. Let's create a React component in the file src/DreamList.js:

import React, { useEffect, useState } from "react";

const DreamList = () => {
  const [dreamList, setDreamList] = useState([]);
  useEffect(() => {
    const fetchDreams = async () => {
      const response = await fetch("/.netlify/functions/dreams");
      const dreamList = await response.json();
      setDreamList(dreamList);
    };
    fetchDreams();
  }, []);
  return (
    <div>
      <h3>All my dreams</h3>
      <ul>
        {dreamList.map((dream) => (
          <li key={dream.id}>{dream.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default DreamList;

We need to render this component inside the main App component, so we will add this line in App.js after the <p>...</p> element:

<DreamList />

You will also need to add an import statement next to the other import statements at the top of the file:

import DreamList from './DreamList';

During development the back-end runs on a different port (8888, served by Node) than the front-end (3000, served by WebpackDevServer). We need to tell WebpackDevServer to proxy back-end requests to our back-end, so we must add this line to package.json (right after the initial { character):

"proxy": "http://localhost:8888",

Now, if you stop (pressing Ctrl+C) and then restart the front-end (npm start), you will see the list of dreams being displayed as a bullet list in the browser.

Congratulations: You now have a full-stack JavaScript application running on your computer!

Deploy

To ensure that other people can use our application we need to bundle the front-end code, ensure that Netlify will serve the front-end code and upload the combined front-end and back-end code to the cloud.

To bundle the front-end code, run the following command in your terminal:

cd your-project-directory-name/app
npm run build

We need to tell Netlify where to find the bundled code. Modify the file netlify.toml at the root of your project to contain:

[build]
  base = "./"
  publish = "./app/build"
  functions = "./server/src"

You will then need to tell Netlify to create a new public website to host your app. You will need "your-user-name" from when we signed up to Netlify. Enter these commands in the terminal, and be careful to substitute "your-user-name" with your actual user name:

cd your-project-directory-name
netlify sites:create --name dreams-your-user-name --account-slug your-user-name
netlify link --name dreams-your-user-name

You can now deploy your application to the cloud from the terminal:

netlify deploy --prod

Your code will be deployed to a cloud server and given a unique URL. You can open the URL with this command in the terminal:

netlify open:site

Congratulations: Your application is now running in the cloud and is available from everywhere!

Learn more

To extend your application you may need to learn more about how to use the tools we used. Here is a list of links to documentation and tutorials.

Discuss on Twitter