DEV Community

Cover image for Object Oriented Express API
Pankaj Gupta
Pankaj Gupta

Posted on

Object Oriented Express API

I've been toying around with the idea of having a scalable express api service, which will handle some of the basic crud operations for a model without writing the actual code for the controller. I've three years of experience and I was not sure if you could something like that but then something happened.

I started to work on Ruby on Rails last year and my perspective has been changed a lot since then, working on different tech stacks, with different people really opens you up. Someone had written a generic handler for all the models and all the basic operations of CRUD were handled without writing a piece of code for the new controller. It was then I realised the power of Object Oriented Programming.

There are a lots of language specific perks of writing code in Ruby, but Object Orientation is a fundamental concept which can be very useful, so I decided to use it in Node and Express and make my first worthwhile contribution to the Open Source Community. I choose Node and Express because I have worked on it before and wanted to see the difference in the quality of code, In other words, I wanted to see how my skills have improved over the years.

I started the project on a Saturday afternoon, I was almost done on Sunday. During the project, I found out some important things:

  • In Express 5, which is currently a beta version, you don't have to throw the error explicitly, It throws the error itself and call the next() with the error, so no more if this then that in the handlers.
  • You can use a whole javascript class as a route handler. (AMAZING!!)

It had to dig a bit to find these two solutions. Now with the help of these two solutions my handlers were looking something like this.

var express = require('express');
var router = express.Router();

const Book = require('../models/Book');
const books = require('../caches/book.cache');
const Base = require('./base.controller');

class BookController extends Base {
  constructor(model) {
    super()
    this.model = model;

    // All the routes declared here!
    router.get('/', books.cache, this.index);
    router.get('/:id',books.cache, this.get);
    router.post('/', this.create);
  }
}

new BookController(Book);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

You can use this in your routes as this:

var booksRouter = require("../controllers/book.controller");

module.exports = function(app) {
  app.use("/books", booksRouter);
};
Enter fullscreen mode Exit fullscreen mode

Isn't this neat and clean? As you can see I've imported the Model, and a cache class I've implemented for this. If you look closely you'll realize how is this working. Thats the best part. I've implemented a base controller which can be extented to every controller and they can use the methods defined in the base controller to implement the basic crud.

The base controller looks something like this:

class Base {
  constructor() {}

  index = async (req, res, next) => {
    const resources = await this.model.find({})
    return res.status(200).json(this.apiSend(resources))
  }

  get = async (req, res, next) => {
    const resource = await this.model.findById(req.params.id)
      .orFail(new NotFoundError());
    return res.status(200).json(this.apiSend(resource))
  }

  create = async (req, res) => {
    const { ...body } = req.body
    const resource = await this.model.create(body)
    return res.status(200).json(this.apiSend(resource))
  }

  apiSend = (json) => {
    return {
      data: json
    }
  }
}

module.exports = Base;
Enter fullscreen mode Exit fullscreen mode

As You can see there is nothing specific to any model or controller. If you have a create method method which needs a bit of manipulations of post changes, You can implement that in your new controller using a service module specific to the controller or the model.

A important point to notice is there is no try catch blocks in any of the handlers, it's the magic of express 5.

If you guys are interested you can check it out here

This project still needs a lot of improvements, but I am happy that it started this way! I'll keep posting further updates.

Top comments (10)

Collapse
 
corners2wall profile image
Corners 2 Wall

Amazing examples! Great work

Collapse
 
pankajgupta221b profile image
Pankaj Gupta

Thanks

Collapse
 
doublefaces profile image
DoubleFacess

Interesring article. i porting my backends to type script-express...

Collapse
 
pankajgupta221b profile image
Pankaj Gupta

After this article?

Collapse
 
doublefaces profile image
DoubleFacess

No, i meant that can i add your notions, thx

Thread Thread
 
pankajgupta221b profile image
Pankaj Gupta

Yes

Collapse
 
aminnairi profile image
Amin

Curious to see how you handle query parameters for route like

GET /users?fields=email,phone
Enter fullscreen mode Exit fullscreen mode
Collapse
 
pankajgupta221b profile image
Pankaj Gupta

For handling params we can have a serializers with a particular naming convention which is called for handling params separately

Collapse
 
web-dev-codi profile image
Web-Dev-Codi

Any specific reason you chose to use var?
Great article

Collapse
 
pankajgupta221b profile image
Pankaj Gupta

No, this piece of code is just a POC, all the optimisations and everything will be done in the coming days.