Last active
April 30, 2023 20:39
-
-
Save lilianalillyy/56b81a010fe52aa535544b4254fb7f7d to your computer and use it in GitHub Desktop.
TL;DR: Repositories & Prisma minus the boilerplate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PrismaModule } from "@infra/prisma/PrismaModule"; | |
import { Module } from "@nestjs/common"; | |
import { UserRepository } from "./UserRepository"; | |
import { PrismaRepository } from "@infra/prisma/PrismaRepository"; | |
@Module({ | |
imports: [ | |
PrismaModule.forRoot( | |
UserRepository, | |
"user", // base repository, can be injected with @InjectRepository("user") | |
{ | |
modelName: "user", | |
provide: "key", // optional, if set, use @Inject("key"), otherwise you can use @InjectRepository("user") | |
useClass: PrismaRepository // base repository | |
// useClass: UserRepository, | |
// useValue: new UserRepository(...), | |
// useFactory: (prisma: PrismaService) => new UserRepository(prisma) | |
} | |
), | |
], | |
exports: [PrismaModule], | |
}) | |
export class UserModule { | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PrismaRepository } from "@infra/prisma/PrismaRepository"; | |
import { Injectable } from "@nestjs/common"; | |
import { PrismaService } from "nestjs-prisma"; | |
@Injectable() | |
export class UserRepository extends PrismaRepository<"user"> { | |
constructor(prisma: PrismaService) { | |
super("user", prisma) | |
} | |
// find*, count, group, etc.. from prisma.user is now available | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DynamicModule, ExistingProvider, Inject, InjectionToken, Module, Provider } from '@nestjs/common'; | |
import { PrismaModule as BasePrismaModule, PrismaService } from 'nestjs-prisma'; | |
import { PrismaRepository } from './PrismaRepository'; | |
import { ForModuleCustomEntry, ForModuleEntry, PrismaModelName, PrismaRepositoryType } from './types'; | |
@Module({ | |
imports: [BasePrismaModule], | |
}) | |
export class PrismaModule { | |
public static forRoot<N extends PrismaModelName>( | |
...entries: (ForModuleEntry<N> | ForModuleEntry<N>[])[] | |
): DynamicModule { | |
const providers = (Array.isArray(entries[0]) ? entries[0] : entries).map(provider => { | |
if (typeof provider === 'string') { | |
return this.createRepositoryProvider(provider); | |
} | |
if (Array.isArray(provider)) { | |
throw new Error('Cannot pass multiple arrays in PrismaModule.forRoot()'); | |
} | |
return this.createCustomRepositoryProvider(provider); | |
}); | |
return { | |
module: PrismaModule, | |
providers: providers, | |
exports: providers, | |
}; | |
} | |
public static createRepositoryProvider<N extends PrismaModelName>( | |
modelName: N | |
): Provider<PrismaRepositoryType<N>> { | |
return { | |
provide: this.createRepositoryProviderName(modelName), | |
useFactory: (prisma: PrismaService) => new PrismaRepository(modelName, prisma), | |
}; | |
} | |
public static createCustomRepositoryProvider<N extends PrismaModelName>( | |
entry: ForModuleCustomEntry<N> | |
): Provider<typeof entry extends ExistingProvider<infer T> ? T : PrismaRepositoryType<N>> { | |
if (typeof entry === 'function') { | |
return { | |
provide: entry, | |
useClass: entry, | |
}; | |
} | |
const provide: InjectionToken = !('provide' in entry) | |
? this.createRepositoryProviderName(entry.modelName) | |
: entry.provide; | |
return { | |
...entry, | |
provide, | |
}; | |
} | |
public static createRepositoryProviderName<N extends PrismaModelName>(modelName: N): `PrismaRepository__${N}` { | |
return `PrismaRepository__${modelName}`; | |
} | |
} | |
// Only used when basic PrismaRepository is needed or when the repository entry is passed as ExistingProvider<Repository>/Type<Repository> | |
export function InjectRepository<N extends PrismaModelName>(modelName: N) { | |
return Inject(PrismaModule.createRepositoryProviderName<N>(modelName)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DynamicModule, ExistingProvider, Inject, InjectionToken, Module, Provider } from '@nestjs/common'; | |
import { PrismaModule as BasePrismaModule, PrismaService } from 'nestjs-prisma'; | |
import { PrismaRepository } from './PrismaRepository'; | |
import { ForModuleCustomEntry, ForModuleEntry, PrismaModelName, PrismaRepositoryType } from './types'; | |
@Module({ | |
imports: [BasePrismaModule], | |
}) | |
export class PrismaModule { | |
public static forRoot<N extends PrismaModelName>( | |
...entries: (ForModuleEntry<N> | ForModuleEntry<N>[])[] | |
): DynamicModule { | |
const providers = (Array.isArray(entries[0]) ? entries[0] : entries).map(provider => { | |
if (typeof provider === 'string') { | |
return this.createRepositoryProvider(provider); | |
} | |
if (Array.isArray(provider)) { | |
throw new Error('Cannot pass multiple arrays in PrismaModule.forRoot()'); | |
} | |
return this.createCustomRepositoryProvider(provider); | |
}); | |
return { | |
module: PrismaModule, | |
providers: providers, | |
exports: providers, | |
}; | |
} | |
public static createRepositoryProvider<N extends PrismaModelName>( | |
modelName: N | |
): Provider<PrismaRepositoryType<N>> { | |
return { | |
provide: this.createRepositoryProviderName(modelName), | |
useFactory: (prisma: PrismaService) => new PrismaRepository(modelName, prisma), | |
}; | |
} | |
public static createCustomRepositoryProvider<N extends PrismaModelName>( | |
entry: ForModuleCustomEntry<N> | |
): Provider<typeof entry extends ExistingProvider<infer T> ? T : PrismaRepositoryType<N>> { | |
if (typeof entry === 'function') { | |
return { | |
provide: entry.name, | |
useClass: entry, | |
}; | |
} | |
const provide: InjectionToken = !('provide' in entry) | |
? this.createRepositoryProviderName(entry.modelName) | |
: entry.provide; | |
return { | |
...entry, | |
provide, | |
}; | |
} | |
public static createRepositoryProviderName<N extends PrismaModelName>(modelName: N): `PrismaRepository__${N}` { | |
return `PrismaRepository__${modelName}`; | |
} | |
} | |
export function InjectRepository<N extends PrismaModelName>(modelName: N) { | |
return Inject(PrismaModule.createRepositoryProviderName<N>(modelName)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ExistingProvider, InjectionToken, Provider, Type } from '@nestjs/common'; | |
import { PrismaClient } from '@prisma/client'; | |
import { PrismaRepository } from './PrismaRepository'; | |
export type KeysMatching<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T]; | |
export type PickKeysMatching<T, V> = { | |
[key in KeysMatching<T, V>]: V; | |
}; | |
export type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never; | |
export interface CustomRepositoryProviderOptionsBase<N extends PrismaModelName = PrismaModelName> { | |
name: N; | |
provide?: InjectionToken; | |
} | |
export type CustomRepositoryProviderOptions<N extends PrismaModelName = PrismaModelName> = | |
| (DistributiveOmit< | |
Exclude<Provider<PrismaRepositoryType<N>>, Type | ExistingProvider> | ExistingProvider<PrismaRepository<N>>, | |
'provide' | |
> & | |
CustomRepositoryProviderOptionsBase<N>) | |
| Type<PrismaRepository<N>>; | |
export type ForModuleCustomEntry<N extends PrismaModelName> = CustomRepositoryProviderOptions<N>; | |
export type ForModuleEntry<N extends PrismaModelName> = N | ForModuleCustomEntry<N>; | |
export type PrismaRepositoryType<N extends PrismaModelName = PrismaModelName> = PrismaDelegate<N> & { | |
modelName: N; | |
prisma: PrismaClient; | |
new <N extends PrismaModelName>(modelName: N, prisma: PrismaClient): PrismaRepositoryType<N>; | |
}; | |
export type PrismaModelName<M = any> = KeysMatching< | |
PrismaClient, | |
{ create(args: { data: any }): ReturnType<Prisma.PrismaPromise<M>['then']> } | |
>; | |
export type PrismaDelegate<N extends PrismaModelName> = PrismaClient[N]; | |
// Args meaning the 'args' argument of actions | |
export type PrismaDelegateFuncArgs<N extends PrismaModelName, F extends keyof PrismaDelegate<N>> = Parameters< | |
PrismaDelegate<N>[F] | |
>[0]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment