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.