# Create a web API with ExpressWeb and MongoDB

# Overview

This tutorial creates the following API:

API Description Request Body Response Body
GET /api/todoitems Get all to-do items None Array of to-do items
GET /api/todoitems/{id} Get an item by ID None To-do item
POST /api/todoitems Add a new item To-do item To-do item
PUT /api/todoitems/{id} Update an existing item To-do item None
DELETE /api/todoitems/{id} Delete an item None None

# Setting up

To install ExpressWeb, Make sure you have Nodejs and ts-node installed. Once that is done, you can use npx expresswebcli new command, followed by your project name and --ts or --typescript to install your ExpressWeb Project.

--ts or --typescript flag enables Expresswebjs to generate the typescript version.

⚠️ It is advised to use Bash command-line when working with ExpressWebJs. Powershell is a task-based command-line interface, specifically designed for system admins and is based on the . Net Framework. Bash is a command-line and scripting language for most Unix/Linux-based operating systems.
 npx expresswebcli new myProjectName --typescript
Copied!

Or you can use ts emplace of typescript

 npx expresswebcli new myProjectName --ts
Copied!

You can decide to install expresswebcli globally via npm like so:

npm install -g expresswebcli
Copied!

Then run the below command to create your project.

  expresswebcli new myProject --ts
Copied!

Once that is done, move into your project directory or use the command below in your terminal.

 cd myProjectName
Copied!

Then install all dependencies by running npm install.

# Connecting to MongoDB using Mongoose

Install Mongoose

Once that is done, run the following command to generate your .env file from example.env

 cp example.env .env
Copied!

We can now configure our env file by updating the following:

DB_SHOULD_CONNECT=true
DB_DRIVER=mongoose
DB_HOST=localhost
DB_PORT=27017
DB_USER= your-username-goes-here
DB_PASSWORD= your-password-goes-here
DB_DATABASE= your-database-name-goes-here
Copied!

Once that is done, we set our ORM to Mongoose in Config/Database.ts file.

//Config/Database.ts

import * as mongoose from "mongoose"; 👈  //import mongoose
import { env, orm } from "expresswebcorets/lib/Env";
import { Path } from "expresswebcorets/lib/Utils/Path";
import { DBConnection, MongooseConfigurationType } from "expresswebcorets/lib/Database/DataSourceConfig"; 👈  //import mongoose configuration type

export default {
  /*
  |--------------------------------------------------------------------------
  | Database ORM
  |--------------------------------------------------------------------------
  | ExpressWeb currently supports the following Object Relational Mappers(ORM)
  | Objection and TypeORM for sql databases and  Mongoose for mongo DB.
  | You need to select one depending on the type of database you are working on.
  |
  */
  ORM: env("ORM", orm.Mongoose), 👈  //set orm to mongoose

  /*
  |--------------------------------------------------------------------------
  | Database Provider
  |--------------------------------------------------------------------------
  | With respect to the orm you selected, you need to import the provider and
  | assign it to provider.
  | Example:
  | for objection, import * as objection from "objection"
  | for typeorm, import * as typeorm from "typeorm"
  | Then assign typeorm to provider like this:
  | provider: typeorm
  |
  */
  provider: mongoose, 👈  //set imported mongoose to provider

  /*
  |--------------------------------------------------------------------------
  | Database Multitenance
  |--------------------------------------------------------------------------
  | Database multitenance can be activated by switching the value to true and can
  | be deactivated by switching it to false.
  |
  */
  database_multitenance: env("DB_MULTITENANCE", false),
  /*
  |--------------------------------------------------------------------------
  | Multitenance Connections
  |--------------------------------------------------------------------------
  | Database multitenance connection enables interaction with multiple
  | SQL databases where each database is a tenant in your system.
  | The tenant array accepts an object of database connections (tenants).
  |
  */
  multitenant_tenants: DBConnection.multitenant<MongooseConfigurationType>("Mongoose", []),

  /*
  |--------------------------------------------------------------------------
  | Database Connection
  |--------------------------------------------------------------------------
  | Here we define connection settings for both TypeORM, Objection, and mongoose.
  | For typeORM, npm i --save typeorm
  | For Objection, npm i --save objection
  | For Mongoose, npm i --save mongoose
  | --------------------------------------------------------------------------
  | For SQL db, install the driver of your choice
  | mysql driver, npm i --save mysql mysql2
  | postgres driver, npm i --save pg pg-hstore
  |
  */
  connection: DBConnection.connect<MongooseConfigurationType>({  👈  //add mongoose configuration type
    client: env("DB_CONNECTION"),
    connection: {
        host: env("DB_HOST"),
        port: env("DB_PORT"),
        user: env("DB_USER"),
        password: env("DB_PASSWORD"),
    },
    database: env("DB_DATABASE"),
    options: {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    },
  }),

  /*
  |--------------------------------------------------------------------------
  | Migration Configuration
  |--------------------------------------------------------------------------
  | Here we have database migration configuration.
  | Which includes the following:
  */
  migrations: {
    directory: Path("Database/Migrations/"),
    tableName: "migrations",
    stub: Path("Database/Migrations/migrationLayout.stub"),
    extension: "ts",
  },

  /*
  |--------------------------------------------------------------------------
  | Seed Configuration
  |--------------------------------------------------------------------------
  | Here we have database seed configuration.
  | Which includes the following:
  */
  seeds: {
    directory: Path("Database/Seeds/"),
  },

  /*
  |--------------------------------------------------------------------------
  | Redis Connection
  |--------------------------------------------------------------------------
  | Here we define connection settings for Redis database.
  | npm i --save ioredis
  */
  redis: {
    client: env("REDIS_CLIENT", "default"),

    default: {
      host: env("REDIS_HOST", "127.0.0.1"),
      port: env("REDIS_PORT", 6379),
      password: env("REDIS_PASSWORD", null),
      database: env("REDIS_DB", 0),
    },

    cache: {
      host: env("REDIS_HOST", "127.0.0.1"),
      port: env("REDIS_PORT", 6379),
      password: env("REDIS_PASSWORD", null),
      database: env("REDIS_DB", 0),
    },
  },
};
Copied!

With this we are done setting up our mongoDB with mongoose. Next will be to configure our service to run with http server. This is done in the app.ts in the project root directory.

/*
|---------------------------------------------------------------
| Http Server
|---------------------------------------------------------------
| This file bootstraps ExpressWebJs to start the Http server.
| Application Host, Port and Transfer Protocols are configured
| in the .env file. You are free to configure them. 
|
*/
import { StartApp } from "expresswebcorets";

StartApp.withHttpServer();
Copied!

Once that is done, we can start our application in development mode with the following command.

  npm run dev
Copied!

# Generating todo model class

To generate model class, use the following command

  ts-node maker make-nosql-migration Todo
Copied!

Once that is done, let's update TodoModel class in App/Model directory.

import { Types } from "mongoose";

export interface ITodoSchema {
  _id: Types.ObjectId;
  name: string;
  isComplete: boolean;
}
Copied!
import mongoose, { Schema } from "mongoose";
import { ITodoSchema } from "./Types/ITransactionSchema";

const TodoSchema: Schema = new Schema(
  {
    _id: {
      type: mongoose.Schema.Types.ObjectId,
      auto: true,
    },
    name: { type: String, enum: TransactionInitiator, required: false },
    isComplete: { type: Boolean, default: false },
  },
  { timestamps: { createdAt: "created_at", updatedAt: "updated_at" } }
);

const TodoModel = mongoose.model<ITodoSchema>("Todo", TodoSchema);
export default TodoModel;
Copied!

# Add and register a repository class

Create TodoRepository class in App/Repository directory and enter the following code:

import { TodoModel } from "App/Model/TodoModel";
import { ITodoModel } from "App/Model/ITodoModel";
import { MongooseRepository } from "Elucidate/Repository/Mongoose";

export class TodoRepository extends MongooseRepository<ITodoModel> {
  constructor() {
    super(TodoModel);
  }
}
Copied!

Once that is done, you can now register TodoRepository in AppServiceProvider class located at App/Provider directory

import { TodoRepository } from "App/Repository/TodoRepository";  👈  //import TodoRepository

export class AppServiceProvider extends ServiceProvider {
  /**
   * Register any application services.
   * @return void
   */
  public register() {
    this.singleton(TodoRepository);  👈  //Register as singleton
  }
}
Copied!

# Scaffold a controller

Before we continue, make sure that all of your changes so far are saved.

You can generate your resource controller with the following command

  ts-node maker make-controller TodoController r
Copied!

This command will generate a controller in App/Http/Controllers directory. The controller will contain a method for each of the available resource operations.

import { Request, Response } from "Config/Http";
import { BaseController } from "./BaseController";

export class TodoController extends BaseController {
  /**
   * Display a listing of the resource.
   * @method GET
   * @endpoint
   */
  public async index(req: Request, res: Response) {
    throw new Error("TodoController index method not implemented.");
  }

  /**
   * Store a newly created resource in storage.
   * @method POST
   * @endpoint
   */
  public async store(req: Request, res: Response) {
    throw new Error("TodoController store method not implemented.");
  }

  /**
   * Display the specified resource.
   * @method GET
   * @endpoint
   */
  public async show(req: Request, res: Response) {
    throw new Error("TodoController show method not implemented.");
  }

  /**
   * Update the specified resource in storage.
   * @method PATCH
   * @endpoint
   */
  public async update(req: Request, res: Response) {
    throw new Error("TodoController update method not implemented.");
  }

  /**
   * Remove the specified resource from storage.
   * @method DELETE
   * @endpoint
   */
  public async destroy(req: Request, res: Response) {
    throw new Error("TodoController destroy method not implemented.");
  }
}
Copied!

Once TodoController is generated, we can now inject TodoRepository into TodoController constructor

import { Request, Response } from "Config/Http";
import { BaseController } from "./BaseController";
import {TodoRepository} from "App/Repository/TodoRepository"

export class TodoController extends BaseController {
   constructor(private todoRepository:TodoRepository){}   👈  //inject TodoRepository
}
Copied!

Lets save todo from http request:

  /**
   * Store a newly created resource in storage.
   * @method POST
   * @endpoint
   */
  public async store(req: Request, res: Response) {
    const todo = req.body;
    const createdTodo = await this.todoRepository.save(todo);
    return this.response.OK(res, createdTodo);
  }
Copied!

Lets fetch all todos:

  /**
   * Display a listing of the resource.
   * @method GET
   * @endpoint
   */
  public async index(req: Request, res: Response) {
    const todos = await this.todoRepository.findAll();
    return this.response.OK(res, todos);
  }
Copied!

# Todo route

Now that we have created our todo controller class, let's create the route. This is done in the Route/api.ts file in the project root directory.

import { Route } from "Elucidate/Route/RouteManager";
import { Request, Response } from "Config/Http";

/*
|--------------------------------------------------------------------------
| Api route
|--------------------------------------------------------------------------
|
| Here is where you can register your application routes. These
| routes are loaded by the RouteProvider. Now create something great!
|
*/

Route.get("/", async (req: Request, res: Response) => {
  res.send("Welcome to ExpressWebJs Version 4.2");
});

Route.post("/todo", "TodoController@save");

Route.get("/todo", "TodoController@index");

//--------------------------------------------------------------------------
export default Route.exec;
Copied!

We can now test our api using postman:

To get all todos, make a GET request to http://127.0.0.1:5100/api/todo

To save a new todo, make a Post request to http://127.0.0.1:5100/api/todo