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

Codegen Model Types #34

Closed
ChrisChares opened this issue May 15, 2019 · 19 comments
Closed

Codegen Model Types #34

ChrisChares opened this issue May 15, 2019 · 19 comments
Labels
feature-request New feature or request type-gen Issues on graphql-type-generator

Comments

@ChrisChares
Copy link

Is your feature request related to a problem? Please describe.

It's frustrating that codegen doesn't generate typescript model types. If I define a @model in my schema consisting of:

type Room
  @model 
{
  id: ID!
  name: String!
  primaryColor: String
  iconName: String
}

Codegen will generate literally thousands of lines of code. For example:

export type GetRoomQuery = {
  getRoom:  {
    __typename: "Room",
    id: string,
    name: string,
    primaryColor: string | null,
    iconName: string | null,
};

export type ListRoomsQuery = {
  listRooms:  {
    __typename: "ModelRoomConnection",
    items:  Array< {
      __typename: "Room",
      id: string,
      name: string,
      primaryColor: string | null,
      iconName: string | null,
};

export type CreateRoomInput... etc etc

But it never generates a simple Room model interface. We have to create and maintain this manually. Why? Is there a reason for this?

Describe the solution you'd like

For this specific example, somewhere in the thousands of lines of code in API.ts I would expect to find:

export interface Room {
  id: string;
  name: string;
  primaryColor?: string;
  iconName?: string;
}

Describe alternatives you've considered

Manual creation and maintenance of model types.

Thanks for your time,
Chris

@yuth
Copy link
Contributor

yuth commented May 16, 2019

@ChrisChares GraphQL supports having only a subset of selection in each query and codegen generates this code so that the type has only the chosen selection set in the type of that operation.

We will put this in the backlog to use Typescripts advanced Pick types
type Pick<T, K extends keyof T>

@ChrisChares
Copy link
Author

@yuth thanks for the quick response. Pick types sounds great.

I figured the request-only-what-you-need nature of Graphql had something to do with the current implementation.

Out of curiosity though, does that make sense in light of the fact that every one of a given model's fields are already included in all the auto generated queries and mutations (if sufficient depth)?

Yes these full auto generated models will be incomplete (or worse misleading) when used with custom queries, but unless i'm missing something they'll map perfectly to what is currently autogenerated?

@yuth
Copy link
Contributor

yuth commented May 16, 2019

Out of curiosity though, does that make sense in light of the fact that every one of a given model's fields are already included in all the auto generated queries and mutations (if sufficient depth)?

The auto generated statements are meant to be used as starting point for an application and we expect them to either customize or add additional statements by adding new files to src/graphql folder. So I would prefer to take Pick approach.

@ChrisChares
Copy link
Author

Sounds good, thanks for the explanation.

@hisham
Copy link
Contributor

hisham commented May 17, 2019

Hi @ChrisChares - I currently do something like this:

export type Room = GetRoomQuery['getRoom'];

And voila you have what you want.

Of course in the case of partial query results you will sometimes have to cast the result as unknown then back to Room. I should look into Pick as @yuth suggested for sure.

@jkeys-ecg-nmsu
Copy link

@hisham do you map the non-primitive (in Java, I guess) Scalar values to primitive Javascript? e.g. AWSTimestamp -> string, AWSJSON -> object? I've wondered how clean this would be irl.

@hisham
Copy link
Contributor

hisham commented Aug 6, 2019

@jkeys-ecg-nmsu, I'm using typescript. Yes the codegen tool does map AWSTimestamp and AWSJSON to string in typescript. We did run into a couple of cases where we had to convert dates to specific string formats for it to be accepted by GraphQL as AWSTimestamp. Also AWSJSON (dynamodb specifically) for example does not like empty JSON fields so we deep clean the json before submitting them through the graphql endpoint.

@elibolonur
Copy link

This feature would be very useful!

@hoegertn
Copy link

This would really be great. Due to the selective queries, what about having a Room interface with all fields optional? So I at least get the field names and it would be compatible with selective results.

@adilusman51
Copy link

@ChrisChares @hisham
We extract the Room base type, by utilizing TypeScripts Utility types to transform these types into the types all of our projects can use.

export interface Room extends Omit<Exclude< GetRoomQuery["getRoom"], null>, "__typename"> {}

@hoegertn
export interface RoomOptional = Partial<Room>

Have a look at this approach

@amcdnl
Copy link

amcdnl commented Jul 29, 2020

I messed with this for a while and finally gave up on Amplify doing this. Ironically Amplify uses this same code generator ( that I reference below ) to actually do its autogeneration.

Add the following to your package:

    "@graphql-codegen/cli": "1.17.6",
    "@graphql-codegen/typescript": "^1.17.6",
    "@graphql-codegen/typescript-operations": "^1.17.6",

Create a config.yml in the root:

overwrite: true
schema: "src/core/graphql/schema.json"
documents: "src/core/graphql/*.ts"
generates:
  src/core/graphql/types.d.ts:
    plugins:
      - typescript
      - typescript-operations
    config:
      skipTypename: true

NOTE: schema: "src/core/graphql/schema.json" is my file downloaded from amplify codegen.

Then add the following to your package.json scripts:

"build:graphql": "graphql-codegen --config codegen.yml",

@ianmartorell
Copy link

I think the problem is basically that amplify codegen is using the onlyOperationTypes flag of graphql-codegen's typescript plugin. When you set it to false or simply not set it, all the base types are generated as well. Why not just drop this flag, or at least make it configurable through the amplify extension in .graphqlconfig.yml?

@amcdnl
Copy link

amcdnl commented Aug 3, 2020

cc @gsans

@ywongweb
Copy link

ywongweb commented Nov 1, 2020

@amcdnl

Edit: fixed my error by updating the graphql dependency of the project
yarn add -D graphql

I tried your solution but encountered this error, have you seen this before?

  ✖ ./src/types.d.ts
    Failed to load schema from src/graphql/schema.json:

        originalType.toConfig is not a function
        TypeError: originalType.toConfig is not a function

@LaszloDev
Copy link

I think the problem is basically that amplify codegen is using the onlyOperationTypes flag of graphql-codegen's typescript plugin. When you set it to false or simply not set it, all the base types are generated as well. Why not just drop this flag, or at least make it configurable through the amplify extension in .graphqlconfig.yml?

Any plans from the team to implement this?

@AaronZyLee AaronZyLee transferred this issue from aws-amplify/amplify-cli Jan 27, 2021
@AaronZyLee AaronZyLee added feature-request New feature or request type-gen Issues on graphql-type-generator labels Jan 27, 2021
@amcdnl
Copy link

amcdnl commented Jan 27, 2021

I actually ended up removing the amplify codegen all together. Here is what I came up with:

overwrite: true
schema:
  - ${REACT_APP_SCHEMA_URL}:
      method: GET
documents: "src/core/graphql/*.ts"
generates:
  src/core/graphql/schema.graphql:
    plugins:
      - schema-ast:
          includeDirectives: true
          commentDescriptions: true

  src/core/graphql/types.ts:
    plugins:
      - add:
          content: '/* eslint-disable */'
      - typescript:
          skipTypename: true

  src/core/graphql/operations.ts:
    plugins:
      - add:
          content: '/* eslint-disable */'
      - named-operations-object:
          identifierName: Operations

  src/core/graphql/:
    preset: near-operation-file
    presetConfig:
      baseTypesPath: types.ts
      extension: .ts
    plugins:
      - add:
          content: '/* eslint-disable */'
      - typescript-operations:
          namingConvention: pascal-case#pascalCase
          skipTypename: true
          # preResolveTypes: true
      - typescript-document-nodes:
          namingConvention: camel-case#camelCase

and then I made a REST API endpoint that would let me get the graphql schema that looks like this:

import { AppSync } from 'aws-sdk';
import { Request, Response } from 'express';

const client = new AppSync();

const apiId = process.env.API_API_GRAPHQLAPIIDOUTPUT as string;
const apiKey = process.env.API_API_GRAPHQLAPIKEYOUTPUT as string;

export async function schemaHandler(req: Request, res: Response) {
  try {
    const req = client.getIntrospectionSchema({
      apiId,
      format: 'JSON',
      includeDirectives: true
    });

    req.on('build', () => {
      req.httpRequest.headers['x-api-key'] = apiKey;
    });

    const result = await req.promise();

    return res
      .type('application/json')
      .send((result.schema as any).toString());
  } catch (error) {
    return res
      .status(500)
      .json({ success: false, errors: ['Failed to get schema'] });
  }
}

@AaronZyLee
Copy link
Contributor

I think the problem is basically that amplify codegen is using the onlyOperationTypes flag of graphql-codegen's typescript plugin. When you set it to false or simply not set it, all the base types are generated as well. Why not just drop this flag, or at least make it configurable through the amplify extension in .graphqlconfig.yml?

Amplify codegen is not using the guild's graphql-code-generator typescript plugin but is based on the Apollo generator. Hence the flag is not applicable to the package. However we do notice that there are some advantages in guild's graphql generator system and consider the tradeoffs of migrating to it.

As for the model type generation mentioned above , we just merged one PR pended on the release.

Last but not least, since most of amplify codegen users are mostly using amplify framework and appsync service, we would like to develop the codegen as a more amplify/appsync/datastore oriented generator, which is different from others. A redesign for amplify codegen is under the plan and we are welcome for any thoughts or feedbacks so that we can provide a better developer experience.

@phani-srikar
Copy link
Contributor

This is similar to #32. Closing this in favor of that.

@joekiller
Copy link

Totally a necrobump but for those in the future I utilized the following type to stripe __typename from nested objects:

/**
 * Exclude __typename from the type for API responses.
 *
 * combining {@link https://grrr.tech/posts/2021/typescript-partial/#in-conclusion Making TypeScript's Partial type work for nested objects} and {@link https://github.com/aws-amplify/amplify-codegen/issues/34#issuecomment-610964922}
 *
 */
export type Response<K> = {
    [attr in keyof K]?: K[attr] extends object
        ? Response<K[attr]>
        : K[attr] extends object | null
            ? Response<K[attr]> | null
            : K[attr] extends object | null | undefined
                ? Response<K[attr]> | null | undefined
                : Omit<Exclude< K[attr], null>, "__typename">;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request New feature or request type-gen Issues on graphql-type-generator
Projects
None yet
Development

No branches or pull requests