0
0
Typescriptprogramming~7 mins

Generic repository pattern in Typescript

Choose your learning style9 modes available
Introduction

The generic repository pattern helps you manage data in a simple, reusable way. It lets you write one set of code to handle many types of data, so you don't repeat yourself.

When you want to handle different data types (like users, products, orders) with the same code structure.
When you want to keep your data access code clean and easy to maintain.
When you want to separate how you get data from how you use it in your app.
When you want to make testing easier by swapping real data with fake data.
When you want to add new data types without writing new data access code.
Syntax
Typescript
interface IRepository<T> {
  getAll(): Promise<T[]>;
  getById(id: string): Promise<T | null>;
  create(item: T & { id: string }): Promise<T>;
  update(id: string, item: T & { id: string }): Promise<T | null>;
  delete(id: string): Promise<boolean>;
}

class GenericRepository<T> implements IRepository<T> {
  private items: Map<string, T> = new Map();

  async getAll(): Promise<T[]> {
    return Array.from(this.items.values());
  }

  async getById(id: string): Promise<T | null> {
    return this.items.get(id) ?? null;
  }

  async create(item: T & { id: string }): Promise<T> {
    this.items.set(item.id, item);
    return item;
  }

  async update(id: string, item: T & { id: string }): Promise<T | null> {
    if (!this.items.has(id)) return null;
    this.items.set(id, item);
    return item;
  }

  async delete(id: string): Promise<boolean> {
    return this.items.delete(id);
  }
}

The <T> means this code works with any type you give it.

Methods return Promise to simulate async data access like databases.

Examples
This example shows how to create a repository for users.
Typescript
interface User {
  id: string;
  name: string;
  email: string;
}

const userRepo = new GenericRepository<User>();
Here we add a user and then get all users.
Typescript
await userRepo.create({ id: '1', name: 'Alice', email: 'alice@example.com' });
const users = await userRepo.getAll();
This example shows a repository for products, using the same generic class.
Typescript
interface Product {
  id: string;
  title: string;
  price: number;
}

const productRepo = new GenericRepository<Product>();
Sample Program

This program shows how to use the generic repository with a User type. It adds users, reads them, updates one, deletes one, and shows the results at each step.

Typescript
interface IRepository<T> {
  getAll(): Promise<T[]>;
  getById(id: string): Promise<T | null>;
  create(item: T & { id: string }): Promise<T>;
  update(id: string, item: T & { id: string }): Promise<T | null>;
  delete(id: string): Promise<boolean>;
}

class GenericRepository<T> implements IRepository<T> {
  private items: Map<string, T> = new Map();

  async getAll(): Promise<T[]> {
    return Array.from(this.items.values());
  }

  async getById(id: string): Promise<T | null> {
    return this.items.get(id) ?? null;
  }

  async create(item: T & { id: string }): Promise<T> {
    this.items.set(item.id, item);
    return item;
  }

  async update(id: string, item: T & { id: string }): Promise<T | null> {
    if (!this.items.has(id)) return null;
    this.items.set(id, item);
    return item;
  }

  async delete(id: string): Promise<boolean> {
    return this.items.delete(id);
  }
}

interface User {
  id: string;
  name: string;
  email: string;
}

async function main() {
  const userRepo = new GenericRepository<User>();

  await userRepo.create({ id: '1', name: 'Alice', email: 'alice@example.com' });
  await userRepo.create({ id: '2', name: 'Bob', email: 'bob@example.com' });

  const allUsers = await userRepo.getAll();
  console.log('All users:', allUsers);

  const user1 = await userRepo.getById('1');
  console.log('User with id 1:', user1);

  await userRepo.update('2', { id: '2', name: 'Bobby', email: 'bobby@example.com' });
  const updatedUser = await userRepo.getById('2');
  console.log('Updated user with id 2:', updatedUser);

  const deleted = await userRepo.delete('1');
  console.log('Deleted user with id 1:', deleted);

  const usersAfterDelete = await userRepo.getAll();
  console.log('Users after delete:', usersAfterDelete);
}

main();
OutputSuccess
Important Notes

Make sure each item has a unique id property for this pattern to work well.

This pattern helps keep your code clean and easy to change later.

Summary

The generic repository pattern lets you write one data handler for many types.

It helps keep your code organized and reusable.

You can add, get, update, and delete items easily with this pattern.