Building a simple API with Nodejs, Expressjs and PostgreSQL DB - 2
"There is always one more bug to fix." Ellen Ullman
This tutorial is a continuation from where we stopped from the last post. If you have not read my previous post, please do. Click here to read my post on Building a simple API with Nodejs, Expressjs, and JS Data structure to persist data.
In this post, I'll explain how to connect and use PostgreSQL Database to store input data as opposed to JavaScript Object datatype we used in part 1. We will also learn how to write some basic [SQL](Structure Query Language) queries. SQL is a standard language for storing, manipulating and retrieving data in databases. SQL is basically the only language you can use in querying data in the database.
To make this simple and so that we can have a better understanding of how SQL works, I'm not going to make use ORM(Object Relational Mapping) in this post. Check out this answer on stackoverflow to get a better understanding of ORM. ORM is basically a technique that allows us to query and manipulate data from the database with little or zero knowledge of SQL. ORM packages expose some methods needed to query and manipulate the data in the database. We have several ORM packages that could be used with our database e.g Sequelize. Instead of using an ORM, we will use PG NodeJS package directly - PG is a NodeJs package for interfacing with the PostgreSQL database. Using PG alone will also give us the opportunity to understand some basic SQL queries as we will be querying and manipulating data in the DB using raw SQL queries.
Getting Started
Install PostgreSQL on your system. To check if progress was installed successfully, run which psql
from the terminal
Now, let's make some tweak to our project structure. Create two new folders in src
- usingJSObject
and usingDB
. Move the previous folders in src
to usingJSObject
folder.
Next, create two new folders inside usingDB
- controllers
and db
. Create Reflection.js
js file in controller
folder and index.js
in db
folder.
Next, create db.js
and .env
in the project root directory.
Your new project structure should look like the following
project
|-src
|-usingDB
|-controllers
|-Reflection.js
|-db
|-index.js
|-usingJSObject
|-controllers
|-Reflection.js
|-models
|-Reflection.js
|-.babelrc
|-.env
|-db.js
|-package.json
|-server.js
Lastly, we need to install PG and dotenv(we need this to load our environment variables from .env
) packages.
Run the following command to install the two packages
$ npm install --save pg dotenv
Set Up Database
Create Database
Let create our DB and name it reflection_db
. To create the DB, we can make use of any PostgreSQL client such as POSTICO and we can also create the DB directly from the terminal using createdb
command.
$ createdb reflection_db
use \l
to check the list of available DB from the terminal
$ psql
-# \l
you should see something similar to this
Create Reflections Table
First, let save our Database URL in the system environment. Your Database URL should be in the following format
postgres://{db_username}:{db_password}@{host}:{port}/{db_name}
e.g postgres://olawalequest@127.0.0.1:5432/reflection_db
.
If you were unable to set your DB locally, the easy way out is to make use of any PostgreSQL cloud DB such as ElephantSQL. If you use cloud PostgreSQL DB, copy the URL and use it instead.
Save the DB URL in .env
file located in the root directory.
#.env
DATABASE_URL=postgres://olawalequest@127.0.0.1:5432/reflect_db
Next, we need to create reflections
table in our DB. This is where we will start making use of pg
package with basic SQL query to create the table.
Copy and paste the following into db.js
in the project directory
Code Steps:
- Import
Pool
object frompg
. Check pg documentation to read more onpooling
. We use this to connect to our PostgreSQL Db. Pool is the easiest and common way of using pg. You can also make use of their Client API to connect to the DB. - Import
dotenv
fromdotenv
and load it usingdotenv.config()
- what this does is to search for.env
file in our project and load its content into the system environment so we can use nodeprocess.env
to access those variables. - We create a new instance of
Pool
and pass inconnectionString
to its constructor. We useprocess.env.DATABASE_URL
to getDATABASE_URL
variable from the system environment. - We listened to pool
connect
event and console logconnected to the db
- We set up a
createTables()
function, inside the function is a query that createsreflections
table with the following fields;id
- UUID datatype,success
- TEXT datatype,low_point
- TEXT datatype,take_away
- TEXT datatype,created_date
- TIMESTAMP datatype andmodified_date
- datatype.
const queryText =
`CREATE TABLE IF NOT EXISTS
reflections(
id UUID PRIMARY KEY,
success TEXT NOT NULL,
low_point TEXT NOT NULL,
take_away TEXT NOT NULL,
created_date TIMESTAMP,
modified_date TIMESTAMP
)`;
What the above does is to tell PostgreSQL DB to create reflections
table if reflections
table does not exist with fields listed.
- We call
pool
query method withqueryText
as an argument and it returns apromised
. We calledpool.end()
to closepool
connection to the db. - We created another function called
dropTables()
- What this does it to deletereflection
table. We set up a new queryDROP TABLE IF EXISTS reflections
that drop reflections table if it exists in the DB. - We use
pool.on('remove')
to listened topool
remove
event and useprocess.exit(0)
to exit the node process. - Lastly, we require
make-runnable
package - We need this to be able to call and any of our two functions from the terminal. Note: You have to requiremake-runnable
at the end. Also, don't forget to installmake-runnable
as project dev-dependency.
You'll also notice that we usedrequire
instead ofimport
, this is because we only want to rundb.js
file from the terminal alone and it is not directly part of our project so there is no point in compiling it.
Finally, lets run createTables
function to create our reflections
table. Use the following command
$ node node db createTables
After running the above, you should something similar to below
NOTE: Instead of going through the above process, you can also create tables directly either using any PostgreSQL client or using PostgreSQL commands on the terminal.
Set Up Controller
Copy the following and paste it inside src/usingDB/db/index.js
Code Steps:
- We created a new method
query
that takes in two argumentstext
- query text andparams
- values required bytext
. These two arguments are what is needed to query the DB. The method returns a promise and we will call it in our controller. Click here to read more about JavaScript Promise.
To make our controller simple, it will contain the same five methods created in the previous tutorial but we will tweak it contents to get values from the DB.
Copy and paste the following inside src/usingDB/controllers/reflections.js
Code Steps:
- Here, we create
Reflection
object with five methods -create()
,getAll()
,getOne()
,update()
anddelete()
. We also make use ofasync/await
. create()
- We imported our db fromsrc/usingDB/db/index.js
. For our SQL query, we usedINSERT INTO reflections(list_columns_here..) VALUES($1, $2, $3, $4, $5, $6 ...)
- what this does is create a new row inreflections
table and insert the supplied values into its fields.values
is an array of values that contains what we want to insert into the table. The elements insidevalues
array must be in the same order as$1, $2, $3, $4, $5, $6
. We usedreturning *
to return the created row. Remembered we createdquery
method that takes in two argumentstext
andparams
insidesrc/usingDB/db/index.js
, this is where we will use it. We called the method and send increateQuery
and values as parameters. Sincedb.query
returns a promise we make use ofasync/await
to make our code lookssweet
.getAll()
- We set upSELECT * FROM reflections
to get all rows in reflections tablegetOne()
- We useSELECT * FROM reflections WHERE id = $1
to retrieve a single row from reflections table where theid
is the specified id.update()
- To update a reflection, we queried the DB twice - the first one is to get the specific row the user wants to update and the second DB query update the row based on new input from the user.
We useUPDATE reflections SET success=$1,low_point=$2,take_away=$3,modified_date=$4 WHERE id=$5 returning *
to update a specific row in the table.delete()
- We useDELETE FROM reflections WHERE id=$1 returning *
to delete a row in reflection table using the rowid
Update Server.js
We need to figure out a way to switch between using what we did in the previous post or using DB. The best way I could think of is to add TYPE
in the environment variable. We will use this to check what to use for the app. If TYPE===db
we will make use of what we did in this post and if TYPE===jsobject
we will use what we did in the previous post. Now put TYPE=db
inside your .env
- your .env
should contain two variables now DATABASE_URL
and TYPE
.
Update your server.js
with the following
// server.js
import express from 'express';
import dotenv from 'dotenv';
import 'babel-polyfill';
import ReflectionWithJsObject from './src/usingJSObject/controllers/Reflection';
import ReflectionWithDB from './src/usingDB/controller/Reflection';
dotenv.config();
const Reflection = process.env.TYPE === 'db' ? ReflectionWithDB : ReflectionWithJsObject;
const app = express()
app.use(express.json())
app.get('/', (req, res) => {
return res.status(200).send({'message': 'YAY! Congratulations! Your first endpoint is working'});
});
#####################
# Existing Code #
#####################
app.listen(3000)
console.log('app running on port ', 3000);
Code Steps:
- We installed babel-polyfill npm package and imported it - We need this here so that node runtime will recognise
async/await
andPromise
. - Since we need to load/get
TYPE
to/from system enviroment, we make use ofdotenv
to do just that andprocess.env.TYPE
is used to retrieve its value. - We make use of JavaScript ternary operator to set the value of Reflection. What this
process.env.TYPE === 'db' ? ReflectionWithDB : ReflectionWithJsObject;
basically does is to assignReflectionWithDB
toReflection
ifTYPE === db
or assignReflectionWithJsObject
otherwise.
YAY!!!! Run and Test all your endpoints.
Conclusion
In this tutorial, we learned how to connect our node app to PostgreSQL and we also learned how to query and manipulate data in the DB.
Please check out part 1 of this post here if you haven't done so.
Check out the complete code here
As always, drop your questions and comments. Don't forget to like and share this post if you learned one or two things.
Hy Olawale Aladeusi, we really appreciate for your help on this perfect tutorial and my question is, have you for us the tutorial for guidance us on how to deploy these builden endpoints with Postgres Database to Heroku
Thanks for this yet another awesome continuation of the series, it has worked for me but however, I found out src/usingDB/controllers/reflections.js line 19 would work if it was just called as uuid() instead of uuidv4
curious about the db/index.js file and the controllers/reflections.js …
At what point is the pool.connect() being called?
I dont see it in any of the reflection methods nor in the …/db query method that is being required.