Editor’s note: This tutorial was last updated 1 February 2022 to replace tools that had become outdated.
Generally, Node.js is coupled with MongoDB and other NoSQL databases, but Node.js performs well with relational databases like MySQL, too. If you want to write a new microservice with Node.js for an existing database, it’s highly likely that you’ll use MySQL, one of the world’s most popular open-source databases.
In this tutorial, we’ll learn how to build a REST API using MySQL as our database and Node.js as our language. We’ll also use the Express.js framework to make our task easier. Our example REST API will track the most popular programming languages.
Prerequisites
To follow along with this article, you should have the following:
Understanding of how MySQL and relational databases work in general
Knowledge of what CRUD (create, read, update, delete) is and how it relates to the HTTP methods GET, POST, PUT, and DELETE
The code in this tutorial is performed on a Mac with Node 14 LTS installed. If you want, you can try to use Node.js, Docker, and Docker Compose to improve developer experience. You can also access the full code at the GitHub repository. Let’s get started!
MySQL is one of the most popular databases in the world, if not the most popular. Per the 2020 Stack Overflow survey, MySQL was the most-loved database, with more than 55 percent of respondents using it. The community edition is freely available, supported by a large and active community.
MySQL is a feature-packed relational database first released in 1995. MySQL runs on all major operating systems like, Linux, Windows, and macOS. Because of its features and its cost-effectiveness, MySQL is used by big enterprises and new startups alike.
For our example REST API, we’ll use a free MySQL service instead of setting up a local MySQL server. To host our testing MySQL database, we’ll use db4free.net.
Register on db4free.net
To get your free MySQL 8.0 database up and running, you can register on db4free.net. First, go to the db4free signup page, then fill out the required details by choosing your database name and username:
Click on Signup and you should receive a confirmation email. Confirm your account by clicking on the link in the email. Next, on the sidebar, click on phpMyAdmin. In the phpMyAdmin login, enter the username and password you chose and click Go:
Create the programming languages table
Now, we have an empty database. Let’s add the programming_languages table. First, click on the database name on the left; for me, it was restapitest123. Then, click SQL on the top menu, which is the second link after Structure, and put the following code for CREATE TABLE in the text area:
CREATE TABLE `programming_languages`
(
`id` INT(11) NOT NULL auto_increment ,
`name` VARCHAR(255) NOT NULL ,
`released_year` INT NOT NULL ,
`githut_rank` INT NULL ,
`pypl_rank` INT NULL ,
`tiobe_rank` INT NULL ,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ,
`updated_at` DATETIME on UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (`id`),
UNIQUE `idx_name_unique` (`name`(255))
)
engine = innodb charset=utf8mb4 COLLATE utf8mb4_general_ci;
Click the Go button, as below:
The code will come back with a green check box and a message along the lines of MySQL returned an empty result set (i.e. zero rows).
With that, we’ve created a table called programming_languages with eight columns and a primary key called id, which is an internet and auto-increment. The name column is unique, and we also added the released_year for the programming language. We have three columns to input the rank of the programming language, sourced from the following resources:
The created_at and updated_at columns store dates to keep a track of when the rows were created and updated.
Add demo rows for programming languages
Next, we’ll add 16 popular programming languages to our programming_languages table. Click the same SQL link on the top of the page and copy and paste the code below:
You should receive a message that reads something like “16 rows inserted”.
The data collected from our three sources is collected and added to the table in bulk by the INSERT statement, creating 16 rows, one for each programming language. We’ll return to this later when we fetch data for the GET API endpoint.
If we click on the programming_languages table, visible on the left, we’ll see the rows that we just added:
Next, we’ll set up Express.js for our REST API with Node.js and MySQL.
Setting up Express.js for our REST API
To set up a Node.js app with an Express.js server, we’ll first create a directory for our project to reside in:
mkdir programming-languages-api && cd programming-languages-api
Then, we can create a package.json file with npm init -y as follows:
To install Express, we’ll run npm iexpress, adding Express as a dependency in the package.json file.Next, we’ll create a slim server in the index.js file. It will print an ok message on the main path /:
There are a few important things to note in the code above. For one, we’ll using the built-in Express JSON parser middleware to parse JSON in the next steps. We’ll also utilize the express.urlencoded() middleware to parse the URL encoded body.
If the PORT is not provided as an environment variable, our app will run on port 3000. We can run the server with node index.js and hit http://localhost:3000 to see {message: "ok"} as the output.
REST API project structure
We’ll structure our project in the following manner to arrange our files logically in folders:
config.js will contain configuration for information like the database credentials and the rows we want to show per page when we paginate results. helper.js is the home for any helper functions, like calculating offset for pagination.
The routes/programmingLanguages.js file will act as the glue between the URI and the corresponding function in the services/programmingLanguages.js service. The services folder will house all our services. One of them is db.js, which we use to talk with the MySQL database.
Another service is programmingLanguages.js, which will have methods like getMultiple, create, etc. to get and create the programming language resource. Basic mapping of the URI and the related service function will look like the code below:
GET /programming-languages → getMultiple()
POST /programming-languages → create()
PUT /programming-languages/:id → update()
DELETE /programming-languages/:id → remove()
Now, let’s code our GET programming languages API with pagination.
GET popular programming languages
To create our GET programming languages API, we’ll need to link our Node.js server with MySQL. To do so, we’ll use the mysql2 package from npm, which we can install with the npm i mysql2 command on the project root.
Next, we’ll create the config file on the root of the project with the following contents:
const config = {
db: {
/* don't expose password or any sensitive info, done only for demo */
host: "db4free.net",
user: "restapitest123",
password: "restapitest123",
database: "restapitest123",
},
listPerPage: 10,
};
module.exports = config;
Consequently, we’ll create the helper.js file with the code below:
For the fun part, we’ll add the route and link it to the services. First, we’ll connect to the database and enable running queries on the database in the services/db.js file:
We’ve also added an error handler middleware to handle any errors and provide a proper status code and message.
After adding the GET endpoint, when we run our app again with node index.js and hit the browser with http://localhost:3000/programming-languages, we’ll see an output like the following:
Depending on the extensions you have installed on your browser, your output might look a little different.
Note that we’ve already implemented pagination for our GET API, which is possible because of the getOffset function in helper.js and the way we run the SELECT query in services/programmingLanguage.js. Try http://localhost:3000/programming-languages?page=2 to see languages 11–16.
POST a new programming language
Our POST API will allow us to create a new programming language in our table.
To create a POST programming language API in the /programming-languages endpoint, we’ll add code to the service and the routes files. In the service method, we’ll get the name, the release year, and other ranks from the request body, then insert them into the programming_languages table.
Append the following code to the services/programmingLanguages.js file:
async function create(programmingLanguage){
const result = await db.query(
`INSERT INTO programming_languages
(name, released_year, githut_rank, pypl_rank, tiobe_rank)
VALUES
(${programmingLanguage.name}, ${programmingLanguage.released_year}, ${programmingLanguage.githut_rank}, ${programmingLanguage.pypl_rank}, ${programmingLanguage.tiobe_rank})`
);
let message = 'Error in creating programming language';
if (result.affectedRows) {
message = 'Programming language created successfully';
}
return {message};
}
Make sure you export the following function as well:
module.exports = {
getMultiple,
create
}
For the function above to be accessible, we need to add a route to link it up in the routes/programmingLanguages.js file as follows:
/* POST programming language */
router.post('/', async function(req, res, next) {
try {
res.json(await programmingLanguages.create(req.body));
} catch (err) {
console.error(`Error while creating programming language`, err.message);
next(err);
}
});
PUT to update an existing programming language
To update an existing programming language, we’ll use the /programming-languages/:id endpoint, where we’ll get the data to update the language. To update a programming language, we’ll run the UPDATE query based on the data we got in the request.
PUT is an idempotent action, meaning if the same call is made over and over again, it will produce the exact same results. To enable updating existing records, we’ll add the following code to the programming language service:
async function update(id, programmingLanguage){
const result = await db.query(
`UPDATE programming_languages
SET name="${programmingLanguage.name}", released_year=${programmingLanguage.released_year}, githut_rank=${programmingLanguage.githut_rank},
pypl_rank=${programmingLanguage.pypl_rank}, tiobe_rank=${programmingLanguage.tiobe_rank}
WHERE id=${id}`
);
let message = 'Error in updating programming language';
if (result.affectedRows) {
message = 'Programming language updated successfully';
}
return {message};
}
Make sure you export this function as well as we did before:
To wire up the code with the PUT endpoint, we’ll add the code below to the programming languages route file, just above module.exports = router;:
/* PUT programming language */
router.put('/:id', async function(req, res, next) {
try {
res.json(await programmingLanguages.update(req.params.id, req.body));
} catch (err) {
console.error(`Error while updating programming language`, err.message);
next(err);
}
});
Now, we have the ability to update any existing programming language. For instance, we can update a language’s name if we see a typo.
DELETE a programming language
We’ll use the /programming-languages/:id path with the HTTP DELETE method to add the functionality to delete a programming language. Go ahead and run the code below:
async function remove(id){
const result = await db.query(
`DELETE FROM programming_languages WHERE id=${id}`
);
let message = 'Error in deleting programming language';
if (result.affectedRows) {
message = 'Programming language deleted successfully';
}
return {message};
}
Don’t forget to export this function as well. Once again, to link up the service with the route, we’ll add the following code to the routes/programmingLanguages.js file:
After you have the Node.js Express server running with node index.js, you can test all the API endpoints. To create a new programming language, let’s go with Dart, run the following cURLcommand. Alternately, you can use Postman or any other HTTP client:
The code above will result in the following output:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 55
ETag: W/"37-3mETlnRrtfrms6wlAjdgAXKq9GE"
Date: Mon, 01 Feb 2021 11:20:07 GMT
Connection: keep-alive
{"message":"Programming language created successfully"}
You can remove the X-Powered-By header and add other security response headers using Express.js Helmet, which will be a great addition to improve the API’s security. For now, let’s update the GitHut rank of Dart from 13 to 12:
The code above will result in the following output:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 55
ETag: W/"37-aMzd+8NpWQ09igvHbNLorsXxGFo"
Date: Mon, 01 Feb 2021 11:50:17 GMT
Connection: keep-alive
{"message":"Programming language deleted successfully"}
If you’re more used to a visual interface for testing, for instance, Postman, you can import the cURL commands into Postman.
Further considerations
For the sake of simplicity in this tutorial, we kept our example fairly simple. However, if this was a real-life API, and not a demo, I’d highly recommend the following:
Use a robust validation library like Joi to precisely validate the input, for example, to ensure the name of the programming language is required and doesn’t already exist in the database.
Improve security by adding Helmet.js to Express.js
We now have a functioning API server that uses Node.js and MySQL. In this tutorial, we learned how to set up MySQL on a free service. We then created an Express.js server that can handle various HTTP methods in connection to how it translates to SQL queries.
The example REST API in this tutorial serves as a good starting point and foundation for building real-world, production-ready REST APIs, wherein you can practice the additional considerations describes above. I hope you enjoyed this article, happy coding!
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
200’s only Monitor failed and slow network requests in production
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/