Mongoose Express REST API Project Skeleton
A Express and Mongoose REST API skeleton project.
Check out the walkthrough here.
Read the wiki.
Getting started
This is meant to be a starter project template.
- Fork the project.
 
OR
- Download the ZIP file and extract its contents.
 - Update 
package.jsonwith your own name, version and description. - 
    
Install the node modules.
npm installOR
npm i - 
    
Update
config/config.envwith a custom PORT and MONGO_URI.PORT=3000 MONGO_URI=mongodb://localhost:27017/classBasedRestApiDb - 
    
Start the development server from the terminal.
npm run dev 
Usage
Lets take an example of a simple API for books. View the example project here.
Create a file models/Book.js. In this file we will add our mongoose Schema for a Book.
const mongoose = require("mongoose");
const BookSchema = new mongoose.Schema({
  name: {
    type: String,
  },
  description: {
    type: String,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});
module.exports = mongoose.model("Book", BookSchema);
Now that our schema is ready we can make the API.
In loaders/expressLoader.js, create a new instance of the Api class after the developmentHelpers middleware.
let myApi = new Api();
To this instance we can add our mongoose schemas using the ModelData class.
In loaders/expressLoader.js, require the mongoose schema for Book.
const Book = require("../models/Book");
Create an instance of the ModelData class for the Book schema.
const bookModelData = new ModelData({
  apiModel: Book,
  modelName: "book",
});
By default this creates a Router instance with a Controller and Service instance, using the apiModel and modelName.
apiModel should have a value of the mongoose schema, in our case Book.
modelName should have a string for the schema which will be used for its api route.
Now we can add the ModelData instance called bookModelData to our Api instance.
myApi.addModelData(bookModelData);
Finally, after adding our ModelData to our Api instance, we can initialize the routers.
myApi.initRouters(app);
This adds the Router instances to our express application.
Restart the server. Your REST API is now ready.
This creates 4 routes by default:
| Request Type | Route | Description | 
|---|---|---|
| POST | /books/ | Create a book. | 
| GET | /books/:bookId | Get a book by id. | 
| PUT | /books/:bookId | Update a book by id. | 
| DELETE | /books/:bookId | Delete a book by id. | 
Using the classes to extend functionality
Lets say we want to add a route to get all books.
Create a file services/book.js. We will add a function to our service by inheritance.
We can make use of the asyncHandler wrapper function to handle errors in our promises or async-await. Just wrap your asynchronous function inside asyncHandler to automatically catch the error in the promises.
const asyncHandler = require("../middleware/async");
const Service = require("./index");
class BookService extends Service {
  getAllBooks = asyncHandler(async () => {
    return await this.model.find();
  });
}
module.exports = BookService;
Now we will create a custom Controller to use our new BookService.
Create a file controllers/book.js. We will add a function to our controller just like we did for BookService.
const Controller = require("./index");
const BookService = require("../services/book");
const SuccessfulResponse = require("../middleware/succesfulResponse");
const ErrorResponse = require("../utils/errorResponse");
const asyncHandler = require("../middleware/async");
class BookController extends Controller {
  constructor(model, modelName) {
    super(model, modelName, new BookService(model));
  }
  getAllBooks = asyncHandler(async (req, res, next) => {
    const books = await this.service.getAllBooks();
    if (!books) {
      return next(new ErrorResponse(`No books were found.`, 404));
    }
    new SuccessfulResponse(
      res,
      200,
      `The books were successfully found.`,
      books
    ).buildResponse();
  });
}
module.exports = BookController;
Now we will make our own router that uses BookController. Create a file routes/book.js.
const Router = require("./index");
const BookController = require("../controllers/book");
class BookRouter extends Router {
  constructor(model, modelName) {
    super(model, modelName, new BookController(model, modelName));
    this.router.route(this.endpoint).get(this.controller.getAllBooks);
  }
}
module.exports = BookRouter;
Require BookRouter in loaders/expressLoader.js
const BookRouter = require("../routes/book");
Now that we have our BookRouter we only have to update our ModelData instance. We will add the router option to bookModelData.
const bookModelData = new ModelData({
  apiModel: Book,
  modelName: "book",
  router: new BookRouter(Book, "book"),
});
Restart the server. Your updated REST API is now ready.
| Request Type | Route | Description | 
|---|---|---|
| POST | /books/ | Create a book. | 
| GET | /books/ | Get all books. | 
| GET | /books/:bookId | Get a book by id. | 
| PUT | /books/:bookId | Update a book by id. | 
| DELETE | /books/:bookId | Delete a book by id. |