TypeGraphQL v1.0
8月19日,发布了TypeGraphQL框架,该框架简化了在Typescript中使用GraphQL的工作。在两年半的时间里,该项目获得了数家公司的扎实社区和支持,并有信心获得普及。经过650次提交后,他在github上拥有5,000多颗星星和400个叉子,这是波兰开发人员Michal Litek辛勤工作的成果。在1.0版中,性能得到了显着改善,架构得到了隔离,摆脱了以前的冗余,出现了两个主要功能-指令和扩展,该框架与GraphQL完全兼容。
这个框架是做什么用的?
Michal提到他在裸GraphQL方面的经验,由于修改现有代码的冗余性和复杂性,将开发过程称为“痛苦的”:
听起来不太实用,使用这种方法,主要的问题是代码的冗余,这使得在编写代码时难以同步所有参数,并在更改时增加了风险。要向我们的实体添加新字段,我们必须遍历所有文件:更改实体类,然后更改架构部分和接口。输入数据或参数是相同的,很容易忘记更新一个元素或在一种类型中犯错误。
为了消除冗余并使所有这些体力劳动自动化,创建了TypeGraphQL。它基于将所有信息存储在一个地方的想法,通过类和装饰器描述数据模式。该框架还承担依赖项注入,数据验证和授权的手动工作,从而减轻了开发人员的负担。
工作原理
让我们以使用配方数据库的GraphQL API为例查看TypeGraphQL的工作方式。
这是SDL中的架构:
type Recipe {
id: ID!
title: String!
description: String
creationDate: Date!
ingredients: [String!]!
}
让我们将其重写为Recipe类:
class Recipe {
id: string;
title: string;
description?: string;
creationDate: Date;
ingredients: string[];
}
让我们为类和属性配备装饰器:
@ObjectType()
class Recipe {
@Field(type => ID)
id: string;
@Field()
title: string;
@Field({ nullable: true })
description?: string;
@Field()
creationDate: Date;
@Field(type => [String])
ingredients: string[];
}
在文档 的相应部分中描述字段和类型的详细规则,
然后我们将描述通常的CRUD查询和变异。为此,创建一个RecipeResolver控制器,并将RecipeService传递给构造函数:
@Resolver(Recipe)
class RecipeResolver {
constructor(private recipeService: RecipeService) {}
@Query(returns => Recipe)
async recipe(@Arg("id") id: string) {
const recipe = await this.recipeService.findById(id);
if (recipe === undefined) {
throw new RecipeNotFoundError(id);
}
return recipe;
}
@Query(returns => [Recipe])
recipes(@Args() { skip, take }: RecipesArgs) {
return this.recipeService.findAll({ skip, take });
}
@Mutation(returns => Recipe)
@Authorized()
addRecipe(
@Arg("newRecipeData") newRecipeData: NewRecipeInput,
@Ctx("user") user: User,
): Promise<Recipe> {
return this.recipeService.addNew({ data: newRecipeData, user });
}
@Mutation(returns => Boolean)
@Authorized(Roles.Admin)
async removeRecipe(@Arg("id") id: string) {
try {
await this.recipeService.removeById(id);
return true;
} catch {
return false;
}
}
}
在这里,@Authorized()装饰器用于限制对未授权(或特权不足)用户的访问。您可以在文档中阅读有关授权的更多信息。
是时候添加NewRecipeInput和RecipesArgs了:
@InputType()
class NewRecipeInput {
@Field()
@MaxLength(30)
title: string;
@Field({ nullable: true })
@Length(30, 255)
description?: string;
@Field(type => [String])
@ArrayMaxSize(30)
ingredients: string[];
}
@ArgsType()
class RecipesArgs {
@Field(type => Int, { nullable: true })
@Min(0)
skip: number = 0;
@Field(type => Int, { nullable: true })
@Min(1) @Max(50)
take: number = 25;
}
长度, 敏和@ArrayMaxSize是来自Validator类的装饰器,它们会自动执行字段验证。
最后一步实际上是组装电路。这是通过buildSchema函数完成的:
const schema = await buildSchema({
resolvers: [RecipeResolver]
});
就是这样!现在,我们有了一个可以正常工作的GraphQL模式。在编译形式下,它看起来像这样:
type Recipe {
id: ID!
title: String!
description: String
creationDate: Date!
ingredients: [String!]!
}
input NewRecipeInput {
title: String!
description: String
ingredients: [String!]!
}
type Query {
recipe(id: ID!): Recipe
recipes(skip: Int, take: Int): [Recipe!]!
}
type Mutation {
addRecipe(newRecipeData: NewRecipeInput!): Recipe!
removeRecipe(id: ID!): Boolean!
}
这是基本功能的示例,实际上,TypeGraphQL可以使用TS军械库中的许多其他工具。您已经看到了文档的链接:)
1.0新增功能
让我们快速浏览一下该发行版中的主要更改:
性能
TypeGraphQL本质上是对graphql-js库的抽象的附加层,并且它将始终比它慢。但是现在,与0.17版相比,在25,000个嵌套对象的样本上,该框架增加了30倍的开销-从500%降低到17%,并有可能加速到13%。文档中介绍了一些非平凡的优化方法。
隔离电路
在旧版本中,该架构是根据从装饰器获得的所有元数据构建的。每次对buildSchema的后续调用都返回相同的模式,该模式是从存储中所有可用的元数据构建的。现在,隔离了方案,并且buildSchema仅发出与给定参数直接相关的那些请求。也就是说,通过仅更改resolvers参数,我们将对GraphQL模式执行不同的操作。
指令和扩展
有两种方法可以将元数据添加到架构元素:GraphQL指令是SDL的一部分,可以在架构中直接声明。他们还可以更改它并执行特定的操作,例如,生成用于分页的连接类型。它们是使用@Directive和@Extensions装饰器应用的,它们在架构构造方法上有所不同。文档指令,扩展文档。
接口字段的转换器和参数
与GraphQL完全兼容的最后一个领域就在这里。现在,您可以使用@ObjectType语法为接口字段定义转换器:
@InterfaceType()
abstract class IPerson {
@Field()
avatar(@Arg("size") size: number): string {
return `http://i.pravatar.cc/${size}`;
}
}
这里 描述了一些例外情况。
转换嵌套的输入和数组
在以前的版本中,仅在第一个嵌套级别创建输入类的实例。这会在验证过程中产生问题和错误。固定。
结论
在整个开发期间,该项目一直对思想和批评,开源和煽动性开放。99%的代码是由Michal Litek本人编写的,但是社区也为TypeGraphQL的发展做出了巨大贡献。现在,随着越来越受欢迎和财政支持,它可以成为该领域的真正标准。Michal的Github Doki Twitter
网站