Skip to content

Specifying the literal value type of a custom scalar #1139

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

Open
mindplay-dk opened this issue Feb 25, 2025 · 3 comments
Open

Specifying the literal value type of a custom scalar #1139

mindplay-dk opened this issue Feb 25, 2025 · 3 comments

Comments

@mindplay-dk
Copy link

I was surprised to find there is no way to declare a custom scalar type with it's actual value type?

When declaring something like:

scalar Date

It's all down to run-time type-checking for actual Date literal values.

I was expecting something like:

scalar Date : String

vs

scalar Date : Int

I understand we can't statically verify the actual contents of e.g. a Date beyond the fact that it's a String or Int, but a literal could at least be validated (statically) for the correct literal value type, which would be better than nothing, wouldn't it?

At run-time, of course, there would be further validatio - but statically, a scalar is almost like a type alias, and could at least be validated as such, I think?

(Sorry if this has already been asked - it seems like this would be a common question for someone learning GraphQL, but I've read through dozens of issues and I couldn't find this question asked or answered.)

@benjie
Copy link
Member

benjie commented Feb 25, 2025

Since custom scalars can be anything, declaring them as String or Int or such has limited value (see #337 for one discussion around this). Instead, we allow you to link to the specification of the scalar using the @specifiedBy directive, and clients can recognize this (unique, canonical) URL and apply the relevant behaviour. We also have a repository of "well known scalars" that clients can implement at https://scalars.graphql.org/ though no-one has really submitted much to it as of yet (please do!).

@mindplay-dk
Copy link
Author

mindplay-dk commented Feb 26, 2025

The most recent comment on that thread echoes the argument I'm making though:

When defining a custom scalar, say Timestamp, it would be nice to know from the schema what that will be serialized as. Assuming my serialization format was json, I'd wonder if this would be coerced to a string or number in a response. Unfortunately, we cannot schematize this unless some other information, such as the primitive types native to the serialization format, is also schematized.

That seems to be the same point I'm trying to make.

Since custom scalars can be anything, declaring them as String or Int or such has limited value
Instead, we allow you to link to the specification of the scalar using the @specifiedBy directive,

These are two different problems though?

One is about minimal static type-safety in the document itself - the other is about run-time type logic following a spec.

Why do you think type-safety at the document level has "limited value"?

@benjie
Copy link
Member

benjie commented Feb 26, 2025

The value of marking a scalar as a Date is that you know all of the constraints on the type: not just whether it is serialized as string or not (depending on the encoding used), but also the valid formats that are a subset of this (e.g. ISO8601/RFC3339), how to handle it on the client, and what you can feed in as inputs to the schema.

A custom scalar may allow multiple different types of input, for example Date might accept an ISO8601 or RFC3339 encoded string value whilst also accepting a numeric unix timestamp. (Likely only one of these would be used on output though.)

Some custom scalars don't have a specific encoding, e.g. JSON, and should allow any inputs.

The serialization format for scalars is specific to the transport encoding used (which is generally outside of the domain of the GraphQL spec), and some serialization formats other than JSON might allow you to e.g. represent Binary data directly rather than Base64 encoding it as we would in JSON. They may have a specific "date" type that you can use, rather than serializing to string. The schema doesn't necessarily know the on-the-wire serialization format that will be used.

When the client understands a type, e.g. via the specifiedBy directive, it allows it to use a codec that automatically converts the custom scalar data into a more appropriate local type, for example using new Date(Date.parse(value)) to turn a serialized date string into a Date object. Saying it's a "string" in the schema could therefore be confusing to client developers (if what they actually see is a Date object), and thus would need to be handled carefully (e.g. @serialize(output: STRING, input: [STRING, INT, FLOAT]) or something else suitably specific).

Though the value of indicating the serialization format of a custom scalar is limited and doesn't confer the various other benefits and subtleties outlined above, that isn't to say we shouldn't do it; there have been previous proposals to do so (such as the one I linked above). If you have a proposal that addresses the concerns and provides value then you should bring it to the GraphQL Working Group; instructions for joining a meeting can be found here:

https://github.com/graphql/graphql-wg/blob/main/JoiningAMeeting.md

Let us know if you need any help forming your proposal!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants