Deploy your full stack React & Node.js app
Tags: database, react, node, postgres
Published: 2023-07-03 18:04:41

Are you ready to take your full-stack application from development to production but unsure of the necessary steps and potential pitfalls along the way? Deploying a full-stack application requires careful planning and consideration to ensure a smooth transition and a successful launch. Last month, I deployed my application and it took me a week until I finally saw it running perfectly in production. 

In this article, I will share with you the small and big mistakes I made and points to bear in mind in order to successfully deploy your full-stack application. From understanding the build command and customising it for your project to selecting a hosting service and setting up a production database, we will cover key aspects that can make or break your deployment.

## Understand build 

Let's understand what the `build` command means before deploying your application. You can skip this part if you are already comfortable with the concept and what it actually means to our deployment.

Basically, we need to run `build` command to make our files deployable. This command creates a build folder in the root of our application and stores optimised files, including static resources, css, html, JavaScript files, used in your application inside this folder. The content of the folder gets updated if changes are added to these files.

## Customise build command for your project

The `build` command, like other scripts, can be customised in `package.json`. What should be included here totally depends on your project. For example, you might need to include logic to convert TypeScript code to JavaScript code if you are using TypeScript.

While it may seem arbitrary, it is very important to include all the steps required for your particular project in this command; otherwise, you might encounter some errors in deployment. So let's take a little moment, search for what you should include, and customise your `build` command.

The frontend build script in my recent project looks like this:

```
"build": "tsc && react-scripts build"
```

And this is its backend script:

```
"build": "tsc && prisma generate && prisma migrate deploy"
```
Let me explain these scripts.

### Compile TypeScript into JavaScript

If you are using TypeScript in your project, please do not forget to include `tsc` in your build command. This command converts all TypeScript codes into JavaScript codes, making your files executable in production.

### Set up access to the database in production

In my project, I am using a combination of Prisma and PostgreSQL to manage its database. The second command, `prisma generate`, generates Prisma Client, which allows me to interact with my database to implement CRUD operations (e.g., creating a new item in a table). The third command, `prisma migrate deploy`, on the other hand, applies new structural modifications (e.g., adding a new table) to my database.

As you can see, what should be included in your build scripts really depends on which technologies you are using and how you manage your codebase and database. 

## Deploy with a hosting service

I used [Render](https://render.com/) to deploy my application. Render is one of the most popular web hosting services today. It is really easy to set up,  and we can use the basic service for free. 

To give you an overview, I set up my hosted client as "static site", my hosted server as "web service", and created a PostgreSQL database for production on Render. 

My dashboard looks like this:

![render dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezgzu5jrjg3exvk7nyit.png)

### Set up the client

First, I set up my client hosting following [their documentation](https://render.com/docs/deploy-create-react-app) for a Create React App. 

![render's instruction about static site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ju2dvmpx815wqwcouxdr.png)

In the process, they ask me to add values to "Build Command" and "Publish Directory." Now that we know what the build command is, the instruction should be straightforward. 

![static site commands](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zj354zzn5s8vvn2gytu7.png)

I did not fully understand the build command when I initially set it up, and I excluded some critical implementations from the script. I hope you will not make the same mistake as me!

Then, in the "Custom Domains" section, I set up a custom domain so I can use my unique domain purchased from [Namecheap](https://www.namecheap.com/) instead of the default Render domain (e.g., `yourstaticsitename.onrender.com`). You can find out how to set it up via this [link](https://render.com/docs/custom-domains#configuring-dns-to-point-to-render). 

Once I saved the settings, I filled out all the environment variables used in my client.

![Environment variables](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ckcxhg3yqjyj2dzrvmdx.png)

### Set up backend

Next, I set up web service following [their documentation for deploying a Node Express App](https://render.com/docs/deploy-node-express-app). 

You might get confused when setting up the "Buid& Deploy" section. 

![render's instruction about web service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p982nlcqbhr1or9pew4b.png)

First confusion might come from "Build Command." The example in the instruction uses just `yarn` as its "Build Command." 

Yes, this `yarn` command installs all the libraries required for the server and we definitely need to run this to deploy our server. However, as I explained at the beginning of this article, build command should include more logic than this if we are using TypeScript and/or need migration for our database. 

Thus, I added this value to "Build Command":

![Build Command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ki1nuxy9xq87bp4r9i0.png)

Secondly, instead of "Publish directory" that we saw when setting up a static site, we are asked to add "Start Command" value. 

This is because we need to START our server to handle requests from our client. And this command runs only after build command runs. 

So in my `package.json`, I created my `start` script like this:

```
 "start": "node ./build/src/index.js"
```

`node` executes JavaScript code stored in the build folder. It is important to double-check the location of built main JS file in your project and write the right path to the file. The file could be named `app.js` or `index.js` depending on your project. 

Then I again set up a custom domain for the server and set environment variables as I did for the static site.

## Ensure the client and server are related subdomains

If you do not use cookies, please skip this section. 
But if you do, this is really important.

I am using cookies to authenticate users in my application. In short, when a user logs in, my server issues an access token and passes it to the client via cookies. Then, the client's token is checked on the server every time the client makes a request.

This exchange via cookies is only possible when they are sharing the same root domain. For example:

- `www.rootdomain.com` and `api.rootdomain.com` can exchange cookies.
- But `www.rootdomain.com` and `api.differentdomain.com` cannot exchange cookies.

If you run your application in production and see no cookies in Developer tools, it might be because of this domain restriction. 

For those who want to see more examples like this, please check out this [video](https://www.youtube.com/watch?v=xgW0XjOeusY).

## Create a production database

After all of these setups, I was still failing to connect my server with the client. 

And I finally learned that I need to make my database accessible by my server in production!

PostgreSQL database has a "connection URL", a URL that the application server uses to connect to the database. The connection URL looks like this:

```
user@localhost:5433/mydb?options=-c%20synchronous_commit%3Doff
```

As explained in [this documentation](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING), `localhost:5433` points to a port. And this was the port set up by PostgreSQL by default and I continued using this default port in production.

In fact, this was the very cause of my problem.
This "local" database is only accessible from the application running on the same machine, meaning it is impossible for the deployed server to make a connection with it.

So what did I do?

There were two options to overcome this:

1. Build a production database and use it both from the development and production environment.
2. Build a production database and use it only in production.

In web development, the second option is regarded as better since having a separate database can protect valuable information from accidental modifications and 
unauthorised access.

Fortunately, Render has a great service with which we can create a [hosted production PostgreSQL database](https://render.com/docs/databases). 

Its set-up is not as complicated as it seems but there are several things to bear in mind:

1. The web service and database should be in the same region.
2. Dynamically pass the database URL to your server so that the server can connect the local database in development and the hosted database in production.

It might be a good idea to look at the database hosting services when choosing a provider to deploy your application.

## Conclusion

That's it!

As we have seen, deploying a full-stack application involves several crucial steps that require good knowledge of the technologies used in the application and our decision-making. By customising the build command, setting up hosting services, and ensuring proper database configuration, you can successfully deploy your application in a production environment.  

Thank you so much for reading this article if you made it this far! Whether you are about to start deploying your application or experiencing problems in deployment, I hope it could offer you some ideas.

Happy deploying!