Skip to content

Instantly share code, notes, and snippets.

@MurylloEx
Last active July 25, 2023 17:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MurylloEx/63638cf4c386f9d13a471f1794c7dfbb to your computer and use it in GitHub Desktop.
Save MurylloEx/63638cf4c386f9d13a471f1794c7dfbb to your computer and use it in GitHub Desktop.
Sample of CRUD Repository and Paging Repository Pattern based on Spring Framework repository abstraction layers (CRUD, PagingAndSorting, Jpa). It was designated to work with Sequelize Typescript.
import { Model } from 'sequelize-typescript';
import { MakeNullishOptional } from 'sequelize/types/utils';
import { DestroyOptions, FindOptions, UpdateOptions } from 'sequelize';
export type RepoPrimaryKey = string | number | bigint;
export type RepoDelete<TAttributes extends object> = DestroyOptions<TAttributes>;
export type RepoUpdate<TAttributes extends object> = UpdateOptions<TAttributes>;
export type RepoFetch<TAttributes extends object> = FindOptions<TAttributes>;
export type RepoCreate<TAttributes extends object> = MakeNullishOptional<TAttributes>;
export interface ICrudRepository<
TModelAttributes extends object,
TCreationAttributes extends object,
TModel extends Model<TModelAttributes, TCreationAttributes>
> {
create(value: RepoCreate<TCreationAttributes>): Promise<TModel>;
fetchOne(options: RepoFetch<TModelAttributes>): Promise<TModel>;
updateOne(options: RepoUpdate<TModelAttributes>, value: TModelAttributes): Promise<TModel>;
deleteOne(options: RepoDelete<TModelAttributes>): Promise<TModel>;
restoreOne(options: RepoFetch<TModelAttributes>): Promise<TModel>;
fetchUnique(identifier: RepoPrimaryKey, options?: RepoFetch<TModelAttributes>): Promise<TModel>;
updateUnique(identifier: RepoPrimaryKey, value: TModelAttributes): Promise<TModel>;
deleteUnique(identifier: RepoPrimaryKey): Promise<TModel>;
restoreUnique(identifier: RepoPrimaryKey): Promise<TModel>;
fetch(options: RepoFetch<TModelAttributes>): Promise<TModel[]>;
update(options: RepoUpdate<TModelAttributes>, value: TModelAttributes): Promise<TModel[]>;
delete(options: RepoDelete<TModelAttributes>): Promise<TModel[]>;
restore(options: RepoFetch<TModelAttributes>): Promise<TModel[]>;
count(options: RepoFetch<TModelAttributes>): Promise<number>;
exists(options: RepoFetch<TModelAttributes>): Promise<boolean>;
}
import { Model } from 'sequelize-typescript';
import { ModelStatic } from 'sequelize';
import {
RepoUpdate,
RepoCreate,
RepoDelete,
RepoFetch,
RepoPrimaryKey,
ICrudRepository,
} from './crud-repository.interface';
export abstract class CrudRepository<
TModelAttributes extends object,
TCreationAttributes extends object,
TModel extends Model<TModelAttributes, TCreationAttributes>
> implements ICrudRepository<TModelAttributes, TCreationAttributes, TModel> {
constructor(protected readonly model: ModelStatic<TModel>) {}
async create(value: RepoCreate<TCreationAttributes>): Promise<TModel> {
return await this.model.create(value);
}
async fetch(options: RepoFetch<TModelAttributes>): Promise<TModel[]> {
return await this.model.findAll(options);
}
async fetchOne(options: RepoFetch<TModelAttributes>): Promise<TModel> {
const entity = await this.model.findOne(options);
if (!entity)
throw new Error('The specified record was not found in the database.');
return entity;
}
async fetchUnique(identifier: RepoPrimaryKey, options?: RepoFetch<TModelAttributes>): Promise<TModel> {
const entity = await this.model.findByPk(identifier, options);
if (!entity)
throw new Error('The specified record was not found in the database.');
return entity;
}
async update(options: RepoUpdate<TModelAttributes>, value: TModelAttributes): Promise<TModel[]> {
await this.model.update(value, options);
return await this.fetch(options);
}
async updateUnique(identifier: RepoPrimaryKey, value: TModelAttributes): Promise<TModel> {
const entity = await this.fetchUnique(identifier);
return await entity.update(value);
}
async updateOne(options: RepoUpdate<TModelAttributes>, value: TModelAttributes): Promise<TModel> {
const entity = await this.fetchOne(options);
return await entity.update(value);
}
async delete(options: RepoDelete<TModelAttributes>): Promise<TModel[]> {
const entities = await this.fetch(options);
await Promise.all(entities.map(entity => entity.destroy()));
return entities;
}
async deleteUnique(identifier: RepoPrimaryKey): Promise<TModel> {
const entity = await this.fetchUnique(identifier);
await entity.destroy();
return entity;
}
async deleteOne(options: RepoDelete<TModelAttributes>): Promise<TModel> {
const entity = await this.fetchOne(options);
await entity.destroy();
return entity;
}
async restore(options: RepoFetch<TModelAttributes>): Promise<TModel[]> {
const entities = await this.fetch(options);
await Promise.all(entities.map(entity => entity.restore(options)));
return entities;
}
async restoreUnique(identifier: RepoPrimaryKey): Promise<TModel> {
const entity = await this.fetchUnique(identifier, { paranoid: false });
await entity.restore();
return entity;
}
async restoreOne(options: RepoFetch<TModelAttributes>): Promise<TModel> {
const entity = await this.fetchOne({ ...options, paranoid: false });
await entity.restore();
return entity;
}
async count(options: RepoFetch<TModelAttributes>): Promise<number> {
return await this.model.count(options);
}
async exists(options: RepoFetch<TModelAttributes>): Promise<boolean> {
return (await this.count(options)) > 0;
}
}
import { Model } from 'sequelize-typescript';
import { ICrudRepository, RepoFetch } from './crud-repository.interface';
export interface IPagingRepository<
TModelAttributes extends object,
TCreationAttributes extends object,
TModel extends Model<TModelAttributes, TCreationAttributes>
> extends ICrudRepository<TModelAttributes, TCreationAttributes, TModel> {
fetchByPage(page: number, pageSize: number): Promise<TModel[]>;
fetchByOffset(offset: number, count: number): Promise<TModel[]>;
fetchPaginated(page: number, pageSize: number, options: RepoFetch<TModelAttributes>): Promise<TModel[]>;
}
import { ModelStatic } from 'sequelize';
import { Model } from 'sequelize-typescript';
import { CrudRepository } from './crud.repository';
import { IPagingRepository, RepoFetch } from './paging-repository.interface';
export abstract class PagingRepository<
TModelAttributes extends object,
TCreationAttributes extends object,
TModel extends Model<TModelAttributes, TCreationAttributes>
> extends CrudRepository<TModelAttributes, TCreationAttributes, TModel>
implements IPagingRepository<TModelAttributes, TCreationAttributes, TModel> {
constructor(protected readonly model: ModelStatic<TModel>) {
super(model);
}
async fetchByOffset(offset: number, count: number): Promise<TModel[]> {
return await super.fetch({
offset,
limit: count,
});
}
async fetchByPage(page: number, pageSize: number): Promise<TModel[]> {
return await super.fetch({
offset: page * pageSize,
limit: pageSize,
});
}
async fetchPaginated(page: number, pageSize: number, options: RepoFetch<TModelAttributes>): Promise<TModel[]> {
return await super.fetch({
...options,
offset: page * pageSize,
limit: pageSize,
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment