Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building type-safe GraphQL servers

Building type-safe GraphQL servers

Introduces `graphql-nexus` by going through what happened so far in the GraphQL community. What problems have we been trying to solve? What problems have we created by trying to solve these problems in the first place? And based on that, what makes us think that `graphql-nexus` might solve all these problems once and for all

Avatar for Flavian Desverne

Flavian Desverne

December 06, 2018

Other Decks in Technology

Transcript

  1. Agenda 2 graphql-nexus Intro to + Demo 3 Type-safe DB

    access with Prisma 1 What happened so far?
  2. graphql-js R esolver-first type User { id: ID! name: String!

    posts: [Post!]! } type Post { id: ID! title: String! } const User = new GraphQLObjectType({ name: “User”, fields: () => ({ id: { type: new GraphQLNonNull(GraphQLID) }, name: { type: new GraphQLNonNull(GraphQLString) }, posts: { type: new GraphQLNonNull( new GraphQLList( new GraphQLNonNull(Post) ) ) } }) }) const Post = new GraphQLObjectType({ name: “Post”, fields: () => ({ id: { type: new GraphQLNonNull(GraphQLID) }, title: { type: new GraphQLNonNull(GraphQLString) } }) }) const User = new GraphQLObjectType({ name: “User”, fields: () => ({ id: { type: new GraphQLNonNull(GraphQLID) }, name: { type: new GraphQLNonNull(GraphQLString) }, posts: { type: new GraphQLNonNull( new GraphQLList( new GraphQLNonNull(Post) ) ) } }) }) const Post = new GraphQLObjectType({ name: “Post”, fields: () => ({ id: { type: new GraphQLNonNull(GraphQLID) }, title: { type: new GraphQLNonNull(GraphQLString) } }) })
  3. const User = new GraphQLObjectType({ name: “User”, fields: () =>

    ({ id: { type: new GraphQLNonNull(GraphQLID) } name: { type: new GraphQLNonNull(GraphQLString) } posts: { type: new GraphQLNonNull( new GraphQLList( new GraphQLNonNull(Post) ) ) } }) }) const Post = new GraphQLObjectType({ name: “Post”, fields: () => ({ id: { type: new GraphQLNonNull(GraphQLID) } title: { type: new GraphQLNonNull(GraphQLString) } }) }) graphql-js R esolver-first type User { id: ID! name: String! posts: [Post!]! } type Post { id: ID! title: String! }
  4. Consistent schema with resolvers graphql-js Concise and simple API Easy

    to reason about relationships No circular-dependencies issues R esolver-first
  5. type Query { me: User } type Mutation { login(input:

    Login): User signup(input: Login): User! } input Login { userName: String! password: String! } type User { id: ID! userName: String! name: String! profilePictureUrl: String! } const resolvers = { Query: { me: (parent, args, ctx) => getAuthenticatedUser(ctx) }, Mutation: { login: (parent, args, ctx) => { return loginUser(args.userName, args.password) } }, User: { profilePictureUrl: (parent, args, ctx) => { if (!parent.pictureId) return null return buildPictureUrl(parent.pictureId) } } } schema.graphql resolvers.js Schem a-first
  6. type Query { me: User } type Mutation { login(input:

    Login): User signup(input: Login): User! } input Login { userName: String! password: String! } type User { id: ID! userName: String! name: String! profilePictureUrl: String! } const resolvers = { Query: { me: (parent, args, ctx) => getAuthenticatedUser(ctx) }, Mutation: { login: (parent, args, ctx) => { return loginUser(args.userName, args.password) } }, User: { profilePictureUrl: (parent, args, ctx) => { if (!parent.pictureId) return null return buildPictureUrl(parent.pictureId) } } } schema.graphql resolvers.js Forgot args.input wrapper Schem a-first
  7. type Query { me: User } type Mutation { login(input:

    Login): User signup(input: Login): User! } input Login { userName: String! password: String! } type User { id: ID! userName: String! name: String! profilePictureUrl: String! } const resolvers = { Query: { me: (parent, args, ctx) => getAuthenticatedUser(ctx) }, Mutation: { login: (parent, args, ctx) => { return loginUser(args.userName, args.password) } }, User: { profilePictureUrl: (parent, args, ctx) => { if (!parent.pictureId) return null return buildPictureUrl(parent.pictureId) } } } schema.graphql resolvers.js Forgot a mutation signup Schem a-first
  8. type Query { me: User } type Mutation { login(input:

    Login): User signup(input: Login): User! } input Login { userName: String! password: String! } type User { id: ID! userName: String! name: String! profilePictureUrl: String! } const resolvers = { Query: { me: (parent, args, ctx) => getAuthenticatedUser(ctx) }, Mutation: { login: (parent, args, ctx) => { return loginUser(args.userName, args.password) } }, User: { profilePictureUrl: (parent, args, ctx) => { if (!parent.pictureId) return null return buildPictureUrl(parent.pictureId) } } } schema.graphql resolvers.js Not allowed to return null Schem a-first
  9. type Query { me: User } type Mutation { login(input:

    Login): User signup(input: Login): User! } input Login { userName: String! password: String! } type User { id: ID! userName: String! name: String! profilePictureUrl: String! } schema.graphql const resolvers = { Query: { me: (parent, args, ctx) => getAuthenticatedUser(ctx) }, Mutation: { login: (parent, args, ctx) => { return loginUser(args.userName, args.password) } }, User: { profilePictureUrl: (parent, args, ctx) => { if (!parent.pictureId) return null return buildPictureUrl(parent.pictureId) } } } resolvers.js { id: "user-1", name: "Alice", username: "AliceInWonderland", pictureId: "http://example.org/alice.png", hashedPassword: "nv02lsac9sdocx" } Schem a-first
  10. type Query { me: User } type Mutation { login(input:

    Login): User signup(input: Login): User! } input Login { userName: String! password: String! } type User { id: ID! userName: String! name: String! profilePictureUrl: String! } schema.graphql const resolvers = { Query: { me: (parent, args, ctx) => getAuthenticatedUser(ctx) }, Mutation: { login: (parent, args, ctx) => { return loginUser(args.userName, args.password) } }, User: { profilePictureUrl: (parent, args, ctx) => { if (!parent.pictureId) return null return buildPictureUrl(parent.pictureId) } } } resolvers.js { id: "user-1", name: "Alice", username: "AliceInWonderland", pictureId: "http://example.org/alice.png", hashedPassword: "nv02lsac9sdocx" } resolver missing (“default resolver”) userName Schem a-first
  11. graphql-js Easy to reason about relationships Concise and simple API

    Consistent schema with resolvers Circular-dependencies issues No need graphql-tools Easy to reason about relationships Concise and simple API Inconsistencies No circular-dependencies issues Lack of tooling for GraphQL SDL
  12. “Schema-first fixes” Community tooling coming to the rescue EXAMPLES graphql-import,

    graphql- codegen, graphql-modules… type Query { hello(name: String!): String! } export interface QueryInterface { hello: ( parent: {}, args: { name: string }, ctx: Context, info: GraphQLResolverInfo ) => string } export const Query: QueryInterface = { hello: (parent, { name }) => `Hello ${name}` } Import & implement interface Create mapping
  13. “Schema-first fixes” Schem a-first Problems Solutions • No import support

    • Inconsistencies between schema and resolver • Scalability graphql-import , graphqlgen gqlgen , graphql-modules
  14. Let’s take a step back Resolver-first Schema-first Single source of

    truth Human-readable schema Concise, and flexible API No circular-dependencies issues
  15. “Built-in” type-safety Single source of truth What are we pursuing?

    Concise, and flexible API Human-readable schema No circular-dependencies issues
  16. A glimpse into the future “Built-in” type-safety Single source of

    truth Concise, and flexible API Human-readable schema No circular-dependencies issues
  17. Resolver-first vs Schema-first 1 Key Takeaways 1 2 enables type-safety

    for resolver-first GraphQL server development graphql-nexus 3 Type-safe DB access prevents downtime and speeds up development with great DX