Rest API in NodeJs using es6
Introduction
Es6 is the new standard of javascript and it provides so many new things like — classes, spread operator etc.
We can use the power of es6 to create a server side app using node. This will help us to get rid of common problem of nodejs development i.e callback hell. We can use async await, classes etc to develop apps which are future proof.
In this article — we will be developing a rest api in es6 using fortjs — a server side MVC framework for nodejs..
FortJs enables you to write server side code which is modular , secure and code writtens are pretty much beautiful & readable. FortJs also provides support for dependency injection. Here is doc link — http://fortjs.info/
So let’s create a rest api using fortjs in es6 —
Installation
Clone or download the starter project of fortjs — https://github.com/ujjwalguptaofficial/fortjs-javascript-starter.
After you have downloaded the project. Open the console and move to project directory and do the following steps —
- run the command —
npm install
- run the command —
npm run start
Open the url — localhost:4000 in browser . You will see something like below -
Creating Rest
We are going to create a rest endpoint “user” which will perform add, get, delete, update.
The code for the below tutorial can be downloaded at — https://github.com/ujjwalguptaofficial/fortjs/tree/master/example/rest/javascript
For creating an end point- we first need to create a controller. Before creating controller, please read the docs for controller.
Let’s create a file user_controller.js inside the contollers folder.
Copy the below code inside the file -
import { Controller, textResult, DefaultWorker} from 'fortjs'export class UserController extends Controller { @DefaultWorker()
async default() {
return textResult('you have successfully created a user controller');
}}
In the above code -
- We have created a class
UserController
which is extending another classController
from fortjs . - We have created a method
default
which is returning some result by using the methodtextResult
from fortjs.textResult
return http response with content-type 'text/plain'. - We have used a decorator
DefaultWorker
from fortjs. A worker makes the method visible so that it can be called using http request (no worker means it is just a function which is only available for this class). A default worker is a worker which add the route "/" for target method. Please take a look at worker doc.
Now we have created a controller but its still unknown by fortjs. In order to use this controller ,we need to add this to routes .
Open route.js and add UserController to routes.
import {DefaultController } from "./controllers/default_controller";
import { UserController } from "./controllers/user_controller";export const routes = [{
path: "/*",
controller: DefaultController
},{
path: "/user",
controller: UserController
}]
Now open the url — localhost:4000/user. You can see the output which is returned from default method inside “UserController”.
Before moving further let’s write service code, which will help us to do crud operation.
Create a folder “models” and then a file “user.js” inside the folder. Paste the below code inside the file -
export class User { constructor(user) {
this.id = Number(user.id);
this.name = user.name;
this.gender = user.gender;
this.address = user.address;
this.emailId = user.emailId;
this.password = user.password;
}
}
This model “user” will be used by service and controller for transfer of data.
Create a folder “services” and then a file “ user_service.js” inside the folder. Paste the below code inside the file
import {
User
} from "../models/user";const store = {
users: [{
id: 1,
name: "ujjwal",
address: "bhubaneswar india",
emailId: "ujjwal@mg.com",
gender: "male",
password: "admin"
}]
}export class UserService {
getUsers() {
return store.users;
} addUser(user) {
const lastUser = store.users[store.users.length - 1];
user.id = lastUser == null ? 1 : lastUser.id + 1;
store.users.push(user);
return user;
} updateUser(user) {
const existingUser = store.users.find(qry => qry.id === user.id);
if (existingUser != null) {
existingUser.name = user.name;
existingUser.address = user.address;
existingUser.gender = user.gender;
existingUser.emailId = user.emailId;
return true;
}
return false;
} getUser(id) {
return store.users.find(user => user.id === id);
} removeUser(id) {
const index = store.users.findIndex(user => user.id === id);
store.users.splice(index, 1);
}
}
Above code contains a variable store which contains collection of users and the method inside the service do operation like — add, update, delete, get on that store.
So now we have service, we need to write the code to use those service and create a rest api.
REST
According to REST — An end point with http method
- GET — should return some entity or entities
- POST — should add some entity.
- PUT — should update some entity.
- DELETE — should delete some entity.
GET
Let’s rename the default methods to “getUsers” which will return all users. Replace the user_controller.js by below code -
import { Controller, DefaultWorker, jsonResult } from 'fortjs'
import { UserService } from '../service/user_service';
export class UserController extends Controller {
@DefaultWorker()
async getUsers() {
const service = new UserService();
return jsonResult(service.getUsers());
}
}
In the above code — we are using jsonResult
as we need to return json values.
Now lets refresh the browser .
We are getting one user as we had only one user in the store.
This method is only for http method — “GET” (since we are using DefaultWorker). So if you will call this same endpoint for methods other than “GET” , you will get status code 405.
POST
So we need to create a method which will add the user and only work for http method “POST”. So now “UserController” looks like this -
import { Controller, jsonResult, DefaultWorker, HTTP_METHOD, HTTP_STATUS_CODE, Worker, Route } from 'fortjs'
import { UserService } from '../service/user_service';export class UserController extends Controller {
@DefaultWorker()
async getUsers() {
const service = new UserService();
return jsonResult(service.getUsers());
} @Worker(HTTP_METHOD.Post)
@Route("/")
async addUser() {
const user = {
name: this.body.name,
gender: this.body.gender,
address: this.body.address,
emailId: this.body.emailId,
password: this.body.password
};
const service = new UserService();
const newUser = service.addUser(user);
return jsonResult(newUser, HTTP_STATUS_CODE.Created);
}
}
In the above code -
- We have added a decorator “Route” with parameter “/” which will add the route to method addUser. This means that, method “addUser” will be called when url will be — localhost:4000/user/.
- In order to make this method visible — we are using decorator “Worker”. The parameter “HTTP_METHOD.Post” makes the method only work when the request method will be POST.
- The method addUser -takes data from body (post data) and add the user to store by calling service. After the successfull addition , it returns the added user with http code — 201 (Resource Created).
But one thing to note here is that — we are not doing any validation for the user. It might be that invalid data is supplied in post request.
We can write code inside the method “addUser” to validate or write a method inside a controller (like validateUser) for validation.
But A/c to fort (fortjs works on the principle of fort) — a worker should only have code related to its main purpose and extra code should be written into components. Please take a look at component docs.
Here comes the modularization part. Let’s see how we can validate user by creating a component. Since we are doing operation on worker level, we need to use Guard component.
Create a folder “guards” and a file “ model_user_guard.js” inside the folder. Write the below code inside the file -
import {
Guard,
HTTP_STATUS_CODE,
textResult
}
from "fortjs";
import {
User
} from "../models/user";
import {
isEmail,
isLength,
isIn,
} from "validator";
import {
isNullOrUndefined
} from "util";
export class ModelUserGuard extends Guard {
validate(user) {
let errMessage;
if (user.name == null || !isLength(user.name, 5)) {
errMessage = "name should be minimum 5 characters"
} else if (user.password == null || !isLength(user.password, 5)) {
errMessage = "password should be minimum 5 characters";
} else if (user.gender == null || !isIn(user.gender, ["male", "female"])) {
errMessage = "gender should be either male or female";
} else if (user.gender == null || !isEmail(user.emailId)) {
errMessage = "email not valid";
} else if (user.address == null || !isLength(user.address, 10, 100)) {
errMessage = "address length should be between 10 & 100";
}
return errMessage;
}
async check() {
const user = new User(this.body);
const errMsg = this.validate(user);
if (errMsg == null) {
// pass user to worker method, so that they dont need to parse again
this.data.user = user;
// returning null means - this guard allows request to pass
return null;
} else {
return textResult(errMsg, HTTP_STATUS_CODE.BadRequest);
}
}
}
Now we need to add this guard to method “addUser” -
@Guards(ModelUserGuard)
@Worker(HTTP_METHOD.Post)
@Route("/")
async addUser() {
const user: User = this.data.user;
const service = new UserService();
return jsonResult(service.addUser(user), HTTP_STATUS_CODE.Created);
}
In the above code -
- I have added the guard — “ModelUserGuard” using the decorator —
Guards
. - With the guard in process, we dont need to parse the data from body anymore inside worker, we are reading it from
this.data
which we are passing from "ModelUserGuard" . - The method “addUser” will be only called when Guard allow means if all data is valid.
You can see that our worker method looks very light after using component.
PUT
Now we need to create a method which will update the user and will only work for http method — “put”.
Let’s add another method — “updateUser” with route “/” , guard — “ModelUserGuard” (for validation of user) and most important worker with http method — “PUT”
@Worker(HTTP_METHOD.Put)
@Guards(ModelUserGuard)
@Route("/")
async updateUser() { const user = this.data.user;
const userUpdated = new UserService().updateUser(user);
if (userUpdated === true) {
return textResult("user updated");
} else {
return textResult("invalid user");
}}
The above code is very simple, just calling the service code to update the user. But one important thing to notice is that we have reutilized the guard — “ModelUserGuard” and it makes our code clean.
So we are now done with —
GET — Returns all users
POST — add users
PUT — update user
Currently the GET request returns all the users but what if we want to get only one user.
Let’s see : how to do it -
We have created a method “getUsers” for returning all users. Now let’s create another method “getUser” which will return only one user.
@Worker(HTTP_METHOD.Get)
@Route("/{id}")
async getUser() { const userId = Number(this.param.id);
const service = new UserService();
const user = service.getUser(userId);
if (user == null) {
return textResult("invalid id");
}
return jsonResult(user);}
In the above code — we are using a place holder in route. Now “getUser” will be called when url will be something like — localhost:4000/user/1
The placeholder value is being consumed by usingthis.param
.
REMOVE
We will use the same format as get -
@Worker(HTTP_METHOD.Delete)
@Route("/{id}")
async removeUser() {
const userId = Number(this.param.id);
const service = new UserService();
const user = service.getUser(userId);
if (user != null) {
service.removeUser(userId);
return textResult("user deleted");
} else {
return textResult("invalid user");
}
}
In the above code — we are just calling the service to remove the user after getting the id from route.
This is the end of the article guys and thanks for reading it. Hope you have liked it.