Erick Wendel

@erickwendel

Patterns — Generic Repository with Typescript and Node.js

Become a master of Codes

If you work with Node.js/Javascript and you have many replicated codes for different data models or create CRUDs (Create, Read, Update and Delete) at all times, then this post is for you!

Generic Repository Pattern

Working with Javascript applications, we have some problems to share similar code between the application, and in many times we create the same code for different applications. That pattern gives to you a power to write an abstraction of data, when we have one abstract class (or many classes) and reuse the implementations independent of your Data Model, passing only your types to someone classes.

Talking about the Repository pattern, it is a representation where you can keep all operations of your database (like a Create, Read, Update and Delete Operations) in one local per Business Entity, when you need to do operations with Database, don’t call directly database drivers and if you have more one database, or differently databases to one transaction, you application only calls the method of repository and is transparently for who calls.

Therefore, the Generic Repository is similar but now you have only one abstraction, one Base Class which have all common operations and your EntityRepository only extends the base class with all implementations of database operations. Following the SOLID principles, that pattern follows the Open / Closed principle, when your baseClass is open for extension, but closed for modification.

When to use a Generic Repository ?

Depends on your Business Model, and critical level of your applications. My opinion about this pattern it’s about the extensibility and enabling you to create only one class to write all common operations, such CRUDs, when all Entities of your application should have a CRUD or similar operations.

When don’t use Generic Repository ?

The same way that you have a power, you have dangerous implicit codes, an clean example for that is:

  • You have two Entity Classes: People and Account.
  • Users can remove People.
  • Users can’t update information about Account (like add more money to account)
  • If both classes extends from BaseClass, that haves update() and remove() methods, the programmer should remember of that rule and not expose remove or update methods to service, or your business case is wrong and dangerous.
Thinking About

Generics with Typescript

Components that are capable of working on the data of today as well as the data of tomorrow will give you the most flexible capabilities for building up large software systems — typescriptlang.org/docs/handbook/generics.html

Following the Typescript's documentation, generics gives the power to build flexible and generic components (or types). From their docs, we have a better example about how it works:

One method that receives one number argument and returns your argument

So, we have a concrete method, which receives one number and returns the same type. If you want pass one string to this method you need to create another method with the same implementation and repeat your code.

With the Generics Implementation, we have the aleatory word to says what is the generic implementation (for convention, uses T to say that is a generic type)

Same method, refactored to Generic implementation

Creating a real project with Generic Repository and Node.js

Lets go! If you don’t understated yet, with the next section you’ll be able be to get the idea.

Requirements:

Testing your environment

After install all environment requirements, test in your terminal if it is all ok.

npm --v && node --version
Output of commands to view Node.js version and npm versions

To verify if your MongoDB is fine, run on another terminal tab, sudo mongod

MongoDB Instance Starting

Then, in another tab run mongo to enter em your database.

Entering em MongoDB database

Then, install the global package typescript, to compile your typescript projects. Run npm install -g typescript

Output of typescript globally package installed

Once you’re done with that, we are ready to move forward :D

Now, we need to create one folder and initializate a Node.js project.

mkdir warriors-project
cd warriors-pŕoject
npm init -y #to init nodejs app without wizard
tsc --init #to init config file to typescript

After that, should open the vscode within your project folder. To create our project, you need create some folders for better organization of our application. We are going to use the folder structure bellow:

.
├── entities
├── package.json
├── repositories
│ ├── base
│ └── interfaces
└── tsconfig.json

Entering in tsconfig.json file, in the section of property "lib": [] modify the value to "lib": [ “es2015”], we alter that property of a json file, to work with es2015 modules, like Promises in Typescript. Alter the outDir property to “outDir”: “lib” to generate .js files in a separated folder.

About our folders, when entities folder is about your data models, repositories folder is about our database operations and interfaces our operations' contracts. Now, we should create our entities, in entities folder, create a Spartan.ts file with the following code:

Entity Classe of a Spartan

Now, on repositories/interfaces folder, we'll create two files, following the single responsability, these files will have our contracts that the abstract classes must be. Our contracts, should follow the generic pattern, that can be written without a fixed type, but, when anyone implements this interfaces, should pass the type for them.

IWrite Generic Interface with our manipulation methods operations
IRead Generic Interface with our read methods operations

After creating our interface, we should create the BaseClass, an abstract class that implements all generic interfaces and has our common implementation for all entities. In base folder, create a BaseRepository.ts with following code

Creating BaseRepository with Interfaces imported

After you importing the interfaces you need to implements the signature of your interfaces, for that, you can press ctrl . to show options of vscode to show options to fix problems and click in “Implements Interface IWrite<T> (Fix all in file)” to add all implementations.

After open options and select fix all in files

Now we have a class similar to code bellow

Our code after fix all interface errors

We should now create the implementations for all methods. Our BaseRepository class, should know how is the database and collection that you can access. At this point, you need to install the mongodb driver package, for that, return to your terminal on project folder and runs npm i -S mongodb @types/mongodb to add mongodb driver and typescript definition of package.

In constructor, we add two arguments, db and collectionName. Your class implementation should like a following code

Now, we created the Repository file to specific entity in repositories folder.

Now, to test our repository and all logic event. We need in root of project, create a Index.ts file, that call all repositories.

Implementation of main code of our application

You need to transpile your Typescript to Javascript files, running the tsc command from terminal, you have now in lib folder all javascript files and now, you can run your application with node lib/Index.js.

To you see the power of Generic Repositories, we go to create more one repository for Heroes with name HeroesRepository.ts and one entity class, that represents a Hero.

Entity Created to represents a Hero
Implementation of HeroRepository without specific operations

Now, we just call that repository in our Index.ts, following the complete code below.

Complete implementation

Conclusion

With one class, we have many implementations to use and to work more easily, for me, the Generics feature in TypeScript is one of most powerful features to work. All the code that you see here is available in the GitHub repo that you can find in the links section below, don’t forget to check it out :D

If you got up to this point, don’t forget to comment, share with your friends and leave some feedback. Please be mindful that this is my first English post so feel free to correct me with some private notes if you happen to spot any error :D

Don’t forget too, to click and claps on post.

More by Erick Wendel

Topics of interest

More Related Stories