Monday, November 25, 2019

Create REST API with NodeJS

An introductory part of the Learn Node.js, Express and MongoDB + JWT course.


First, we will install the latest npm and nodejs version from nodesource: https://github.com/nodesource/distributions

curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
sudo apt-get install -y nodejs

Then we will create a directory API, where we will set up our project:
mkdir api
npm init
(please provide project name, description, and author name as information)
Then is time to install some required packages:
We will install express in order to act as a web server together with nodejs: npm i express
Open the package.json file and notice how now express appears under the dependencies section.

We will also install MongoDB as well as MongoDB as our database and a mongoose DB connection helper with npm i mongodb mongoose
Then we will do a babel installation. Babel (/preset-env) will transpile (convert) our code so it can become cross-browser compatible, this is especially useful when you want to achieve better JavaScript support. Babel (/node) will provide understanding to nodejs of the modern ES features we will be using inside the index.js file.
npm i --save-dev @babel/preset-env @babel/core @babel/node
Now if you open package.json you will see that babel is installed only as a development dependency. Before start using babel we will configure it, just create a file .babelrc with the following content:
{
"presets": ["@babel/preset-env"]
}
Here we are just preparing the Babel transpiling to be suited for specific browsers.

We will also install nodemon to be able automatically to monitor and refresh the nodejs server (loading the new code) when we make changes to our code.
npm i nodemon --save-dev
/* Later we will need  body-parser to be able to parse when receiving as well as send application/json type of requests: npm i body-parser
*/

Now let's change the starting script of package.json to be able to run the upcoming index.js file. We will change it from 'test' to:
start: 'nodemon --exec babel-node index.js" //note the double --
With this change, we now can just type: npm start and it will start recompiled / transpiled version of index.js (using babel) so we can then browse the generated version of index.js via our browser.

In order to be able to browse the output of the index.js file, we will build a server that will serve it. The server will be bound to the localhost IP address and will listen to a specific port.
Create index.js with the following content:
import express from 'express'; // we import the express library
// since we are using babel the import function will be recognized in browsers which are not supporting ES6 imports
const app = express(); // we initiate a class object from the express library
// note the usage of const -> we will not change the app variable later so const is well appropriate to be used here
const PORT = 4000; // let's define a listening port
we will also create our first request route point / :
app.get('/',(req,res) => res.send("server running"));
// basically we say when you have request for the root url / serve them message: "server running"
app.listen(PORT,()=>console.log('listening on ${PORT}));
// here we are just listening to port 4000 and displaying information to the console where the express server starts running.

Now we can just type: npm start and browse inside: http://localhost:4000 to see our application running!

Ok, let's use mongoose to connect to our MongoDB database:
import mongoose from 'mongoose';
then we will connect with the following options:
mongoose.connect(
'mongodb://localhost/my_db',
{
useNewUrlParser: true,
useUnifiedTopology: true
}
);

...
We will proceed with a standard MVC pattern, where we will be having routes that will get browser requests directing them to specific controllers, controllers that will take care of the logic and will use models to perform various actions on the MongoDB database. Index.js will load up everything from a new subdirectory /src/, where our routes, models, and controllers will reside.

Routes
Now it is time to create our routes properly. We will create directory /src/routes/ with file routes.js inside
there we will define and export the routes for our application like so:
const routes = (app)=>{
 app.route('/user')
.get((req,res)=>res.send('Getting information about all users'))
.post((req,res)=>res.send('Creating new user'));
 app.route('/user/:userID')
.get((req,res)=>res.send('Getting specific user by ID'))
.put((req,res)=>res.send('Updating user by ID'))
.delete((req,res)=>res.send('Deleting user by ID'));
}
export default routes;

inside index.js we will import the created routes;
import routes from '/src/routes/routes';
then we will load up those routes with:
routes(app);

Using MongoDB
We will create a schema for of how our documents inside MongoDB we would like to look like, inside the /models/model.js file:
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
export const UserSchema = new Schema(
{
firstName: {type:String,required: 'Enter firstname'},
lastName: {type:String,required: 'Enter lastname'},
email: {type:String,required: 'Enter email'},
created_at: {
type: Date, 
default: Date.now
}
});

Alright, we will use this UserSchema, when gradually creating our controllers.
But before all this, lets recognize requests we will be working with (inside index.js):
// we will be using the built-in middleware functions in Express, so we can parse requests having JSON payloads inside
to parse the request text of type: application/json
app.use(express.json());
and to parse the request text of type application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));

Then we create a directory with file: controllers/controllers.js
import mongoose from 'mongoose';
import {UserSchema} from '../models/model.js'; //so we will be able to use our newly created UserSchema.
const User = mongoose.model('User',UserSchema); // here we create 2 things: 1) User constant which will hold a reference to the User model, 2) which we created from the provided UserSchema.

We will now create our first function that will work with MongoDB and instruct the model to create new User.
export const addNewUser=(req,res)=>{
let newUser = new User(req.body); // we use the whole request body to create a new User model based on the UserSchema.
// Once we have the model we can use its functions such as save, find, findOneAndUpdate and others, which are provided from MongoDB.
newUser.save((err,user)=>{
if (err){res.send(err);} // we send an error if we couldnt create the db document
 res.json(user); // if everything went well, we output the created document as a json format
})
}

now we go back and modify our routes.js to be able to use the newUser function:
1) import addNewUser: import {addNewUser} from 'controller';
2) change .post method to: post(addNewUser);

Next we copy the newUser function and will modify it to create function that will get all of our Users:

export const getUsers=(req,res)=>{
User.find({}(err,users)=>{ // note here we are finding all users via the {} empty search condition, also note that we don't need to create another instance of User, we just use the already created one above.
if (err){res.send(err);}
 res.json(users);
})
}
Now again in routes.js we import the getUsers function, and change the .get method to:
.get((req,res)=>getUsers);
And by the way you can use PostMan to test if the routes are working correctly !

Now for the user/:userID routes:
// we will use mongoDB .findByID() to get specific user by its ID
export const getUserByID=(req,res)=>{
User.findById(req.params.userID, (err,user)=>{ // note here we are getting the  userID from the request parameters
if (err){res.send(err);}
 res.json(users);
})
}
don't forget to update the get method under the '/user/:userID' route in order to activate the getUserByID function: .get(getUserByID)

Let's update some users:
export const updateUser = (req,res) => {
User.findOneAndUpdate(
{_id:req.params.userID}, // here we will be searching first by _id which equals to the passed inside of :userID
req.body, // we pass the request containing the updated information
{new:true, useFindAndModify:false}, // with new:true we will be returning the newly updated user to the res.json
(err,updateduser) =>{
if (err){res.send(err);}
res.json(updateduser); // we send as json the updated user information

});
}

Try to create the deleteUser function by yourself, it will be using the userID parameter just the way updateUser function did. Hint: you can use the .remove() MongoDB function!

Congratulations and enjoy learning !

Subscribe To My Channel for updates