TL;DR — Apollo Server & Typescript ⇒ graphqlgen At Meeshkan, we were writing our own typescript interfaces for JS object representations of our GraphQL schema, which proved to be error-prone and laborious. The new package by makes that crazy easy, but if you are not comfortable with GraphQL schemas yet, it can feel like another layer of incomprehension in an already incomprehensible stack. I wanted to take a moment to share a few key learnings from working with this new tool in case you are struggling to get started: [graphqlgen](https://github.com/prisma/graphqlgen) Prisma How resolvers in Apollo Server actually work. Why is awesome. graphqlgen Why you should use prisma types for your server’s schema’s types. never Bonus: How to unit your test resolvers. How resolvers work in Apollo Server Before you can work with effectively, you need to understand the basics of how resolvers work in Apollo Server. graphqlgen The main thing that helped me in grocking resolvers was to learn how they cascade, not unlike reducers. The from Apollo Server is a helpful start, but I couldn’t quite figure out how everything tied together I concocted a bad example showing the problem they’re trying to solve. redux documentation Let’s assume that your schema is: type User {id: ID! @uniqueposts: [Post!]!} type Post {id: ID! @uniqueauthor: User!} type Query {user(id): User} One (bad) way you could write your resolvers is like this: export const Query: MyQueryResolverType = {user: async (_, { id }, __) => {const user = await getUserFromDatabase(id); // fetch userconst posts = await getUserPostsFromDatabase(id); // fetch postsreturn {...user,posts,}}} export const User: MyUserResolverType {id: ({ id }, _, __) => id,posts: ({ posts }, _, __) => posts,} export const Post: MyPostResolverType {id: ( { id }, _, __) => id,author: ({ author }, _, __) => author,} There are numerous problems with the code above: we have had to write our own types for resolvers like , and . will take care of these for us, which we’ll see in a bit, but for now we’re stuck with that fragility. MyQueryResolverType MyUserResolverType MyPostResolverType graphqlgen The main problem for this section is that the query is doing both too little and too much work. We have no clue if the incoming graphql query is going to even ask for the posts, so why would we fetch them here? Furthermore, if the person does something like: user {user(id) {posts {author {posts {author {id}}}}}} …this will break, as it won’t recurse down the tree. So let’s fix it with proper resolvers. export const Query: MyQueryResolverType = {user: (_, { id }, __) => getUserFromDatabase(id); // a promise} export const User: MyUserResolverType {id: ({ id }, _, __) => id,posts: ({ id }, _, __) => getPostsFromUser(id), // a promise} export const Post: MyPostResolverType {id: ({ id }, _, __) => id,author: ({ id }, _, __) => getUserFromPost(id); // a promise} And no more breakage. ApolloServer traverses down the tree, calling the correct resolver as needed. This is because the first argument passed in to the resolver function is the representation of the object returned from the previous resolver, which we use to get important information about the object like the . In the ApolloServer documentation, this object is called , which I think is a poor choice for a name. I would opt for or or . id parent partial_obj obj_hint obj_with_stuff_missing Why is awesome graphqlgen The “good” example above has (at least) two pesky anti-patterns: the Resolver type needs to be written by hand, and there are functions like that are basically identity functions. fixes this. It will automatically generate Typescript interfaces for resolver types default resolvers for resolvers. Goodbye boilerplate! Post.id graphqlgen and To use , simply include a file in your root directory, and then somewhere in your build to generate the goods. The file has, at the time of this writing, six keys that we use, all of which are pretty self-explanatory: graphqlgen graphqlgen.yml yarn add --dev graphqlgen npx graphqlgen graphqlgen.yml # The target programming language for the generated codelanguage: typescript # The file path pointing to your GraphQL schemaschema: ./src/schema.graphql # Type definition for the resolver context object# The context object is passed into ApolloServer.# It usually contains stuff like a database connection# or user id or whatever.context: ./src/types.ts:IContext # Find TS models that correspond to the graphql schema and map them# NOTE: this part is tricky and is the subject of the next section,# so read on to find out more.models:files:- path: path/to/my/resolver/types.tsdefaultName: 'I${typeName}' # my convention- path: path/to/my/generated/prisma/typesdefaultName: '${typeName}Node' # prisma convention # Where all of this boilerplate code is writtenoutput: ./src/generated/graphqlgen.ts # In case you need inspiration for your resolvers, this generates# stubs that you can fill inresolver-scaffolding:output: ./src/generated/tmp-resolvers/layout: file-per-type # one file per type in the Now, if we revisit our previous example, things get a whole lot easier to follow: export const Query: QueryResolvers.Type = {user: (_, { id }, __) => getUserFromDatabase(id); // a promise} export const User: UserResolvers.Type {...UserResolvers.defaultResolvers,posts: ({ id }, _, __) => getPostsFromUser(id), // a promise} export const Post: PostResolvers.Type {...PostResolvers.defaultResolvers,author: ({ id }, _, __) => getUserFromPost(id); // a promise} All of the resolver types are hanging out in , and we no longer need to roll our own. Furthermore, default resolvers are generated for every type, so we no longer need to write all that boilerplate. Sweet! src/generated/graphqlgen Why you should use prisma types for your server’s schema’s types never Prisma is fantastically efficient. So efficient that you will just want to ping your database from your client using GraphQL and dispense of Apollo Server entirely. This is tantamount to reading and writing to a database directly from a web page which is, in short, a very bad idea. Now that is around, it is even more expedient to create an Apollo Server in record pace, and in doing so, you may be tempted to use the interfaces generated by Prisma as the interfaces representing types in your schema. This is also a bad idea. For example, let’s imagine that your prisma contains: graphqlgen datamodel.prisma type UserNode {id: ID! @uniqueemail: String!internalFieldUsedByDevs: String!anotherInternalField: String!} whereas our contains: schema.graphql type IUserNode {id: ID! @uniqueemail: String} If you do not want and to be reachable by your client, you probably do not want the same Typescript type as prisma generates. The more sensible alternative would be to write your own type and use that. No problem, except how does know how to use yours and not prisma’s? Let’s go back to . internalFieldUsedByDevs anotherInternalField graphqlgen graphqlgen.yml models:files:- path: path/to/my/resolver/types.tsdefaultName: 'I${typeName}' # my convention- path: path/to/my/generated/prisma/typesdefaultName: '${typeName}Node' # prisma convention Here, . will first use the types in and then try to get whatever it does not find from the generated prisma types. That way, will correctly be documented and type-checked as the that we wrote and not generated by prisma. order matters graphqlgen types.ts Query.me IUserNode UserNode While this seems trivial at first, as your server schema and prisma schema start to diverge, you will wind up in situations where your server schema is not a strict subset of your prisma schema. Thus, separating the two will become essential, and it’s better to get in the habit of enforcing that early. Bonus: How to unit test your resolvers Doesn’t really have anything to do with , but hey, since we’re talking about writing resolvers, let’s not forget to test them! graphqlgen import * as request from "supertest";import app from "../../path/to/my/express/app"; const ENDPOINT = "/graphql"; test("test hello", async () => {const { text } = await request(app).post(ENDPOINT).send({query: "{ hello }"});expect(JSON.parse(text).data.hello).toBe("Hello!");}); Underwhelming, I know, but the cleanest things often are. As I couldn’t really find anything online that stated this this succinctly, I thought I’d put it here. This assumes that you have set up an express app in a separate file from your server so that you don’t have to start and stop the server for each test, which can lead to all sorts of wacky port-related errors. We’re using Jest at Meeshkan, but mocha or really anything is fine. At some point, you’ll need to mock your database communication object (in our case, the instance generated by ), which is pretty straightforward on any of these platforms. prisma prisma generate So there you have it! Almost-painless type checking, default resolver scaffolding and unit testing. w00t!