Just like me, you must have attempted to build your Nodejs application using ES6 and beyond. In this article, I'll be taking you through the process of building and testing a basic API using ES6. Trust me, you will be amazed by the end of this post.
How it started
I had built some Nodejs API using the ES5, and everything works perfectly well, but I wasn't satisfied as I was unable to make use of some syntaxes which are somehow cool to me whenever I go through javascript code online. I found out I could actually do more with lesser code by writing my code in ES6 and beyond.
Wait a minute! What is ES6 (ECMAScript 2015) ?
ECMAScript is based on several originating technologies, the most well-known being JavaScript (Netscape) and JScript (Microsoft). It is a JavaScript standard meant to ensure the interoperability of Web pages across different Web browsers.
While the definition may seem bookish, ES6 and beyond come with lots of cool features from which I'll mention just a few. You must have come across or used some of the following syntaxes in your code:
The use of const and let, instead of var.
const is used to declare constants i.e. variables that are not meant to change through the program. It used to create immutable variables.
const tax_rate = 2.50;
let is used to declare mutable variables
var is hoisted, using it may cause a program to behave unexpectedly at runtime, if care is not taken.
The use arrow function.
// ES5
function shout() {
console("Hurray!")
}
// ES6
const shout = () => console.log("Hurray!")
Writing a basic function in one line is cool. Isn't it? It also makes the code more readable and clean when using javascript built-in functions like map, reduce, filter, and so on. For example:
//ES5
var arr = [1,2,3,4,5,6];
var arrElements = arr.map(function (item) {
return item;
})
//ES6
const arr = [1,2,3,4,5,6];
const arrrElements = arr.map(item => return item);
And lastly, you can make use of import and export keywords, as opposed to module.export
and the require
keyword.
You can read more on ES6 here, If you wish to learn more about it, in order not to digress from the purpose of this article, we have to proceed.
Setting up our project
Firstly, create a new folder from your editor of choice (mine is VSCODE!) and enter the following command in the terminal to generate a package.json file for our app:
npm init -y
This will generate a package.json file for you without any further questions.
{
"name": "hasnode",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
next, we need to install some dependencies to set up our app. Enter the following command in your terminal:
npm install --save express morgan nodemon
Then, create an app.js file at the root directory and type the following command:
const logger = require('morgan');
const express = require('express');
const app = express();
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.status(200).json({
message: "Welcome to Express"
});
})
app.listen(8000, () => console.log('Server running at 8001'));
In your package.json, add the following in your "script"
"scripts": {
"start": "node app.js"
}
and remove "main": "index.js"
To test our code, open your browser (Chrome, Mozilla, or any other browser), go to this address:
Your browser should return this:
message "Welcome to Express"
Let's play with our code a bit:
Go into your app.js and replace the following code:
const logger = require('morgan');
const express = require('express');
with this:
import logger from 'morgan';
import express from 'express';
What did you see? An Error? If that's true, it's very much expected, you can only use the import
statement when you have configured your app to use it. And that's what we are about to do now.
In order to configure our project to make use of ES6 syntax, we need to install some dependencies and do some configurations. Follow the steps below to get things working.
Before that, create an src
folder and move the app.js file into it.
Firstly, we have to install babel and some of its modules as seen below:
npm install @babel/cli @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/register @babel/runtime @babel/node --save-dev
- @babel/cli: A required install for using babel. It allows the use of Babel from the terminal and is available as ./node_modules/.bin/babel.
- @babel/core: Core Babel functionality. This is a required installation.
- @babel/node: This works exactly like the Node.js CLI, with the added benefit of compiling with babel presets and plugins. This is required for use with nodemon.
- @babel/plugin-transform-runtime: This helps to avoid duplication in the compiled output.
- @babel/preset-env: A collection of plugins that are responsible for carrying out code transformations.
- @babel/register: This compiles files on the fly and is specified as a requirement during tests.
- @babel/runtime: This works in conjunction with @babel/plugin-transform-runtime.
{
"name": "hashnode-es6-node",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "node app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"morgan": "^1.10.0"
},
"devDependencies": {
"@babel/cli": "^7.13.0",
"@babel/core": "^7.13.10",
"@babel/node": "^7.13.10",
"@babel/plugin-transform-runtime": "^7.13.9",
"@babel/preset-env": "^7.13.9",
"@babel/register": "^7.13.8",
"@babel/runtime": "^7.13.9"
}
}
Next, add the following to the "script" section of your package.json.
"babel-node": "babel-node --presets='@babel/preset-env'"
In the root directory, create a babel.config.js file as opposed to .babelrc. that only applies to a single part of your project.
It is recommended that you use babel.config.json, since babel itself uses it.. Then enter the following code in the newly-created babel.config.json file.
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/transform-runtime"]
}
Babel won't work without the code above
From the earlier explanation, we now understand the purpose of adding the code above.
Next, we will configure babel to run our app by adding the following code to the script section in our package.json file.
"dev": "nodemon --exec npm run babel-node -- src/app.js"
In your package.json file, the script section of your code should look exactly like this:
"scripts": {
"start": "node app.js",
"babel-node": "babel-node --presets='@babel/preset-env'",
"dev": "nodemon --exec npm run babel-node -- src/app.js"
}
Then, enter this in the terminal to run your code: npm run dev
You should get the following output, by navigating to HTTP:localhost:8000.
message "Welcome to Express"
Whaoo!! You are awesome, you've successfully completed the first part of this article. It took you through the process of configuring a nodejs app for ES6 using babeljs.
Configure our setup to write unit tests for the basic API we created in the last section.
We will not be writing some sophisticated tests. Instead, I'll take you through the basic concepts you can easily build on as you write more complex apps in production.
In this section, we will be making use of mocha to run our unit tests, coupled with chai for our assertions. Type the following command in the terminal to install the mentioned modules.
npm install --save-dev mocha chai sinon-chai supertest
Awesome! you have installed all the necessary dependencies for our unit test to work.
Next, create a .mocharc.yaml
file and paste the code below in there.
require:
- '@babel/register'
Some features may require polyfill, therefore install the following:
# Polyfills for builtin methods
npm install --save core-js
# Polyfills for generator function
npm install --save regenerator-runtime
Then, add import polyfills before '@babel/register'
Your .mocharc.yml
should now look like this:
require:
- 'core-js'
- 'regenerator-runtime'
- '@babel/register'
The configuration above works fine with mocha 8.
Then, update the script section of your package.json:
"scripts": {
"start": "node app.js",
"babel-node": "babel-node --presets='@babel/preset-env'",
"dev": "nodemon --exec npm run babel-node -- src/app.js",
"test": "mocha"
}
Lastly, create a test folder in the root directory, and create two new files in the newly created folder: setup.js and index.test.js.
In your setup.js, paste the code below:
import supertest from 'supertest';
import chai from 'chai';
import sinonChai from 'sinon-chai';
import app from '../src/app';
chai.use(sinonChai);
export const { expect } = chai;
export const server = supertest.agent(app);
In your index.test.js, paste the code below:
import { expect, server } from './setup';
describe('app page test', () => {
it('should return status code 200', done => {
server
.get('/')
.expect(200)
.end((err, res) => {
expect(res.status).to.equal(200);
done();
});
});
it('should return a message', done => {
server
.get('/')
.expect(200)
.end((err, res) => {
expect(res.body.message).to.equal(
'Welcome to Express'
);
done();
});
});
});
Run this command in your terminal to check if it worked.
npm run test
Your test result should look like this:
With this, we have come to the end of this tutorial. But wait! You might want to run your app in production.
Bonus
Open your package.json, and update it:
"babel-node": "babel-node --presets='@babel/preset-env'",
"prestart": "babel ./src --out-dir build",
"start": "node ./build/app",
"dev": "nodemon --exec npm run babel-node -- src/app.js",
"test": "mocha"
}
- prestart script builds the content of the src/ folder and puts it in the build/ folder. When you issue the npm start command, this script runs first before the start script.
start
script now serves the content of the build/ folder instead of the src/ folder we were serving previously. This is the script you’ll use when serving the file in production. Cloud services like Heroku automatically run this script when you deploy.npm run dev
is used to start the server during development. We have been using this script to run our app. Notice that we’re now using babel-node to run the app instead of the regular node. The --exec flag forces babel-node to serve the src/ folder. For the start script, we use node since the files in the build/ folder have been compiled to ES5.
Thank you so much for following through with this tutorial from the beginning, you are an awesome learner like me. Leave your comments below so that I can learn from it and make clarifications if there is a need for it.