Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
TypeGraphQL makes developing GraphQL APIs an enjoyable process, i.e. by defining the schema using only classes and a bit of decorator magic.
So, to create types like object type or input type, we use a kind of DTO classes.For example, to declare Recipe
type we simply create a class and annotate it with decorators:
@ObjectType()
class Recipe {
@Field(type => ID)
id: string;
@Field()
title: string;
@Field(type => [Rate])
ratings: Rate[];
@Field({ nullable: true })
averageRating?: number;
}
And we get the corresponding part of the schema in SDL:
type Recipe {
id: ID!
title: String!
ratings: [Rate!]!
averageRating: Float
}
Then we can create queries, mutations and field resolvers. For this purpose we use controller-like classes that are called "resolvers" by convention. We can also use awesome features like dependency injection and auth guards:
@Resolver(Recipe)
class RecipeResolver {
// dependency injection
constructor(private recipeService: RecipeService) {}
@Query(returns => [Recipe])
recipes() {
return this.recipeService.findAll();
}
@Mutation()
@Authorized(Roles.Admin) // auth guard
removeRecipe(@Arg("id") id: string): boolean {
return this.recipeService.removeById(id);
}
@FieldResolver()
averageRating(@Root() recipe: Recipe) {
return recipe.ratings.reduce((a, b) => a + b, 0) / recipe.ratings.length;
}
}
And in this simple way we get this part of the schema in SDL:
type Query {
recipes: [Recipe!]!
}
type Mutation {
removeRecipe(id: String!): Boolean!
}
We all know that GraphQL is great and solves many problems we have with REST APIs, like overfetching and underfetching. But developing a GraphQL API in Node.js with TypeScript is sometimes a bit of a pain. Why? Let's take a look at the steps we usually have to take.
First, we create all the GraphQL types in schema.gql
using SDL. Then we create our data models using ORM classes, which represent our db entities. Then we start to write resolvers for our queries, mutations and fields, but this forces us to first create TS interfaces for all arguments, inputs, and even object types.
Only then can we actually implement the resolvers using weird generic signatures and manually performing common tasks, like validation, authorization and loading dependencies:
export const getRecipesResolver: GraphQLFieldResolver<void, Context, GetRecipesArgs> = async (
_,
args,
ctx,
) => {
// common tasks repeatable for almost every resolver
const repository = TypeORM.getRepository(Recipe);
const auth = Container.get(AuthService);
await joi.validate(getRecipesSchema, args);
if (!auth.check(ctx.user)) {
throw new NotAuthorizedError();
}
// our business logic, e.g.:
return repository.find({ skip: args.offset, take: args.limit });
};
The biggest problem is redundancy in our codebase, which makes it difficult to keep things in sync. To add a new field to our entity, we have to jump through all the files - modify an entity class, the schema, as well as the interface. The same goes for inputs or arguments. It's easy to forget to update one piece or make a mistake with a single type. Also, what if we've made a typo in field name? The rename feature (F2) won't work correctly.
Tools like GraphQL Code Generator or graphqlgen only solve the first part - they generate the corresponding interfaces (and resolvers skeletons) for our GraphQL schema but they don't fix the schema <--> models redundancy and developer experience (F2 rename won't work, you have to remember about the codegen watch task in background, etc.), as well as common tasks like validation, authorization, etc.
TypeGraphQL comes to address these issues, based on experience from a few years of developing GraphQL APIs in TypeScript. The main idea is to have only one source of truth by defining the schema using classes and some help from decorators. Additional features like dependency injection, validation and auth guards help with common tasks that normally we would have to handle ourselves.
The documentation, installation guide, detailed description of the API and all of its features is available on the website.
A full getting started guide with a simple walkthrough (tutorial) can be found at getting started docs.
If you prefer video tutorials, you can watch Ben Awad's TypeGraphQL video series on YouTube.
You can also check the examples folder in this repository for more examples of usage: simple fields resolvers, DI Container support, TypeORM integration, automatic validation, etc.
The Tests folder might also give you some tips how to get various things done.
The currently released version is a stable 1.0.0 release. It is well tested (95% coverage, 428 test cases) and has most of the planned features already implemented. Plenty of companies and independent developers are using it in production with success.
However, there are also plans for a lot more features like better TypeORM, Prisma and dataloader integration, custom decorators and metadata annotations support - the full list of ideas is available on the GitHub repo. You can also keep track of development's progress on project board.
If you have any interesting feature requests, feel free to open an issue on GitHub so we can discuss that!
TypeGraphQL is an MIT-licensed open source project. This framework is a result of the tremendous amount of work - sleepless nights, busy evenings and weekends.
It doesn't have a large company that sits behind - its ongoing development is possible only thanks to the support by the community.
BlueReceipt | ECAD Labs |
Please ask your company to support this open source project by becoming a gold sponsor and getting a premium technical support from our core contributors.
Gorrion Software House | Chums |
Live Graphic Systems | LifeX Aps | SwissMentor |
Want to file a bug, contribute some code, or improve documentation? Great! Please read ourguidelines for contributing and then check out one of our help wanted issues.
在看些文章时不经意看到智联团的的GraphQL使用的技术沉淀。拿出来给大家分享。 此文是作者考虑 GraphQL 在 Node.js 架构中的落地方案后所得。从最初考虑可以(以内置中间件)加入基础服务并提供完整的构建、发布、监控支持,到最终选择不改动基础服务以提供独立包适配,不限制实现技术选型,交由业务团队自由选择的轻量方式落地。中间经历了解除误解,对收益疑惑,对最初定位疑惑,最终完成利弊权衡的过
TYPE key 返回 key 所储存的值的类型。 可用版本: >= 1.0.0 时间复杂度: O(1) 返回值: none (key不存在) string (字符串) list (列表) set (集合) zset (有序集) hash (哈希表) # 字符串 redis> SET weather "sunny" OK redis> TYPE weather string # 列表 r
描述: 记录一个对象的类型。 语法 @type {typeName} 概述 @type标签允许你提供一个表达式,用于标识一个标识符可能包含的值的类型,或由函数返回值的类型。您还可以将其包含与其他JSDoc标签类型的表达式结合使用,如@param 标签。 类型表达式可以包括标识符的namepath(例如,myNamespace.MyClass);一个内置的JavaScript类型(例如,string
Type: Minimal and Clean Free Jekyll Theme Configurations Deployment Posts Pages Navigation Disqus Comments Social Media Links Update favicon Configurations Type theme comes with different customizatio
深入的语言特性 本文所讲的是一个高阶主题,能帮你更好地理解一些边缘情况。 这仅是锦上添花。许多经验丰富的的开发者不甚了了也过得不错。如果你想了解代码运行的本质,那就继续读下去吧。 一个动态执行的方法调用可能会丢失 this。 例如: let user = { name: "John", hi() { alert(this.name); }, bye() { alert("Bye");
TYPE key 返回 key 所储存的值的类型。 可用版本: >= 1.0.0 时间复杂度: O(1) 返回值: none (key不存在)string (字符串)list (列表)set (集合)zset (有序集)hash (哈希表) # 字符串 redis> SET weather "sunny" OK redis> TYPE weather string # 列表 redis>
描述 (Description) java.lang.reflect.Method.getGenericReturnType()方法返回一个Type对象,该对象表示此Method对象表示的方法的正式返回类型。 声明 (Declaration) 以下是java.lang.reflect.Method.getGenericReturnType()方法的声明。 public Type getGeneri