如何使用 Type-GraphQL、TypeORM 和依赖注入实现 Resolver-Inheritance
How to implement Resolver-Inheritance with Type-GraphQL, TypeORM, and dependency injection
我正在尝试扩展 Type-GraphQL 提供的解析器继承示例,除了用 TypeORM 存储库替换静态数据。
以下是 PersonResolver 如何扩展 ResourceResolver 以及它如何将 persons
数组作为 ResourceResolver 构造函数的第二个参数传递。
const persons: Person[] = [
{
id: 1,
name: "Person 1",
age: 23,
role: PersonRole.Normal,
},
{
id: 2,
name: "Person 2",
age: 48,
role: PersonRole.Admin,
},
];
@Resolver()
export class PersonResolver extends ResourceResolver(Person, persons) {
...
}
ResourceResolver 内部
export function ResourceResolver<TResource extends Resource>(
ResourceCls: ClassType<TResource>,
resources: TResource[],
) {
const resourceName = ResourceCls.name.toLocaleLowerCase();
// `isAbstract` decorator option is mandatory to prevent multiple registering in schema
@Resolver(_of => ResourceCls, { isAbstract: true })
@Service()
abstract class ResourceResolverClass {
protected resourceService: ResourceService<TResource>;
constructor(factory: ResourceServiceFactory) {
this.resourceService = factory.create(resources);
}
...
}
而在 ResourceServiceFactory
@Service()
export class ResourceServiceFactory {
create<TResource extends Resource>(resources?: TResource[]) {
return new ResourceService(resources);
}
}
export class ResourceService<TResource extends Resource> {
constructor(protected resources: TResource[] = []) {}
getOne(id: number): TResource | undefined {
return this.resources.find(res => res.id === id);
}
我想知道实现 ResourceResolver 的最佳方法,但我想传递来自 TypeORM 的存储库而不是静态数据。
这是原始示例 - https://github.com/MichalLytek/type-graphql/tree/master/examples/resolvers-inheritance。
非常感谢任何帮助或建议。
我相信你必须在你的解析器函数中做一些像这样时髦的事情:
function createBaseResolver<T extends BaseEntity>(suffix: string, objectTypeCls: T) {
@Resolver({ isAbstract: true })
abstract class BaseResolver {
@Query(type => [objectTypeCls], { name: `getAll${suffix}` })
async getA(@Arg("id", type => Int) id: number): Promise<T> {
let beCastedObj = (<typeof BaseEntity> objectTypeCls.constructor); // https://github.com/Microsoft/TypeScript/issues/5677
return beCastedObj.findOne({ where: { id:id } }) as Promise<T>;
}
}
return BaseResolver;
}
在另一个文件中...
const PersonBaseResolver = createBaseResolver("person", Person);
@Resolver(of => Person)
export class PersonResolver extends PersonBaseResolver {
// ...
}
编辑:OP 在评论中询问他是否认为这是一个好的模式。我才刚刚开始学习这个,所以不要把我的话当作福音。然而。我在进行下一步时遇到了麻烦:定义自定义参数。如果您为 @Args() 使用自定义 classes(推荐使用 auto-validate),那么 typescript/typegraphql 在编译时需要该信息。如果这些参数不会在任何 children 中发生变化,您可以保留 parent 的解析器 as-is,但如果它们发生变化,您需要一种方法来传递自定义参数。
我通过将解析器装饰器移动到 children.
来完成此操作
示例Parent:
abstract class BaseUserCreatedEntityResolver {
async get(args: any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let beCastedObj = (<typeof UserCreatedEntity>objectTypeCls.constructor);
args = Object.assign(args, { userCreator: ctx.req.session.userId })
let a = beCastedObj.findOne({ where: args }) as any;
return a;
}
async getAll(args: any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let beCastedObj = (<typeof UserCreatedEntity>objectTypeCls.constructor);
args = Object.assign(args, { userCreator: ctx.req.session.userId });
beCastedObj.create(args);
return beCastedObj.find({ where: args }) as any;
}
async add(args:any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let beCastedObj = (<typeof UserCreatedEntity>objectTypeCls.constructor);
args = Object.assign(args, { userCreator: ctx.req.session.userId });
let entity = await beCastedObj.create(args)[0];
await entity.save();
return entity as any;
}
async delete(args:any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let entity = await this.get(args,ctx);
await entity.remove();
return new Promise(()=>true);
}
async update(args:any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let entity = await this.get(args,ctx);
delete args['userCreator'];// this should've been filtered out in child param definition, but adding it here just in case
Object.assign(entity,args);
await entity.save();
return entity;
}
checkForLogin(ctx:any){
if(!ctx.req.session.userId) throw new Error("User not logged in");
}
}
示例Child:
@ArgsType()
class GetAllArgs {
@Field()
date:Date;
}
//...
@Query(() => Entity)
async getAllEntitiesName(@Args() args :GetAllArgs, @Ctx() ctx: any) {
return super.get(args,ctx);
}
我很喜欢这种范式。如果我有一个函数在所有 children 解析器中都不会改变,我将创建该函数并在 parent 中修饰它。出于这个原因,我将 parent 作为可自定义的 function-class,而不仅仅是基本的 class。
我正在尝试扩展 Type-GraphQL 提供的解析器继承示例,除了用 TypeORM 存储库替换静态数据。
以下是 PersonResolver 如何扩展 ResourceResolver 以及它如何将 persons
数组作为 ResourceResolver 构造函数的第二个参数传递。
const persons: Person[] = [
{
id: 1,
name: "Person 1",
age: 23,
role: PersonRole.Normal,
},
{
id: 2,
name: "Person 2",
age: 48,
role: PersonRole.Admin,
},
];
@Resolver()
export class PersonResolver extends ResourceResolver(Person, persons) {
...
}
ResourceResolver 内部
export function ResourceResolver<TResource extends Resource>(
ResourceCls: ClassType<TResource>,
resources: TResource[],
) {
const resourceName = ResourceCls.name.toLocaleLowerCase();
// `isAbstract` decorator option is mandatory to prevent multiple registering in schema
@Resolver(_of => ResourceCls, { isAbstract: true })
@Service()
abstract class ResourceResolverClass {
protected resourceService: ResourceService<TResource>;
constructor(factory: ResourceServiceFactory) {
this.resourceService = factory.create(resources);
}
...
}
而在 ResourceServiceFactory
@Service()
export class ResourceServiceFactory {
create<TResource extends Resource>(resources?: TResource[]) {
return new ResourceService(resources);
}
}
export class ResourceService<TResource extends Resource> {
constructor(protected resources: TResource[] = []) {}
getOne(id: number): TResource | undefined {
return this.resources.find(res => res.id === id);
}
我想知道实现 ResourceResolver 的最佳方法,但我想传递来自 TypeORM 的存储库而不是静态数据。
这是原始示例 - https://github.com/MichalLytek/type-graphql/tree/master/examples/resolvers-inheritance。
非常感谢任何帮助或建议。
我相信你必须在你的解析器函数中做一些像这样时髦的事情:
function createBaseResolver<T extends BaseEntity>(suffix: string, objectTypeCls: T) {
@Resolver({ isAbstract: true })
abstract class BaseResolver {
@Query(type => [objectTypeCls], { name: `getAll${suffix}` })
async getA(@Arg("id", type => Int) id: number): Promise<T> {
let beCastedObj = (<typeof BaseEntity> objectTypeCls.constructor); // https://github.com/Microsoft/TypeScript/issues/5677
return beCastedObj.findOne({ where: { id:id } }) as Promise<T>;
}
}
return BaseResolver;
}
在另一个文件中...
const PersonBaseResolver = createBaseResolver("person", Person);
@Resolver(of => Person)
export class PersonResolver extends PersonBaseResolver {
// ...
}
编辑:OP 在评论中询问他是否认为这是一个好的模式。我才刚刚开始学习这个,所以不要把我的话当作福音。然而。我在进行下一步时遇到了麻烦:定义自定义参数。如果您为 @Args() 使用自定义 classes(推荐使用 auto-validate),那么 typescript/typegraphql 在编译时需要该信息。如果这些参数不会在任何 children 中发生变化,您可以保留 parent 的解析器 as-is,但如果它们发生变化,您需要一种方法来传递自定义参数。
我通过将解析器装饰器移动到 children.
来完成此操作示例Parent:
abstract class BaseUserCreatedEntityResolver {
async get(args: any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let beCastedObj = (<typeof UserCreatedEntity>objectTypeCls.constructor);
args = Object.assign(args, { userCreator: ctx.req.session.userId })
let a = beCastedObj.findOne({ where: args }) as any;
return a;
}
async getAll(args: any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let beCastedObj = (<typeof UserCreatedEntity>objectTypeCls.constructor);
args = Object.assign(args, { userCreator: ctx.req.session.userId });
beCastedObj.create(args);
return beCastedObj.find({ where: args }) as any;
}
async add(args:any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let beCastedObj = (<typeof UserCreatedEntity>objectTypeCls.constructor);
args = Object.assign(args, { userCreator: ctx.req.session.userId });
let entity = await beCastedObj.create(args)[0];
await entity.save();
return entity as any;
}
async delete(args:any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let entity = await this.get(args,ctx);
await entity.remove();
return new Promise(()=>true);
}
async update(args:any, ctx: any): Promise<T> {
this.checkForLogin(ctx);
let entity = await this.get(args,ctx);
delete args['userCreator'];// this should've been filtered out in child param definition, but adding it here just in case
Object.assign(entity,args);
await entity.save();
return entity;
}
checkForLogin(ctx:any){
if(!ctx.req.session.userId) throw new Error("User not logged in");
}
}
示例Child:
@ArgsType()
class GetAllArgs {
@Field()
date:Date;
}
//...
@Query(() => Entity)
async getAllEntitiesName(@Args() args :GetAllArgs, @Ctx() ctx: any) {
return super.get(args,ctx);
}
我很喜欢这种范式。如果我有一个函数在所有 children 解析器中都不会改变,我将创建该函数并在 parent 中修饰它。出于这个原因,我将 parent 作为可自定义的 function-class,而不仅仅是基本的 class。