Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate TypeScript/Flow types for resolvers from the schema #404

Closed
illegalprime opened this issue Sep 26, 2017 · 11 comments
Closed

Generate TypeScript/Flow types for resolvers from the schema #404

illegalprime opened this issue Sep 26, 2017 · 11 comments
Labels
feature New addition or enhancement to existing solutions

Comments

@illegalprime
Copy link

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.

@skovhus
Copy link

skovhus commented Nov 1, 2017

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

@corydeppen
Copy link
Contributor

@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.

@stubailo
Copy link
Contributor

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

@dsebastien
Copy link

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.

@stubailo stubailo changed the title [Feature Request] Generate Resolver types from GraphQL Spec Generate TypeScript/Flow types for resolvers from the schema Jul 13, 2018
@stubailo stubailo added feature New addition or enhancement to existing solutions and removed discussion labels Jul 13, 2018
@stubailo
Copy link
Contributor

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

@koenpunt
Copy link

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

@disintegrator
Copy link

disintegrator commented Oct 9, 2018

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;

@koenpunt
Copy link

koenpunt commented Oct 9, 2018

@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.

@disintegrator
Copy link

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

@kamilkisiela
Copy link
Collaborator

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.

@yaacovCR
Copy link
Collaborator

yaacovCR commented Mar 27, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New addition or enhancement to existing solutions
Projects
None yet
Development

No branches or pull requests

9 participants