Thursday, October 20, 2022
HomeWeb DevelopmentExploring the repository sample with TypeScript and Node

Exploring the repository sample with TypeScript and Node


The repository sample may be outlined as an abstraction over knowledge storage, permitting the decoupling of knowledge entry logic from the enterprise logic. The repository sample has plenty of advantages:

  • It enforces the dependency inversion precept
  • Since enterprise logic and knowledge entry logic are loosely coupled, they are often examined individually
  • It helps with protecting the code structured and arranged
  • It reduces duplication of code and enhances code maintainability

Like most design patterns, the repository sample is language agnostic. With that in thoughts, I’ll be exhibiting how one can implement the repository with TypeScript and Node.js. For the aim of demonstration, I’ll be utilizing Nest, a Node.js framework.

Getting began

Create a brand new Nest app

Like I mentioned earlier, we’ll be utilizing the Nest framework. So, let’s begin by making a recent Nest utility.

First, set up the Nest CLI in the event you don’t have already got it put in:

npm set up -g @nestjs/cli

As soon as put in, we are able to use the CLI to create a brand new Nest utility:

nest new nest-repository-pattern

To reveal the repository sample, we’ll be utilizing the idea of publish. Let’s create the module and controller for it:

nest generate module publish

nest generate controller publish --no-spec

These instructions will generate a publish.module.ts file and a publish.controller.ts file respectively inside a publish listing throughout the src listing.

Database setup

Subsequent, let’s arrange the database for our newly created Nest utility. I’ll be utilizing PostgreSQL, however you need to use any of the databases Knex helps. To work together with our database, we’ll be utilizing Objection.js, which is an ORM for Node.js constructed on high Knex. For this tutorial, we’ll be utilizing Nest Objection, a Nest module for Objection.

So, let’s set up all the mandatory dependencies:

npm set up knex objection @willsoto/nestjs-objection pg

As soon as put in, we are able to register the Nest Objection module contained in the imports array of src/app.module.ts and go alongside our database particulars:

// src/app.module.ts

import { Module } from '@nestjs/frequent';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ObjectionModule } from '@willsoto/nestjs-objection';

@Module({
  imports: [
    ObjectionModule.register({
      config: {
        client: 'pg',
        useNullAsDefault: true,
        connection: {
          host: '127.0.0.1',
          port: 5432,
          user: 'postgres',
          password: '',
          database: 'nest-repository-pattern',
        },
      },
    }),
  ],
  controllers: [AppController],
  suppliers: [AppService],
})
export class AppModule {}

Mannequin and migration

To reveal the repository sample, we’ll be utilizing the idea of publish. So let’s create a Put up mannequin and the corresponding migration to create a posts desk.

Let’s begin with the mannequin. Contained in the publish listing, create a brand new publish.mannequin.ts file and paste the next code in it:

// src/publish/publish.mannequin.ts

import { Mannequin } from 'objection';

export default class Put up extends Mannequin {
  static tableName="posts";

  id: quantity;
  title: string;
  content material: string;
}

The Put up mannequin extends the bottom mannequin from Objection. Then we outline the title of the desk that this mannequin will use. Lastly, we outline the columns that the desk can have and their varieties.

Subsequent, let’s register the mannequin with the PostModule by updating the module as beneath:

// src/publish/publish.module.ts

import { Module } from '@nestjs/frequent';
import { ObjectionModule } from '@willsoto/nestjs-objection';
import { PostController } from './publish.controller';
import Put up from './publish.mannequin';

@Module({
  imports: [ObjectionModule.forFeature([Post])],
  controllers: [PostController],
})
export class PostModule {}

Let’s create the migration for the posts desk. Earlier than we are able to create migrations, our challenge must have a knexfile, and we are able to create the file by working the command beneath:

npx knex init -x ts

By default the init command will create a knexfile.js, however since we’re working with TypeScript, passing -x ts will instruct the init command to create a knexfile.ts as a substitute. The file will created within the root of the challenge. Then, we change it content material with the next:

// knexfile.ts

import sort { Knex } from 'knex';

const config: { [key: string]: Knex.Config } = {
  improvement: {
    shopper: 'pg',
    connection: {
      host: '127.0.0.1',
      port: 5432,
      consumer: 'postgres',
      password: '',
      database: 'nest-repository-pattern',
    },
    migrations: {
      listing: './src/database/migrations',
    },
  },
};

module.exports = config;

Ideally, you may need to have completely different configurations for various environments (improvement, staging, manufacturing, and so on.), however for the aim of this tutorial, I’ve solely added the configuration for the event setting. Along with the database config particulars, we additionally specified the listing the place migrations will reside.

Now, we are able to create the migration for the posts desk:

npx knex migrate:make create_posts_table

As laid out in knexfile.js, the migration can be created inside src/database/migrations. Open it and replace it as beneath:

// src/database/migrations/TIMESTAMP_create_posts_table.ts

import { Knex } from 'knex';

export async operate up(knex: Knex): Promise<void> {
  return knex.schema.createTable('posts', operate (desk) {
    desk.increments('id');
    desk.string('title').notNullable();
    desk.textual content('content material').notNullable();
  });
}

export async operate down(knex: Knex): Promise<void> {
  return knex.schema.dropTable('posts');
}

Within the up operate, we’re making a posts desk in our database with three columns: id, title, and content material. The up operate can be executed after we run the migration. Then contained in the down operate, we’re merely dropping the posts desk which may have been created. The down operate can be executed after we roll again the migration.

Lastly, let’s run the migration:

npx knex migrate:up

Implementing the repository sample

Now, let’s get to the meat of this tutorial. The repository sample makes use of the idea of contracts (interface) and concrete implementations. Mainly, we outline contracts/interfaces that we’d desire a concrete implementation (class) to stick to.

Creating the repository contract

Having mentioned that, let’s create the publish contract/interface. Inside src, create a brand new repositories listing. That is the place we’ll retailer all our repositories. Contained in the newly created listing, create a PostRepositoryInterface.ts file with the next content material:

// src/repositories/PostRepositoryInterface.ts

import Put up from '../publish/publish.mannequin';

export default interface PostRepositoryInterface {
  all(): Promise<Put up[]>;
  discover(id: quantity): Promise<Put up>;
  create(knowledge: object): Promise<Put up>;
}

That is the contract we wish all our publish concrete implementation to stick to. To maintain issues easy and simple, I’ve solely added three strategies.

Creating the concrete implementation

Subsequent, let’s create the concrete implementation. Since our utility present makes use of Knex to work together with the database, this would be the Knex implementation. Nonetheless contained in the repositories listing, create a brand new KnexPostRepository.ts file with the next content material:

// src/repositories/KnexPostRepository.ts

import { Inject } from '@nestjs/frequent';
import Put up from 'src/publish/publish.mannequin';
import PostRepositoryInterface from './PostRepositoryInterface';

export default class KnexPostRepository implements PostRepositoryInterface {
  constructor(@Inject(Put up) personal readonly postModel: typeof Put up) {}

  async all(): Promise<Put up[]> {
    return this.postModel.question();
  }

  async discover(id: quantity): Promise<Put up> {
    return this.postModel.question().the place('id', id).first();
  }

  async create(knowledge: object): Promise<Put up> {
    return this.postModel.question().insert(knowledge);
  }
}

The KnexPostRepository class implements the PostRepositoryInterface we created earlier, and so it due to this fact should adhere to the phrases of the contract; that’s, implement these strategies outlined within the interface. Inside the category constructor, we inject the Put up mannequin into the category. Since we now have entry to the Put up mannequin, we are able to use it to carry out the mandatory operations within the respective strategies.

Utilizing the repository

Now, to make use of the KnexPostRepository we simply created, we have to first register with the Nest IoC container. We will try this by including it to the suppliers array of the PostModule:

// src/publish/publish.module.ts

...
import KnexPostRepository from 'src/repositories/KnexPostRepository';

@Module({
  ...
  suppliers: [
    { provide: 'PostRepository', useClass: KnexPostRepository },
  ],
  ...
})

export class PostModule {}

Contained in the suppliers array, we’re saying, “Hey, Nest, we wish the PostRepository token to resolve to the KnexPostRepository class.” In so doing, every time we inject PostRepository (extra on this shortly), we are going to get an occasion of KnexPostRepository.

Now, let’s truly make use of the repository. Replace the PostController:

// src/publish/publish.controller.ts

import { Controller, Get, Inject } from '@nestjs/frequent';
import PostRepositoryInterface from 'src/repositories/PostRepositoryInterface';
import Put up from './publish.mannequin';

@Controller('publish')
export class PostController {
  constructor(
    @Inject('PostRepository')
    personal readonly postRepository: PostRepositoryInterface,
  ) {}

  @Get()
  async findAll() {
    return this.postRepository.all();
  }
}

The magic occurs contained in the constructor. Bear in mind the PostRepository token from above? We inject it into the controller by way of its constructor, and in consequence the postRepository property can be an occasion of KnexPostRepository as defined above. Then we are able to conveniently use any of the strategies outlined within the repository.

That’s how one can use the repository. If down the road in the midst of the challenge we determine to change knowledge entry layer to one thing like Prisma, we’ll simply must create a PrismaPostRepository class that implements the PostRepositoryInterface:

// src/repositories/PrismaPostRepository.ts

import PostRepositoryInterface from './PostRepositoryInterface';

export default class PrismaPostRepository implements PostRepositoryInterface {  
  async all(): Promise<Put up[]> {
    // Prisma logic
  }

  async discover(id: quantity): Promise<Put up> {
    // Prisma logic
  }

  async create(knowledge: object): Promise<Put up> {
    // Prisma logic
  }
}

Then merely register in with the Nest IoC container:

// src/publish/publish.module.ts

...
import KnexPostRepository from 'src/repositories/KnexPostRepository';

@Module({
  ...
  suppliers: [
    // { provide: 'PostRepository', useClass: KnexPostRepository },
    { provide: 'PostRepository', useClass: PrismaPostRepository },
  ],
  ...
})

export class PostModule {}

The controller code will principally stay the identical.

Refactoring the repository contract to make use of generics

Because it stands, we’ve got efficiently applied the repository sample and we are able to merely name it a day at this level. You’ll discover one thing although: PostRepositoryInterface is tightly coupled with the Put up mannequin, which isn’t an issue, per se, however think about we need to add commenting performance to our utility. We would lean in the direction of making a CommentRepositoryInterface that can have the identical strategies and construction as PostRepositoryInterface. Then, we’ll create a KnexCommentRepository that can implement CommentRepositoryInterface.

You’ll be able to instantly see the sample of code duplication as a result of PostRepositoryInterface and CommentRepositoryInterface are principally the identical with simply completely different fashions. So we’re going to refactor the interface such that it may be reusable with any fashions.

We’d like a option to go the mannequin to the interface. Fortunately for us, we are able to simply obtain that utilizing TypeScript generics.

We’re going to rename PostRepositoryInterface to RepositoryInterface and replace the code:

// src/repositories/RepositoryInterface.ts

export default interface RepositoryInterface<T> {
  all(): Promise<T[]>;
  discover(id: quantity): Promise<T>;
  create(knowledge: object): Promise<T>;
}

Right here, T would be the mannequin that the concrete implementation is for. Any class that desires to utilize this interface should go to it the mannequin to totally adhere to the contract.

Now, we are able to make a slight adjustment to KnexPostRepository by passing the Put up mannequin to the RepositoryInterface:

// src/repositories/KnexPostRepository.ts

import RepositoryInterface from './RepositoryInterface';

export default class KnexPostRepository implements RepositoryInterface<Put up> {
  // remainder of the code stay the identical
}

Then, KnexCommentRepository can appear like this:

// src/repositories/KnexCommentRepository.ts

import RepositoryInterface from './RepositoryInterface';

export default class KnexCommentRepository implements RepositoryInterface<Remark> {
  // strategies implementation for commenting performance
}

Conclusion

On this tutorial, we discovered concerning the repository sample, a few of its advantages, and how one can implement the repository sample with TypeScript and Node.js. Additionally, we noticed how one can scale back code duplication utilizing TypeScript generics.

You will get the entire supply code for our demo from this GitHub repository.

200’s solely Monitor failed and gradual community requests in manufacturing

Deploying a Node-based internet app or web site is the simple half. Ensuring your Node occasion continues to serve assets to your app is the place issues get harder. When you’re involved in making certain requests to the backend or third social gathering providers are profitable, attempt LogRocket. https://logrocket.com/signup/

LogRocket is sort of a DVR for internet and cell apps, recording actually the whole lot that occurs whereas a consumer interacts along with your app. As a substitute of guessing why issues occur, you possibly can combination and report on problematic community requests to rapidly perceive the foundation trigger.

LogRocket devices your app to document baseline efficiency timings corresponding to web page load time, time to first byte, gradual community requests, and in addition logs Redux, NgRx, and Vuex actions/state. .

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments