Simple and Easy way to Build a RESTful API using Node.js
REpresentational State Transfer in short REST is a web standard-based architecture that mainly uses HTTP Protocol. In the RESTful architecture, the Server simply provides access to the resources, and the client accesses them using HTTP protocol. In this article, we will build RESTful API using Node.js
Build a RESTful API using Node.js:
Node.js has made JavaScript programming so popular and implementing full-featured applications using it has become simple and elegant. Let’s go step by step :
Software used
Read here How to get started with Node.js
Adding Packages
As listed above we are using express to create our webserver, mysql as our database. Also as we are going to process some post request we will add support using body-parser and cors.
1 2 3 4 |
npm install --save express npm install --save mysql npm install --save body-parser npm install --save cors |
For our example, we are trying to use ES6 so we will also add Babel to our dev dependencies.
Creating Server
As we are using Express, we will use an exported express app to the create server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import express from "express"; import bodyparser from "body-parser"; import cors from "cors"; import products from "./api/products"; import orders from "./api/orders"; const app = express(); app.use(cors()); app.use(bodyparser.json()); app.use(bodyparser.urlencoded({extended:false})); app.use("/products",products); app.use("/orders",orders); //if we are here then the specified request is not found app.use((req,res,next)=> { const err = new Error("Not Found"); err.status = 404; next(err); }); //all other requests are not implemented. app.use((err,req, res, next) => { res.status(err.status || 501); res.json({ error: { code: err.status || 501, message: err.message } }); }); module.exports = app; |
You can see that we have some imports from api folder and we are using them in our express app. We will get there shortly.
Below is how our server is created. We will use any port that has been configured in system variables or if that is not available then we will use 6001 by default.
1 2 3 4 5 6 7 8 9 |
import http from "http"; import app from "./app"; //Use system configuration for port or use 6001 by default. const port = process.env.port || 6001; //Create server with exported express app const server = http.createServer(app); server.listen(port); |
Create Database Component
With Mysqljs npm package, connecting to the database is very simple. Below is our database component and a utility method to run a query and send results back.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import mysql from "mysql"; const pool = mysql.createPool({ connectionLimit : 10, host : 'localhost', user : 'lessroot', password : 'lessroot', database : 'localdb', debug : false }); function executeQuery(sql, callback) { pool.getConnection((err,connection) => { if(err) { return callback(err, null); } else { if(connection) { connection.query(sql, function (error, results, fields) { connection.release(); if (error) { return callback(error, null); } return callback(null, results); }); } } }); } function query(sql, callback) { executeQuery(sql,function(err, data) { if(err) { return callback(err); } callback(null, data); }); } module.exports = { query: query } |
Please note that we are simply exporting the utility method and not the entire component.
Adding APIs
For our demonstration purpose, we will create table products and perform operations on it like adding a product, deleting a product, listing product etc through our RESTful API using Node.js application.
For simplicity, we will also add a domain object and some utility methods as shown below-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Product { constructor(name,price) { this.prd_id=0; this.prd_name=name; this.prd_price=price; } getAddProductSQL() { let sql = `INSERT INTO PRODUCTS(prd_name, prd_price) \ VALUES('${this.prd_name}',${this.prd_price})`; return sql; } static getProductByIdSQL(prd_id) { let sql = `SELECT * FROM PRODUCTS WHERE PRD_ID = ${prd_id}`; return sql; } static deleteProductByIdSQL(prd_id) { let sql = `DELETE FROM PRODUCTS WHERE PRD_ID = ${prd_id}`; return sql; } static getAllProductSQL() { let sql = `SELECT * FROM PRODUCTS`; return sql; } } export default Product; |
Product API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
import express from "express"; import db from "../db/database"; import Product from "../domain/product"; const router = express.Router(); //handles url http://localhost:6001/products router.get("/", (req, res, next) => { db.query(Product.getAllProductSQL(), (err, data)=> { if(!err) { res.status(200).json({ message:"Products listed.", productId:data }); } }); }); //handles url http://localhost:6001/products/add router.post("/add", (req, res, next) => { //read product information from request let product = new Product(req.body.prd_name, req.body.prd_price); db.query(product.getAddProductSQL(), (err, data)=> { res.status(200).json({ message:"Product added.", productId: data.insertId }); }); }); //handles url http://localhost:6001/products/1001 router.get("/:productId", (req, res, next) => { let pid = req.params.productId; db.query(Product.getProductByIdSQL(pid), (err, data)=> { if(!err) { if(data && data.length > 0) { res.status(200).json({ message:"Product found.", product: data }); } else { res.status(200).json({ message:"Product Not found." }); } } }); }); //handles url http://localhost:6001/products/delete router.post("/delete", (req, res, next) => { var pid = req.body.productId; db.query(Product.deleteProductByIdSQL(pid), (err, data)=> { if(!err) { if(data && data.affectedRows > 0) { res.status(200).json({ message:`Product deleted with id = ${pid}.`, affectedRows: data.affectedRows }); } else { res.status(200).json({ message:"Product Not found." }); } } }); }); module.exports = router; |
As shown above, the API defines an express Router. The router is then configured to handle get and post methods for product operations.
If you note, in our app.js
we have configured urls to be handled as
1 2 |
app.use("/products",products); app.use("/orders",orders); |
This means, all the urls that are like http://localhost:6001/products
will be handled by the product API we have imported. The rest of the url is processed and matched in methods defined in the API.
Testing the API
For testing of our API we will use Postman. Check below screens on how our RESTful APIs work.
Adding a Product
Listing Products
Deleting a Product
Find a Product
Conclusion
In this article, we have seen how we build a RESTful API using Node.js.
The complete code can be downloaded from our git repository. Please feel free to try out and let me know your comments or issues.
Download Code
sql injections?
This function not work. Have you tested?
getAddProductSQL() {
let sql =
INSERT INTO PRODUCTS(prd_name, prd_price) \
;VALUES('${this.prd_name}',${this.prd_price})
return sql;
}
import * as http from “http”
^
SyntaxError: Unexpected token *
at Module._compile (internal/modules/cjs/loader.js:723:23)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
I change to const http = require(“http”); and now is working.
Hi Abreu,
Glad you found it useful and able to use it.
shouldn’t you update the article to include the working code that abreu suggested?
Hi Justin,
The code aberu is using like “import * from http”
I did not see in the article or code from article any way like that. More over the code in the article is tested and working. Please let me know if you facing any issues with the code and I will be happy to re-check and update the code/article.
Hi Pavan,
In this particular example, what is the syntax to parameterize?
To avoid sql injections, we can parameterize as in example:
static getProductByIdSQL(prd_id) {
// let sql =
SELECT * FROM PRODUCTS WHERE PRD_ID = ${prd_id}
;return { sql:
SELECT * FROM PRODUCTS WHERE PRD_ID = ??
, values: prd_id };}
Check:
https://github.com/mysqljs/mysql#escaping-query-values
https://github.com/mysqljs/mysql#performing-queries
Hope it helps.
Thanks Pavan for this RESTful example.
Hello, for some reason, every I make a call with Postman, no matter which endpoint I call, I get a 404 error.
Hi Michael,
Are you using code as is or adapted as per your requirements?
Thanks, Pavan.
very well explained !!
Best practice would be to use a HTTP DELETE request in order to delete a product (instead of POST). https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE
D:\xampp\htdocs\nodeapi\server.js:1
(function (exports, require, module, __filename, __dirname) { import http
http”;
^^^^
SyntaxError: Unexpected identifier
at new Script (vm.js:79:7)
at createScript (vm.js:251:10)
at Object.runInThisContext (vm.js:303:10)
at Module._compile (internal/modules/cjs/loader.js:656:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:1
at Module.load (internal/modules/cjs/loader.js:598:32)
at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
at Function.Module._load (internal/modules/cjs/loader.js:529:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
at startup (internal/bootstrap/node.js:285:19)
Be careful of SQL injection, use parameterization of the arguments.
Hi Hans,
Yes that is the most critical part of any application and we should always take care of that aspect.
how to create code for update data ?
not working! it is stuck at [nodemon] starting
babel-node server.js --presets env
A very helpful code. More grease to your elbow
Thanks