This Snippet presents a backend model project in Node.js that supports the generic CRUD API based on typescript-rest (express.js extension based on annotation for typescript library). This model project is capable of accelerating application development and facilitating its maintenance. Because of its standardization proposal, the results are code reuse and good development practices.
Introduction and Benefits
The acronym CRUD (CREATE, READ, UPDATE, and DELETE) is an abbreviation for the most common operations to be performed on databases, so CRUD API is the way to carry out these operations through the HTTP protocol (POST, GET, PUT, and DELETE).
The project (ac-node-generic-crud) intends to expose CRUD routes to the specified data model by using the Design Pattern Template Method. Another benefit of this model project is its ability to generate automatic Swagger documentation using the typescript-rest library.
With a simple implementation, the developer will be able to extend concrete classes from abstract classes. These abstract classes have controller, service, and repository layers that handle generic objects, requiring only the data model to be operated by the subclass. After using these features, all exposed CRUD endpoint routes will be able to manipulate the entered data model.
About Design Patterns
Before we talk about how to build a generalist CRUD API in Node.js, let's review the design patterns we'll be using and how they work. But before that, what exactly are design patterns?
In software engineering, a design pattern is an optimized solution to a common problem that exists in various system implementation demands and can be reused in different situations. It is also a template that can be customized to solve a specific problem in code.
Why Use Design Patterns?
Of course, each developer thinks and codes differently, and because of that, some challenges start to emerge. Continuing another developer's work can be a daunting and unproductive task, especially if there isn't a design pattern to follow. Of course, these development hurdles go beyond simplistic analysis. The demand for projects to be delivered quickly, for example, often prevents teams from worrying about documentation, so it becomes a secondary activity.
In practical terms, imagine the following scenario: Developers may be pulled in and out of projects as some developers are assigned to new projects and others on bench are called in. Some internal projects may have problems in their continuity given the lack of standardization of codes and the lack of documentation, making it difficult for another developer to understand the project. A standard design scope is then required to make it easier to understand the code already developed. The biggest challenge now is understanding the business rule.
From a corporate standpoint, another design pattern benefit is customer acceptance. Maintaining a standard means maintaining quality and level of development. So internal organization, if well presented, can be used as a selling point for potential customers.
Design Pattern Categories
Let's briefly review some design pattern definitions:
"In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation" (Source Making).
"In Software Engineering, Structural Design Patterns are Design Patterns that ease the design by identifying a simple way to realize relationships between entities" (Source Making).
"In software engineering, behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication" (Source Making).
Some Patterns Used In This Project
Decorator - Structural Patterns
The Decorator pattern lets you dynamically attach new behaviors to objects, extending their capabilities and functionality.
Singleton - Creational Pattern
The Singleton pattern ensures that a class has only one object as its model instance, but it gives a global point of access to that instance.
Image courtesy of Refactoring Guru
Template Method - Behavioral Pattern:
Template Method is a behavioral design pattern that specifies an algorithm skeleton, allowing subclasses to implement their own methods without changing the base structure. Its applicability is diverse, being recommended mainly for:
- Implementing the invariant parts of an algorithm at once and leaving subclasses to implement behavior that may vary.
- Instances where common behavior between subclasses should be factored and concentrated in a common class to avoid code duplication.
- Replacing only certain parts of an algorithm, making them less affected by changes that happen to other parts of the algorithm.
Image courtesy of Refactoring Guru
The ac-node-generic-crud project generally implements a three-tier architecture: controller, service, and repository.
The controller layer is responsible for routing and validating objects, also implemented in a generic way. The subclass simply implements the getValidationSchema() method and specifies attribute information to the Joi object validator.
The second layer (service) implements business rules. Considering that CRUD operations will be performed using as reference the primary key of each object, the Service layer is responsible only for the redirection of data to the repository layer.
Finally, we have the repository layer, which uses TypeORM as the interface to various relational and nonrelational databases. The first version of the template project supports MongoDB, MySQL, and Postgres. To use other databases, you only need to change the class responsible for the connection. See some examples of connections in the ac-node-generic-crud project database directory (path: src/database).
The implemented model project already has all the necessary settings for the Typecript-Rest library to expose the Swagger documentation of its API. Simply use the data model for three-tier architecture (inheritance of abstract layers), and then, after application build, routes will be available in the url localhost:7000/api-docs.
Unit Test with Mocha, Chai, and Coverage
Mocha is a test framework that configures test suites, and Chai is an expectation framework that makes it easier to build tests against code. The model project is set up with Mocha and Chai and contains some example tests. After testing, coverage reports can be viewed in the /reports/coverage directory.
The report has 4 metrics:
- Statement - each statement declared in program;
- Branch: control structure (as in if statements);
- Function: each function (or subroutine) of the program;
- Lines: each executable line.
Negative and Positive Points
- Inflexibility in development;
- Continuous maintenance (important to be in the community);
- Template methods tend to be harder to maintain the more steps they take.
- Positive Points
- Create a work pattern--challenge is just the business rule;
- Code reuse and the power of inheritance--changing a base class changes all subclasses;
- Speed of maintenance and third-party understanding;
- No time waste for repetitive setups;
- Automatic documentation;
- Supports relational and nonrelational applications;
- Generic object validation.
Typescript-rest is an annotation-based express.js extension for typescript. This library allows you to use ES7 decorators to configure their services. Using this feature provides automatic Swagger documentation generation.
Typescript-rest-boilerplate is a minimalist project based on the typescript-rest library. It has an initial configuration for building APIs, with some examples, and support for testing using Mocha and Chai. It already uses some development aids such as TSLint, clustered server instances, some middleware, and MongoDB connection.
References and Further Reading
I hope you enjoyed this guide on how to build a generalist CRUD API in NodeJS! For references and further reading, check out: