grats icon indicating copy to clipboard operation
grats copied to clipboard

Support for enum values as default argument values

Open felamaslen opened this issue 9 months ago • 7 comments

I should be able to write a resolver like so:

/** @gqlEnum */
enum Colour {
  Red = 'RED',
  Blue = 'BLUE',
}

/** @gqlQueryField */
export function myField(context: Context, colour: Colour = Colour.Red): string {
  return 'hello world';
}

It should generate the following schema:

enum Colour {
  RED
  BLUE
}

type Query {
  myField(colour: Colour! = RED): String!
}

The observed behaviour is an error is thrown during schema compilation:

Expected GraphQL field argument default values to be a literal. Grats interprets argument defaults as GraphQL default values, which must be literals. For example: 10 or "foo".

felamaslen avatar Feb 25 '25 15:02 felamaslen

Makes sense. Here's a Playground repro of the issue: link.

Implementing this might be a bit tricky since right now we expect input values to be parsable without needing to do type resolution, but it should be possible since we already do type resolution for output types.

captbaritone avatar Feb 26 '25 15:02 captbaritone

As a workaround I think this would work (but would require using a union of string literals rather than a TypeScript enum).

/** @gqlEnum */
type Foo = "BAR" | "BAZ";


/** @gqlQueryField */
export function greeting(myArg: Foo = "BAR"): string {
  return "Hello";
}

Playground Link.

It's actually a bit interesting because the default value still gets interpreted as a string literal in the generated GraphQL even though the type is an enum. I think this ends up working out because GraphQL will coerce string literals to enums. So, it might be able to unblock you (or someone else) in the short term, but it's a bit odd.

captbaritone avatar Feb 26 '25 15:02 captbaritone

Btw for when args are objects, I had to do this:

/** @gqlEnum */
type Foo = "BAR" | "BAZ";


/** @gqlQueryField */
export function greeting({
  // @ts-ignore https://github.com/captbaritone/grats/issues/172#issuecomment-2685496600
  myArg = "BAR"
}: {
  myArg: Foo
}): string {
  return "Hello";
}

Otherwise works just fine

louy avatar Feb 26 '25 18:02 louy

Ok, actually the workaround doesn't work. The request will fail during validation E.g.

    Received value: [{"locations": [{"column": 13, "line": 5}], "message": "Field \"locations\" argument \"gender\" of type \"TreatmentGender!\" is required, but it was not provided."}]

And looking at output schema (which clients see) defaultValue is missing

This is reproducible in the playground (Typescript Schema)

louy avatar Feb 26 '25 18:02 louy

Btw for when args are objects, I had to do this:

Could you share the TS error you are suppressing here? When I try that in the playground I don't get a TS error.

And looking at output schema (which clients see) defaultValue is missing

A yeah, I think this is a separate bug. We are currently relying on TypeScript to add the default values. That means we don't need GraphQLJS to know about it. But, in the case of non-nullable fields with defaults the runtime behavior for the two is different, so we need to add the default value to the runtime version of the schema to fix that. I'll open a separate issue for that.

captbaritone avatar Mar 02 '25 17:03 captbaritone

Fixed in https://github.com/captbaritone/grats/commit/db77a72bcfa4074ba5ced686c29e2077dfaf63dc

Can you try this npm release and see if it works for you? https://www.npmjs.com/package/grats/v/0.0.0-main-db77a72b

captbaritone avatar Mar 08 '25 04:03 captbaritone

This seems fixed (in the playground for TS enums, and also for type enums) now. No more type errors and defaultValue is being set in both cases.

Thank you!

louy avatar Mar 16 '25 16:03 louy