How to create models and schemas for the API endpoints with Node.js, Express, and Mongoose

This is the second part of the “How to develop API with Node.js and Express” series. Previously we have set up our project. Now we will create models and schemas for the Authors and Books and will create our first API endpoints to create and retrieve authors.

Photo by Glenn Carstens-Peters on Unsplash

Project structure

These are the steps we need to make to create a model for authors and two endpoints we want to develop:

  1. Create models for our objects that we will store in the MongoDB database
  2. Create a Data Access Object (DAO) Interface that will contain operations we need for the endpoints. Go here to learn more about DAO.
  3. Create a service that has methods needed for endpoints and that would call DAO methods.
  4. Create required endpoints that would call the corresponding service methods.

By separating code into different layers/parts we make code easier to develop, read, maintain, and test.

Models

Run this command to install mongoose:

npm i mongoose

Create file authors.js in this location src/services/authors/models/ and paste this code into it:

In this file, we create a new schema for the object author. The authors have first and last names, biography, dates of birth, and death (optional). Using this schema we create a model named AuthorModel.

Now we will do the same for the books model. Create a new file src/services/books/models/books.js with this content:

Books will have a title, description, and author id, which references the AuthorModel we previously created.

DAO Interface

Create a new file in src/services/authors directory named AuthorsDao.js. Let’s create a class with a method that would create a new Author in the database:

This method is pretty straight forward. It creates a new object from the provided parameter author, saves it in the database, and returns the created object converted to JSON. Note that the method has async before the name because we are awaiting asynchronous save method:

await newAuthor.save()

To create a method to retrieve all authors from the database we need to add this code:

This method also returns a Promise, note the async.

For now, we have created all the methods we need.

Authors service

Now we will create a service that will have methods corresponding to methods that we created in DAO. But before doing so we need to create a utility function. We want to store the exact dates users will input for authors birth and death dates, we don’t want them to be dependent on timezone and the dates should have no time (the values for hour, minutes, and seconds would be 0). So let’s create this function that would return exactly that:

src/utils.js file

Create src/services/authors/AuthorsService.js file with this content:

AuthororsService.js

The AuthorsService has dao as a dependency and it calls its methods.

We created a method to validate some of the author's properties, that should be improved later. The createAuthor method firstly calls this method to validate author and only then if object is valid calls dao’s method createAuthor.

In thecreateAuthor method we use our getUTCDateNoTime function for the author’s date of birth and for the date of death if it is provided. Note, that we didn’t mutate the author, but created a new object using its values.

Router

Finally, we are at the router step. In src/services/authors/AuthorsRouter.js we will create GET and POST endpoints:

AuthorsRouter.js

We use express-async-handler. It is a middleware for handling exceptions inside of async express routes and passing them to your express error handlers. Install it by running

npm i express-async-handler

Create asrc/services/authors/index.js file where we will create instances of AuthorsService and AuthorsRouter classes. We will use them to add created endpoints to the server:

We need to install body-parser — a body parsing middleware:

npm i body-parser

Update src/server.js by adding the following imports at the top of the file:

import mongoose from "mongoose";import * as bodyParser from "body-parser";import { authorsRouter } from "./services/authors";

and update runServer method:

We made several changes:

  • We added a new parameter to the function mongoUri that stores the Uri of the database we will use. We use this value for mongoose.connect method to connect to the db.
  • We used body-parser for our server.
  • We added routes from AuthorRouter to the server.
  • We structured the server response when some error happens. Note that we need next parameter for code on line 24, even though we don’t use it

Since we change the parameters list for the runServer function we need to update src/main.js file where we call it:

Create a new environment variable MONGO_URI with URI of your mongo database and run the server:

npm run build

You should get the Example app listening on port 3000! message again (I used port 3000). Go to 127.0.0.1:3000/api/v1/authors and the response should be no authors:

{"authors":[]}

Let’s create a new author. I use Postman for that.

This is the body for the request you might use:

{    "author": {        "firstName": "Stephen",        "lastName": "King",        "biography": "Stephen Edwin King (born September 21, 1947) is an American author of horror, supernatural fiction, suspense, crime, science-fiction, and fantasy novels. His books have sold more than 350 million copies, and many have been adapted into films, television series, miniseries, and comic books.",        "dateOfBirth": "09/21/1947"    }}
Screenshot of POST request in Postman

In response, you should get the same object but with _id property.

Make GET request to retrieve authors and now you should get one author in response.

Try making requests to create a new author with dateOfDeath and it also should work.

Since we set some of the values in AthorModel to be required, if we don’t set them in the request body we will get an error message in response. Test it by making a request with the author without let’s say first name and you should get an error message:

{    "message": "AuthorModel validation failed: firstName: Path `firstName` is required."}

It would be better if we validated the author object before trying to save it to the database, but we won’t do it in this article.

Summary

In this article, we have created schemas and models for our two objects and created two endpoints to create and to retrieve authors. In the next article, we will make proper validation for the data the user sends to the server and create more endpoints.

Full Stack Web Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store