Implementing the Repository Pattern with TypeScript and Node.js

The repository pattern is a design pattern that abstracts data storage, allowing for the decoupling of data access logic from business logic. This leads to a more maintainable, flexible, and scalable codebase.

Benefits of the Repository Pattern

  • Enforces the dependency inversion principle
  • Allows for separate testing of business logic and data access logic
  • Keeps code structured and organized
  • Reduces code duplication and enhances maintainability

Implementing the Repository Pattern with TypeScript and Node.js

In this example, we’ll use a Node.js framework and an ORM to create a PostRepository that encapsulates data access logic for posts.

Step 1: Create a New App

Create a new app using the framework’s CLI:

npx @my-framework/cli new my-app

Step 2: Set Up the Database

Install the required dependencies:

npm install --save @my-framework/orm orm knex pg

Set up the database connection in app.module.ts:


import { Module } from '@my-framework/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { OrmModule } from '@my-framework/orm';
import { knexConfig } from './knex.config';

@Module({
  imports: [
    OrmModule.forRoot(knexConfig),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Step 3: Create a Post Model and Migration

Create a post.model.ts file:


import { Model } from 'orm';

export class Post extends Model {
  id: number;
  title: string;
  content: string;
}

Create a migration for the posts table:


import { Migration } from '@my-framework/orm';

export class PostMigration implements Migration {
  async up(knex: Knex): Promise {
    await knex.schema.createTableIfNotExists('posts', (table) => {
      table.increments('id').primary();
      table.string('title');
      table.text('content');
    });
  }

  async down(knex: Knex): Promise {
    await knex.schema.dropTableIfExists('posts');
  }
}

Step 4: Implement the Repository Pattern

Create a post.repository.ts file:


import { Injectable } from '@my-framework/common';
import { InjectRepository } from '@my-framework/orm';
import { Post } from './post.model';

@Injectable()
export class PostRepository {
  constructor(private readonly postModel: typeof Post) {}

  async findAll(): Promise<Post[]> {
    return this.postModel.query();
  }

  async findOne(id: number): Promise<Post> {
    return this.postModel.query().findById(id);
  }

  async create(post: Post): Promise<Post> {
    return this.postModel.query().insert(post);
  }

  async update(id: number, post: Post): Promise<Post> {
    return this.postModel.query().updateAndFetchById(id, post);
  }

  async delete(id: number): Promise<void> {
    await this.postModel.query().deleteById(id);
  }
}

Using the Repository

Inject the PostRepository into a service or controller:


import { Controller, Get } from '@my-framework/common';
import { PostRepository } from './post.repository';

@Controller('posts')
export class PostController {
  constructor(private readonly postRepository: PostRepository) {}

  @Get()
  async findAll(): Promise<Post[]> {
    return this.postRepository.findAll();
  }
}

This implementation provides a basic example of the repository pattern with TypeScript and Node.js. It demonstrates how to abstract data storage and decouple data access logic from business logic.

Leave a Reply