Graphql-code-generator: Custom Scalars?

Created on 14 Oct 2017  路  25Comments  路  Source: dotansimha/graphql-code-generator

In Typescript, custom scalar types are emitted like:

export type Foo = any;

How can I make the emitted Foo refer to my existing types?

For example, if somewhere else in my code base, I have:

class Foo {
  bar: string

  someMethod() {
    ...
  }
}

I know this supports templating, but even with templating, I'm not sure how to make Foo in the generated types refer to my project's Foo. It seems like I'd need to import existing scalar definitions into my schema template? Or I'd need to define all my custom scalars in templates? Both of these sound pretty unmaintainable. Is there some convenient way to do this? Or is there an example for solving this sort of problem?

PS. Thank you for this package!

enhancement plugins waiting-for-release

Most helpful comment

I see, @pleerock . I am thinking about the right solution for this.

We can easily pass custom configuration, so maybe a map like that:

{
    config: {
        scalars: [
             { scalar: "Date", type: "Date" }
        ]
    }
}

And if you want to use a custom TypeScript type you wrote, for example: MyDateType and map it to a specific GraphQL Scalar, we might need import as well:

{
    config: {
        scalars: [
             { scalar: "Date", type: "MyDateType", import: "../../types/my-date-type" }
        ]
    }
}

And it will also add the import on top of the file.

What do you think?

All 25 comments

@garrettm
I think we can add a custom configuration per each generator, and pass it via gql-gen.json config file. this way we can add custom scalars mapping (and also an import from the file that contains the interface).

What do you think?

Correct me if I'm wrong, here's what I'm imagining, given what you said:

  • add an option for extern definitions of scalars
  • if set, gql-gen adds extra emits:
import * as Extern from './extern'
export type ScalarName = Extern.ScalarName

The user can export scalar types from this extern.ts file, and the TS compiler will complain if you forget one.

@garrettm yeah it makes sense.
I'm currently working on the generator-specific configuration, and then we will be able to implement this feature

@dotansimha sounds good. If you're interested in letting someone else do the work, point me in the direction when ready and I can take a look.

I am curious if there is any progress on this? Would love to be able specify types for my scalars :)

@dotansimha facing same issue here. In my case I have a simple scalar Date and I would like to avoid generator to add export type Date = any; in the output since I already have a Date object globally available.

I see, @pleerock . I am thinking about the right solution for this.

We can easily pass custom configuration, so maybe a map like that:

{
    config: {
        scalars: [
             { scalar: "Date", type: "Date" }
        ]
    }
}

And if you want to use a custom TypeScript type you wrote, for example: MyDateType and map it to a specific GraphQL Scalar, we might need import as well:

{
    config: {
        scalars: [
             { scalar: "Date", type: "MyDateType", import: "../../types/my-date-type" }
        ]
    }
}

And it will also add the import on top of the file.

What do you think?

It will definitely resolve most of issues.

I like the typescript solution. I actually came across a a use-case with scalars today that it would solve. My other idea was to patch the generated code to add an import and replace the generated type alias with a regex, but this solution is of course much nicer :-).

One note about the import. Lets say you want to do a named import, how would that work? Perhaps something like this could work:

{
    config: {
        imports: ["import {MyDateType}" from "../datelib", import {MyFooType} from "../foolib" ]
        scalars: [
             { scalar: "Date", type: "MyDateType"},
             { scalar: "Foo", type: "MyFooType"}
        ]
    }
}

@jonaskello yeah you are right, your examples supports more use cases (such as named import, default import or any other way).

For query/mutation code generation, I use apollo-cli, which has the flag --customScalarsPrefix when using codegen

// with --customScalarsPrefix=GQL
interface SomeType {
    date: GQLDate;
}

GQLDate will not be exported in the generated types, and I keep a separate .d.ts with

declare type GQLDate = Date;

Think this is a nice solution for people who either already have their types globally available, and/or want to avoid name collisions with similarly named types

Note: Not suggesting that apollo-cli can be used instead (it can't), just sharing as inspiration for a potential fix

@dotansimha Since new #444 was released, there seems to be an issue with my JSON scalar type.

schema

scalar JSON

type foo {
  bar: JSON!
}

generated types

export type JSON = any; //  as before

export interface foo {
  bar: Json; // not as before (previously, i would see JSON here)
}

I'm aware of the new scalars option for gql-gen.json but this doesn't help me as it results in the following

export type JSON = MyFancyType; // ok

export interface foo {
  bar: Json; // Json != JSON
}

@kamilkisiela with 0.11.0-alpha.4c054a57 of generator & template, I now get

export type Json = any;

export interface foo {
  bar: Json;
}

This works but why can't it be JSON in full caps, as found in my schema?

scalar JSON

Is this still waiting for release?

@elie222 You can use ScalarsMap now.

I'm using typescript, typescript-apollo-angular plugins to generate the client
and graphql-iso-date on the server for scalar DateTime.

Does anyone know if adding the following:

config:
  scalars:
    DateTime: Date

will mean that any field returned from the fetch of DateTime type will be an instance of the JS Date class?

@teebszet yes, but note that you can't really send JS Date over the network, so it will probably get serialized and you'll get it as string in the client, so the server can use JS Date, but the client will get string.

@teebszet yes, but note that you can't really send JS Date over the network, so it will probably get serialized and you'll get it as string in the client, so the server can use JS Date, but the client will get string.

@dotansimha yeah that makes sense. I guess it would be up to the typescript-apollo-angular plugin to initialise those DateTime fields as Date objects in the client. But I'm not sure if that is a feature that exists or not

@teebszet I think it should be done in Apollo client, during the runtime, and then you can reflect the status of data by using custom scalars in the codegen.
I think you can do that with Apollo Link.

@dotansimha Do you have some useful resources on how it can be done universally with Apollo Link?

I've found this pretty old (but still active) feature request for Apollo client and it almost seems like it's still not possible. It would be really lovely to get ISO date automatically parsed and converted and along with correct typings, it would be a breeze.

This has especially become an issue with date-fns v2 which no longer supports passing a string. Only a Date or number. It's definitely fairly tedious doing that manually whenever there is a need to work with field that contains a stringified date.

@FredyC use parseISO

The previous parse implementation was renamed to parseISO.

Can anyone update on this? I am still not able to convert custom scalars. This is my codegen.yml but it doesn't work.

overwrite: true
schema: 'schema.graphql'
generates:
  src/generated/graphql.tsx:
    config:
      scalars:
        Long: number
        BigDecimal: number
    plugins:
      - 'typescript'
      - 'typescript-operations'
      - 'typescript-react-apollo'
      - 'typescript-graphql-files-modules'
      - 'typescript-document-nodes'
      - 'fragment-matcher'
  ./graphql.schema.json:
    plugins:
      - 'introspection'

@finaxar-arthur What do you mean by converting custom scalars? If you have any issues, could you please create a new issue with a minimal reproduction?

@finaxar-arthur the config must be at the top, like

overwrite: true
schema: 'schema.graphql'
config:
  scalars:
    Long: number
    BigDecimal: number
Was this page helpful?
0 / 5 - 0 ratings