Graphql-tools: Generate TypeScript/Flow types for resolvers from the schema

Created on 26 Sep 2017  ·  11Comments  ·  Source: ardatan/graphql-tools

So far we've been trying to implement GraphQL for our microservices at hackgt/registration#159, but when implementing a resolver I have no type definitions for the resolver or for my GraphQL schema (typescript). I've tried solutions like graphql-typewriter but the types it generates has no root parameter in the query function signature (i.e. it is (args, context) and not (obj, args, context)).

Does this fit under this repo or is there another solution I'm not seeing?

My current workaround is manually typing everything.

feature

Most helpful comment

We have already it in https://github.com/dotansimha/graphql-code-generator

It generates interfaces for Mutation and Query as well as for whole GraphQL Types and single fields. You gain strongly typed arguments too and control of a context and related.

All 11 comments

Would also love this... Did you get any further @illegalprime?

@illegalprime Seems the topic of typing resolvers is still an open issue - graphql/graphql-js#574 - at least from a Flow perspective. There doesn't appear to be a related issue in the DefinitelyTyped repo, but something like GraphQLFieldResolver<TSource, TContext, TArgs> might be helpful in both Flow and TypeScript.

I think this is definitely something that would be super high value, if someone has a design please post it here!

This is indeed the weak link in the whole chain.

I can generate TS interfaces/types from my graphql schema (SDL) using graphql-code-generator / apollo codegen and from there strongly type my client side.

On the server side though, I have a disconnect between my schema and the resolvers implementation, which I can only check partially after the fact by running the server (which occasionally tells me I've done something wrong). Gaining compile-time safety here would be great.

I think this would still be great to have! Perhaps this is something we can work on after we finish cleaning up query code generation, as part of the Apollo CLI here: https://github.com/apollographql/apollo-cli

@prisma is working on something here; https://github.com/prisma/graphql-resolver-codegen

In terms of desired outcome, may I suggest the following:

schema.graphql

type Query {
  companies: [Company!]!
  company(id: ID!): Company
  employee(id: ID!): Employee
}

type Mutation {
  registerCompany(form: CompanyRegistrationForm!): Company!
  registerEmployee(form: EmployeeRegistrationForm!, companyId: ID!): Employee!
}

input CompanyRegistrationForm {
  name: String!
  ticker: String
}

input EmployeeRegistrationForm {
  name: String!
  title: String
  role: EmployeeRole
}

enum EmployeeRole {
  CEO
  Manager
  Other
}

type Employee {
  id: ID!
  name: String!
  title: String
  role: EmployeeRole
  company: Company
}

type Company {
  id: ID!
  name: String!
  ticker: String
}

schema.ts

// Note how optional fields in the input types are marked as optional in
// the generated TypeScript interface
export interface CompanyRegistrationForm {
  name: string;
  ticker?: string | null;
}

export interface EmployeeRegistrationForm {
  name: string;
  title?: string | null;
  role?: EmployeeRole | null;
}

export enum EmployeeRole {
  CEO = "CEO",
  Manager = "Manager",
  Other = "Other"
}

export interface Employee {
  id: string;
  name: string;
  title: string | null;
  role: EmployeeRole | null;
  company: Company | null;
}

export interface Company {
  name: string
  ticker: string | null
}

export interface Query_company_args {
  id: string;
}
export interface Query_employee_args {
  id: string;
}
export interface Mutation_registerCompany_args {
  form: CompanyRegistrationForm;
}
export interface Mutation_registerEmployee_args {
  form: EmployeeRegistrationForm;
  companyId?: string | null;  // Also note how it's generated as optional
}

export type Resolver<T, Root, Args, Context> = (
  root: Root,
  args: Args,
  ctx: Context
) => T | Promise<T>;

export interface RootResolver<Context> {
  Query: {
    companies: Resolver<Company[], null, {}, Context>;
    company: Resolver<Company | null, null, Query_company_args, Context>;
    employee: Resolver<Employee | null, null, Query_employee_args, Context>;
  };

  Mutation: {
    registerCompany: Resolver<
      Company,
      null,
      Mutation_registerCompany_args,
      Context
    >;
    registerEmployee: Resolver<
      Employee,
      null,
      Mutation_registerEmployee_args,
      Context
    >;
  };
}

export type QueryResolver<Context> = RootResolver<Context>['Query']
export type MutationResolver<Context> = RootResolver<Context>['Mutation']

Usage example:

import { Context } from "./graphql/context";
import { MutationResolver, QueryResolver, RootResolver } from "./graphql/schema";

const companies: QueryResolver<Context>['companies'] = (
  root,
  args,
  ctx
) => { /* work it out */ } 

const registerCompany: MutationResolver<Context>['registerCompany'] = (
  root,
  args,
  ctx
) => { /* work it out */ }

const rootResolver: RootResolver<Context> = {
  Query: {
    companies,
    // fill out with remaining resolvers
  },
  Mutation: {
    registerCompany,
    // fill out with remaining resolvers
  },
}

export default rootResolver;

@disintegrator looks nice. But what's the purpose of the Query and Mutation typescript interfaces? Maybe chime in at @prisma's repo too, since they have a lot of generating already working.

@koenpunt good catch they aren't really used for anything. Edited and removed.

We have already it in https://github.com/dotansimha/graphql-code-generator

It generates interfaces for Mutation and Query as well as for whole GraphQL Types and single fields. You gain strongly typed arguments too and control of a context and related.

Closing in favor of above prior art as well as other alternatives (type-graphql, nexus). See also https://github.com/graphql/graphql-js/issues/2188.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

flippidippi picture flippidippi  ·  3Comments

BassT picture BassT  ·  3Comments

ericclemmons picture ericclemmons  ·  4Comments

Adherentman picture Adherentman  ·  4Comments

freiksenet picture freiksenet  ·  4Comments