question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Typesafe serialization and deserialization of scalars

See original GitHub issue

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

We use a lot of custom scalars in our schema. Following are three small examples:

"""The `Date` scalar represents an ISO-8601 compliant date type."""
scalar Date
"""The `DateTime` scalar represents an ISO-8601 compliant date time type."""
scalar DateTime
"""The WeekYear scalar represent an ISO Week compliant week year type. yyyy-Www"""
scalar WeekYear

Currently, the only option we have been able to find is to use the ScalarsMap to map them to strings respectively:

scalars:
     Date: string
     DateTime: string
     WeekYear: string

I am aware of the fact that you can map scalars to custom types aside from string too, but serializing to Date obviously doesnt work since graphql-codegen has no concept(that im aware of) that allows specifying how a specific scalar is deserialized. That leaves me with what we have currently, which is telling graphql-codegen that the scalar should be mapped to string and then deserialize them at every occurence in my code which results in noisy and repetitive code for both mutations and queries.

Describe the solution you’d like I would like to have some way to specify scalar types on the client safely. Here is my general idea:

  1. Add a new config value scalarMappers
  2. The scalarMappers value points to a folder containing all the scalarMappers
  3. ScalarMappers need to adhere to this interface:
interface ScalarMapper<T> {
    resultingTypeName(): string
    deserializeValue(value: any): T
    serializeValue(value: T): string
}
  1. Users could then declare typesafe parsers for scalar values:
import { parseISO } from "date-fns";

class DateTimeMapper implements ScalarMapper<Date> {
  resultingTypeName(): string {
    return 'Date';
  }
  deserializeValue(value: any): Date {
    if(typeof value !== 'string') {
      throw new Error('Could not deserialize value');
    }

    return parseISO(value);
  }
  serializeValue(value: Date): string {
    return value.toISOString();
  }
}

Describe alternatives you’ve considered The current workaround we use is outlined in the first bullet point. Another option i considered was writing a custom plugin, but from the docs it seemed like that would require me to fork the typescript plugin since i want to modify the output of that. If theres a way to modify the output of the typescript plugin, let me know please. I sadly wasnt able to find one.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:11
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

8reactions
pganstercommented, Nov 9, 2021

I’m facing the same issue. My only idea is to serialize & deserialize date fields wherever they are read from / written to in the code which leads to a lot of boilerplate code.

@ardatan you wrote

Codegen doesn’t aim this kind of deserialization. You need to implement it as part of client library.

What do you mean with ‘as part of the client library’? How could I write a central de-serializer that codegen uses for Date or Datetime scalars? As far as I know, I can only supply types with the scalars: config. Or is the only way to serialize / deserialize the scalar fields whenever they are accessed?

2reactions
hachibeeDIcommented, Jun 29, 2021

If I understand correctly, the thing @mastorm pointing is serialization/deserialization and type safety. We can declare scalars map like DateTime: MyDateTime and it looks working fine in compile time but runtime because there is no serialization process from string to MyDateTime.

To work around, I’m using Branded Type:

// externs.ts

type Brand<T, B extends string> = T &
  {
    readonly [k in B]: symbol;
  };

export type MyDatetime = Brand<string, '__MyDatetime__'>;

/*
const dt: MyDatetime = '2011-10-05T14:48:00.000Z';  // error!: Type 'string' is not assignable to type 'MyDatetime'.
*/

and declare scalars:

      scalars:
        DateTime:  "externs#MyDateTime"

then write a serializer from MyDateTime to DateTime you would like to use (i.e. luxon).

Serializer issue still remains but type safety could be assured.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Type-safe serialization in Scala, a practical type class example
The first is called 'serialize' takes an instance of a type and returns an array of bytes. The second, called deserialize, does the...
Read more >
Kotlinx Serialization, inlining sealed class/interface [duplicate]
The answer with kotlinx deserialization: different types && scalar && arrays is basically the answer, and the one I will accept.
Read more >
Serialization - Wikipedia
In computing, serialization (or serialisation) is the process of translating a data structure or object state into a format that can be stored...
Read more >
A Thorough Guide to Bond for C++ - Microsoft Open Source
The core feature provided by Bond is the ability to serialize and deserialize instances of user-defined schemas. The serialization APIs are declared in...
Read more >
YAML properties as Scalar or Sequence? Or both?!
YAML is a data serialization standard that is intended to be human friendly. For example, it reduces the use of delimiters quite drastically ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found