GraphQL + Typescript =爱。TypeGraphQL v1.0





TypeGraphQL v1.0



8月19日,发布了TypeGraphQL框架,该框架简化了在Typescript中使用GraphQL的工作。在两年半的时间里,该项目获得了数家公司的扎实社区和支持,并有信心获得普及。经过650次提交后,他在github上拥有5,000多颗星星和400个叉子,这是波兰开发人员Michal Litek辛勤工作的成果。在1.0版中,性能得到了显着改善,架构得到了隔离,摆脱了以前的冗余,出现了两个主要功能-指令和扩展,该框架与GraphQL完全兼容。



这个框架是做什么用的?



Michal提到他在裸GraphQL方面的经验,由于修改现有代码的冗余性和复杂性,将开发过程称为“痛苦的”:



  1. GraphQL SDL (Schema Definition Language);
  2. ORM (Object-Relational Mapping), ;
  3. , , ...
  4. , .
  5. , , .




听起来不太实用,使用这种方法,主要的问题是代码的冗余,这使得在编写代码时难以同步所有参数,并在更改时增加了风险。要向我们的实体添加新字段,我们必须遍历所有文件:更改实体类,然后更改架构部分和接口。输入数据或参数是相同的,很容易忘记更新一个元素或在一种类型中犯错误。



为了消除冗余并使所有这些体力劳动自动化,创建了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



网站












All Articles