Create a CRUD Application with Node.js and MySQL
Introduction
Node.js is an open-source platform for building fast and scalable network applications. MySQL is a reliable Relational Database Management System (RDBMS) and is a good choice for building CRUD (Create, Read, Update, and Delete) applications with Node.js.
A CRUD application performs the following operations:
Create
: adds data to a MySQL database using the SQLINSERT
command.Read
: queries data from a database using the SQLSELECT
command.Update
: modifies data records using the SQLUPDATE
command.Delete
: removes records from a database using the SQLDELETE
command.
Many data-driven applications run on top of the CRUD programming pattern, including blogs, company portals, e-commerce software, enterprise resource planning applications, and more. This guide implements a CRUD application with Node.js and MySQL on Ubuntu 20.04 server.
Prerequisites
Before you begin:
- Deploy an Ubuntu server.
- Install and secure a MySQL server.
- Set up a Node.js programming environment. You may skip step 2 (Install Express.js) because you don't require the Express.js dependency to test this guide.
1. Set Up a Database and a User Account
In this guide, your sample application permanently stores data in a MySQL database. Follow the steps below to initialize the database and create a user account:
Log in to the MySQL server as
root
.$ sudo mysql -u root -p
Enter your password and press Enter to proceed. Then, issue the SQL commands below to create a sample
my_shop
database and amy_shop_user
account. ReplaceEXAMPLE_PASSWORD
with a strong password to secure the MySQL account against brute-force attacks.mysql> CREATE DATABASE my_shop; CREATE USER 'my_shop_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD'; GRANT ALL PRIVILEGES ON my_shop.* TO 'my_shop_user'@'localhost'; FLUSH PRIVILEGES;
Output.
... Query OK, 0 rows affected (0.01 sec)
Switch to the new
my_shop
database.mysql> USE my_shop;
Output.
Database changed
Create a
products
table. This table stores products' information including: the uniqueproduct_id
,product_name
, andretail_price
. Issue theAUTO_INCREMENT
keyword to allow MySQL to auto-increment theproduct_id
column when you insert new records into theproducts
table.mysql> CREATE TABLE products ( product_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, product_name VARCHAR(50), retail_price DOUBLE ) ENGINE = InnoDB;
Output.
Query OK, 0 rows affected (0.01 sec)
Insert sample records into the
products
table.mysql> INSERT INTO products (product_name, retail_price) values ('LEATHER BELT', 18.45); INSERT INTO products (product_name, retail_price) values ('BLUETOOTH SPEAKER', 75.95); INSERT INTO products (product_name, retail_price) values ('RECHARGEABLE TORCH', 35.85);
Output.
... Query OK, 1 row affected (0.02 sec)
Query the
products
table to ensure the data is in place.mysql> SELECT product_id , product_name, retail_price FROM products;
Output.
+------------+--------------------+--------------+ | product_id | product_name | retail_price | +------------+--------------------+--------------+ | 1 | LEATHER BELT | 18.45 | | 2 | BLUETOOTH SPEAKER | 75.95 | | 3 | RECHARGEABLE TORCH | 35.85 | +------------+--------------------+--------------+ 3 rows in set (0.00 sec)
Log out from the MySQL server.
mysql> QUIT;
Output.
Bye
2. Create a database_gateway
Class
When working on a Node.js project, it is conventional to separate your application into manageable modules distributed across different files. This approach allows you to debug the application quickly and makes support easier. This step focuses on creating a central database class that you can re-use across the project to access your database.
Begin by creating a new
project
directory for the application.$ mkdir project
Switch to the new
project
directory.$ cd project
Open a new
database_gateway.js
file in a text editor.$ nano db_gateway.js
Add the following information to the
database_gateway.js
file. Remember to replaceEXAMPLE_PASSWORD
with the correct MySQL password for themy_shop_user
account.class db_gateway { constructor() { } getDb() { const mysql = require('mysql'); const db_con = mysql.createConnection({ host: "localhost", user: "my_shop_user", password: "EXAMPLE_PASSWORD", database: "my_shop" }); db_con.connect(function(err) { if (err) { console.log(err.message); } }); return db_con; } execute(sql, params, callBack) { var db_con = this.getDb(); db_con.query(sql, params, function (err, result) { if (err) { callBack(err, null); } else { callBack(null, "Success"); } }); } query(sql, params, callBack) { var db_con = this.getDb(); db_con.query(sql, params, function (err, result) { if (err) { callBack(err, null); } else { callBack(null, result); } }); } } module.exports = db_gateway;
Save and close the
db_gateway.js
file.
The db_gateway.js
file explained:
The single
db_gateway {}
class wraps all the database functions that you require to connect to the database and perform CRUD operations.class db_gateway { .... }
The
constructor() {}
method is empty because you're not passing or initializing any default variables for this class.The
db_gateway {}
class has three main methods (functions) as explained below:getDb()
, this method connects to the MySQL database and returns a re-usable connection (return db_con;
) implemented by theexecute()
andquery()
methods.execute()
: this method performs the SQLINSERT
,UPDATE
, andDELETE
commands and returns aSuccess
message in acallBack()
function.query()
: this method performs the SQLSELECT
command and returns an associative array containing data from theproducts
table.
Mapping the database functions in a separate class allows you to use the following classic code to call the methods later in this guide:
var db_gateway = require('./db_gateway.js'); var dg = new db_gateway(); dg.execute(sql, params, callBack); dg.query(sql, params, callBack);
As you might have noticed, Node.js uses a lot of callbacks. A callback is a function that runs when a task completes. This callback model allows Node.js to support high concurrency because it prevents functions from blocking each other.
The
module.exports = db_gateway;
line at the end allows you to avail thedb_gateway
class in other files using therequire('./db_gateway.js');
statement.
The db_gateway
class is now ready. This sample application later implements the db_gateway
class in different files to perform database operations.
3. Create a products
Class
The number of resources or endpoints in a CRUD application can range from one to a few hundred. That number depends on the complexity of the application. For instance, the following are a few resources and HTTP endpoints for a typical e-commerce application:
Resource Endpoint
----------------------
products /products
categories /categories
customers /customers
orders /orders
payments /payments
...
When designing your application, you must create separate classes for all resources to make a neat code that you can debug and fix with little effort. This guide has one resource/endpoint (products
) for fetching data from the products
table. Follow the steps below to create a class for the resource:
Open a new
products.js
file in a text editor:$ nano products.js
Enter the following information into the
products.js
file.class products { constructor(dg) { this.dg = dg; } insertRecord(jsonData, callBack) { var sql = "insert into products (product_name, retail_price) values (?, ?)"; var params = []; params.push(jsonData["product_name"]); params.push(jsonData["retail_price"]); this.dg.execute(sql, params, callBack); } getRecords(resourceId, callBack) { var sql = "select product_id, product_name, retail_price from products"; var params = []; if (resourceId != "") { sql = sql + " where product_id = ?"; params.push(resourceId); } this.dg.query(sql, params, callBack); } updateRecord(resourceId, jsonData, callBack) { var sql = "update products set product_name = ?, retail_price = ? where product_id = ?"; var params = []; params.push(jsonData["product_name"]); params.push(jsonData["retail_price"]); params.push(resourceId); this.dg.execute(sql, params, callBack); } deleteRecord(resourceId, callBack) { var sql = "delete from products where product_id = ?"; var params = []; params.push(resourceId); this.dg.execute(sql, params, callBack); } } module.exports = products;
Save and close the
products.js
file when you're through with editing.
The products.js
file explained:
The
products{}
class wraps all the resource's method in a single file.class products { ... }
The
constructor (dg) {..}
method accepts onedg
argument. Thedg
refers to your previousdatabase_gateway
class that exposes the different database functions.The
this.dg = dg;
statement initializes a new class property (dg
) and assigns the value of thedatabase_gateway
object to the property.The
products{...}
class features four other methods that correspond to the CRUD operations:insertRecord(jsonData, ...)
: accepts a JSON payload (jsonData) and constructs anINSERT
statement.getRecords(resourceId, ...)
: accepts aresourceId
parameter and constructs aSELECT
statement. When you define theresourceId
during an HTTP call, MySQL only returns the product that matches theresourceId
value.updateRecord(resourceId, jsonData, ...)
: accepts aresourceId
and a JSON payload and constructs anUPDATE
command to change the record that matches theresourceId
.deleteRecord(resourceId, ...)
: accepts aresourceId
and constructs aDELETE
command to remove the record that matches theresourceId
.
The four CRUD methods passes a
callBack
function to thedatabase_gateway
class using thethis.dg.query(sql, params, callBack);
andthis.dg.execute(sql, params, callBack);
statements. ThecallBack()
function runs when the operations complete.The
module.exports = products;
line at the end allows you to use the class in other files using therequire('./products.js');
statement.
4. Create a http_requests
Class
This step describes creating a http_requests
class that returns most HTTP variables required in this project.
Open a new
http_requests.js
in a text editor.$ nano http_requests.js
Enter the following information into the
http_requests.js
file.class http_requests { constructor(httpRequest) { var url = require("url"); this.httpRequest = httpRequest; var pathname = url.parse(this.httpRequest.url).pathname; this.resourcePath = pathname.split("/"); this.resourceId = ""; this.httpMethod = httpRequest.method if (this.resourcePath.length >= 3) { this.resourceId = this.resourcePath[2] } } } module.exports = http_requests;
Save and close the
http_requests.js
file.
The http_requests.js
file explained:
The
http_requests
class wraps the different HTTP variables in aconstructor(...)
method.class http_requests { constructor(httpRequest) { } }
The
constructor(httpRequest){}
method accepts ahttpRequest
object. This object comes from anhttp
library that you must later import and include in a different file.The
pathname.split("/");
function splits the URL using the/
character and returns theresourceId
. For instance, if you request the URLhttp://127.0.0.1:8080/products/3
, thepathname.split("/");
function returns3
as theresourceId
. The logicalif (this.resourcePath.length >= 3) {...}
statement ensures theresourceId
is available from the request URL.
5. Create a main.js
File
Your Node.js requires a main file that executes when the application starts. The main file is the entry point to your application. Follow the steps below to create the file:
Open a new
main.js
in a text editor.$ nano main.js
Enter the following information into the
main.js
file.var db_gateway = require('./db_gateway.js'); var http_requests = require('./http_requests.js'); var products = require('./products.js'); const http = require('http'); const hostname = '127.0.0.1'; const port = 8080; const server = http.createServer((req, res) => { var dg = new db_gateway(); var httpRequest = new http_requests(req); var product = new products(dg); var payload = ""; req.on('data', function (data) { payload += data; }); req.on('end', function () { function callBack(err, result) { res.statusCode = 200; res.setHeader('Content-Type', 'application/json'); var response = {} if (err) { response["error"] = err.message; } else { response["data"] = result; } res.write(JSON.stringify(response, null, 4)); res.end(); } resourceId = httpRequest.resourceId; switch (req.method) { case "POST": jsonData = JSON.parse(payload); product.insertRecord(jsonData, callBack); break; case "PUT": jsonData = JSON.parse(payload); product.updateRecord(resourceId, jsonData, callBack); break; case "DELETE": product.deleteRecord(resourceId, callBack); break; case "GET": product.getRecords(resourceId, callBack); break; } }); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
Save and close the
main.js
file.
The main.js file explained:
The first three lines import all the classes you've created for this project.
var db_gateway = require('./db_gateway.js'); var http_requests = require('./http_requests.js'); var products = require('./products.js');
The following lines import the
http
module. Thehttp
module runs a web server that listens for incoming HTTP connections on port8080
.const http = require('http'); const hostname = '127.0.0.1'; const port = 8080;
The following statement starts an HTTP server.
... const server = http.createServer((req, res) => { ... }); ...
The following lines create the new objects from the imported classes.
... var dg = new db_gateway(); var httpRequest = new http_requests(req); var product = new products(dg); ...
The following
callBack()
function runs when your application completes the CRUD operations. ThecallBack()
function displays the response in JSON format.... function callBack(err, result) { res.statusCode = 200; res.setHeader('Content-Type', 'application/json'); var response = {} if (err) { response["error"] = err.message; } else { response["data"] = result; } res.write(JSON.stringify(response, null, 4)); res.end(); } ...
The
switch() {}
statement examines the HTTP method (req.method
) and matches the methods with the correctproduct's
methods to complete HTTP operations per the following list:POST
: runs theproduct.insertRecord(jsonData, callBack);
statement.PUT
: runs theproduct.updateRecord(resourceId, jsonData, callBack);
statement.DELETE
: runs theproduct.deleteRecord(resourceId, callBack);
statement.GET
: runs theproduct.getRecords(resourceId, callBack);
statement.
6. Test the Node.Js CRUD Application
The application is now ready for testing. Your database and source code files are in place. Execute the steps below to initialize your Node.js project directory, download the necessary modules, and run tests using the Linux curl
command:
Initialize your project directory.
$ npm init
Enter the following responses:
package name: (project) : Press :key_enter: to leave as default. version: (1.0.0) : Press :key_enter: to leave as default. description: Press :key_enter: to leave as default. entry point: (main.js) Press :key_enter: to leave as default. test command: Press :key_enter: to leave as default. git repository: Press :key_enter: to leave as default. keywords: Press :key_enter: to leave as default. author: Press :key_enter: to leave as default. license: (ISC) Press :key_enter: to leave as default.
The Node.js npm package displays the following response.
About to write to /home/francis/project/package.json: { "name": "project", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Is this OK? (yes)
Key in yes and press Enter to confirm the changes.
Use the Node.js package manager (
npm
) to download and import themysql
library into your project.$ npm install mysql
Output.
npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN project@1.0.0 No description npm WARN project@1.0.0 No repository field. + mysql@2.18.1 added 11 packages from 15 contributors and audited 11 packages in 1.079s found 0 vulnerabilities
Run the
main.js
file using thenode
command. The command below establishes an HTTP server that listens for incoming connections on port8080
. Thenode main.js
command has a blocking function. Therefore don't run any other commands in your active terminal window.$ node main.js
Output.
Server running at http://127.0.0.1:8080/
Establish a new SSH connection to your server on a second terminal window and use
curl
to run the following CRUD operations:Create operation
: adds a new record to theproducts
table.$ curl -X POST http://127.0.0.1:8080/ -H 'Content-Type: application/json' -d '{"product_name":"DOUBLE-SIDED TAPE","retail_price": 4.95}'
Output.
{ "data": "Success" }
Read operation - all records
: retrieves records from theproducts
table.$ curl -X GET http://127.0.0.1:8080/products
Output.
{ "data": [ { "product_id": 1, "product_name": "LEATHER BELT", "retail_price": 18.45 }, { "product_id": 2, "product_name": "BLUETOOTH SPEAKER", "retail_price": 75.95 }, { "product_id": 3, "product_name": "RECHARGEABLE TORCH", "retail_price": 35.85 }, { "product_id": 4, "product_name": "DOUBLE-SIDED TAPE", "retail_price": 4.95 } ] }
Read operation - one record
: retrieves a record from theproducts
table that matches aresourceId
at the end of the URL (For instance,4
).$ curl -X GET http://127.0.0.1:8080/products/4
Output.
{ "data": [ { "product_id": 4, "product_name": "DOUBLE-SIDED TAPE", "retail_price": 4.95 } ] }
Update operation
: modifies the details of a product that matches theresourceId
(For instance,3
).$ curl -X PUT http://127.0.0.1:8080/products/3 -H 'Content-Type: application/json' -d '{"product_name":"RECHARGEABLE LED TORCH","retail_price": 40.20}'
Output.
{ "data": "Success" }
Request the product (
product_id
3
) to check if the update command was successful.$ curl -X GET http://127.0.0.1:8080/products/3
Output.
{ "data": [ { "product_id": 3, "product_name": "RECHARGEABLE LED TORCH", "retail_price": 40.2 } ] }
Delete Operation
: removes a product that matches theresourceId
(For instance,4
).$ curl -X DELETE http://127.0.0.1:8080/products/4
Output.
{ "data": "Success" }
Query the product again to check if the delete operation was successful.
$ curl -X GET http://127.0.0.1:8080/products/4
Output.
{ "data": [] }
The outputs above show that your Node.js CRUD application is working as expected.
Conclusion
This guide is a complete walkthrough for implementing a CRUD application with Node.js and MySQL database server on Ubuntu 20.04. Use the knowledge in this guide to code your next Node.js project. You can add as many endpoints/resources in your Node.js application depending on the complexity of your project. Remember to separate each resource in a different class file to ease future support.