How to Create MongoDB REST API in Node.js and AWS Lambda

Duomly - Sep 14 '20 - - Dev Community

Intro to Serverless course Lesson 3

Today you will learn how to create MongoDB REST API in Node.js.

In the previous episodes of the serverless course, you've learned how to set up the AWS Lambda Serverless project. 
The URL is here:

https://www.blog.duomly.com/lesson-1-serverless-how-to-get-started-tutorial/

In the second lesson, you've learned how to create AWS Lambda Serverless authentication with Node.js and MongoDB, and the URL is here:

https://www.blog.duomly.com/lesson-2-serverless-authentication/

I'm super excited about what we will build because we will go forward in today's lesson and develop more features.

One of the most important parts of the SaaS application is making orders, and it's what we'll do today.

We will learn how to create orders, how to update them, how to handle API endpoints in a Serverless framework, and how to secure them.

Let's start!

And if you prefer video, here is the youtube version:

Change signJWT to module.exports.signJWT in AuthenticationHelpers.js

Because I've forgotten to change that in the previous lesson, now we should go into the AuthenticationHelpers.js file and change "this" with "module.exports".

Let's see how the whole function "verifyPassword" should look:

module.exports.verifyPassword = (sentPassword, realPassword, userId) => {
  return bcrypt.compare(sentPassword, realPassword)
    .then(valid => !valid ? Promise.reject(new Error('Incorrect password or username')) : module.exports.signJWT(userId)
  );
}

Add createdAt to User.js and register function

To make all the data transparent, we should know when our user was created.

We need to add the "createdAt" field into our user model in the User.js file.

const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({  
  name: String,
  email: String,
  password: String,
  premium: Boolean,
  premiumEnds: Date,
  createdAt: Date,
});
mongoose.model('User', UserSchema);

module.exports = mongoose.model('User');

Next, we should go into the AuthenticationHandler.js file, go into the register function, and add that field.

function register(body) {
  return validRegistration(body)
    .then(() => User.findOne({ email: body.email }))
    .then(exists => exists ? Promise.reject(new Error('User exists')) : bcrypt.hash(body.password, 8))
    .then(hashedPass => User.create({ name: body.name, email: body.email, password: hashedPass, premium: false, createdAt: new Date()}))
    .then(user => ({ auth: true, token: signJWT(user._id) })); 
}

How to create a MongoDB model

Okay, now we can go into the development of new features.

The first step that we should do will be creating the Order.js file inside the directory named "order".

Next, we should create a MongoDB model for the order.

const mongoose = require('mongoose');

const OrderSchema = new mongoose.Schema({  
  userId: String,
  status: String,
  amount: Number,
  createdAt: Date,
});
mongoose.model('Order', OrderSchema);

module.exports = mongoose.model('Order');

Add createOrder handler into serverless.yml

As the next step, we should go into the serverless.yml and define the first AWS Lambda function inside.

Define the AWS Lambda function named "createOrder", and pass "order/OrderHandler.create" as the handler.

Next secure that function with "verify-jwt" authorizer.

createOrder:
  handler: order/OrderHandler.create
  events:
    - http:
        path: order/create
        method: post
        cors: true
        authorizer: verify-jwt

Add myOrders into serverless.yml

We should add one more thing into the serverless.yml file.

The thing is the "myOrders" function, with "myOrders" as the handler's name.

JWT still be as the verifier there.

Anyway, remember about changing the endpoint's path.

myOrders:
  handler: order/OrderHandler.myOrders
  events:
    - http:
        path: order/my
        method: get
        cors: true
        authorizer: verify-jwt

Create orderHandler

Great! We can now go into the handler we'll use to handle our Node.js logic by AWS Lambda functions and our API endpoints.

As the first step, you should create a file "OrderHandler.js" inside the "order" directory.

After creating a file, we should import the database, order, success, and errResponse function.

Take a look at the example below:

const db = require('../database');
const Order = require('./Order');
const { success, errResponse } = require('../authentication/AuthenticationHelpers');

Create handler and function to create

In the next step, we handle our create order endpoint.

We can do that by creating function handlers, and it's very important to do that as the node.js exported.module, so serverless will be able to import the functionality and push into the AWS Lambda function.

module.exports.create = (r, cb) => {
  cb.callbackWaitsForEmptyEventLoop = false;
  return db()
    .then(() => create(JSON.parse(r.body), r.requestContext.authorizer.principalId))
    .then(res => success(res))
    .catch(err => errResponse(err));
};

And next, we need to create a function that contains logic responsible for the create order in the MongoDB database:

function create(body, id) {
  return Order.create({userId: id, status: 'pending', amount: body.amount, createdAt: new Date()});
}

Create handler and function for update

In this step, we should create an exported module for the "update" feature.

We don't add it as the AWS Lambda function and endpoint because we don't want to make it public.

Only our application will be able to call that logic, and only after passing some conditions, like if payment was executed successfully.

Let's take a look at the Node.js module that we should create:

module.exports.update = (r, cb) => {
  cb.callbackWaitsForEmptyEventLoop = false;
  return db()
    .then(() => update(JSON.parse(r.body)))
    .then(res => success(res))
    .catch(err => errResponse(err));
};

We should now create the function that will contain the logic for the "update" module:

function update(body) {
  return Order.findByIdAndUpdate(body.id, body, { new: true })
}

Create handler and function for myOrders

Okay, we are almost done with our Serverless Node.JS REST API.

The last logic that we should create is the logic related to my orders.

That logic will search for our orders by looking into the orders where the userId field is the same as the user's id field encoded from the JWT token.

To do that, we need two parts. The first is the exported module:

module.exports.myOrders = (r, cb) => {
  cb.callbackWaitsForEmptyEventLoop = false;
  return db()
    .then(() => myOrders(r.requestContext.authorizer.principalId))
    .then(res => success(res))
    .catch(err => errResponse(err));
};

And the second part is the DB-related logic:

function myOrders(id) {
  return Order.find({userId: id})
    .then(orders => !orders.length ? Promise.reject('Orders not found.') : orders)
    .catch(err => Promise.reject(new Error(err)));
}

How to test MongoDB REST API in Node.js 

Woohoo! You've created the next SaaS application feature, and your orders are ready!

Now, we can go into the testing phase.

There are two methods of testing our Serverless application.

The first one is deploying the application into the AWS infrastructure and testing on the live API, which can be a good experience for you.

If you want to deploy all the app into the AWS Cloud, you need to have configured AWS CLI, and that will create the whole AWS infrastructure, so remember to double-check your settings, regions, etc.

To do that, you just should open the terminal and type:

sls deploy

And there is the second method that I like, and it boosts my development time because I can just start the app locally.

It's excellent, especially when you plan to debug your application because you can put console.logs that will show messages when you fire some endpoints.

It's very handy when you want to see how data comes into our app, or how it's going out.

How to start an app locally I've told you in the previous lessons to use that code.

Conclusion of How to create MongoDB REST API in Node.js

Congratulations! Your AWS Lambda SaaS application looks much more advanced now!

You've learned how to create orders, how to create MongoDB models, how to update items in MongoDB and Node.js, and how to create AWS Lambda functions in Node.js.

I'm very proud of what you've built and hope you will be able to get a lot of value from today's lesson.

Remember about subscribing to our channel to get notification about new lessons immediately!

If you would like to check if your code is correct, here is the URL of today's lesson:

https://github.com/Duomly/aws-serverlesss-nodejs/tree/serverless-course-lesson-3

Duomly programming courses

Thanks for reading,
Radek from Duomly

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .