Scalars
Aliases
TypeGraphQL provides aliases for 3 basic scalars:
- Int --> GraphQLInt;
- Float --> GraphQLFloat;
- ID --> GraphQLID;
This shorthand allows you to save keystrokes when declaring field types:
// import the aliases
import { ID, Float, Int } from "type-graphql";
@ObjectType()
class MysteryObject {
  @Field(type => ID)
  readonly id: string;
  @Field(type => Int)
  notificationsCount: number;
  @Field(type => Float)
  probability: number;
}
In the last case you can omit the type => Float since JavaScript Number will become GraphQLFloat in the schema automatically.
Other scalars - i.e. GraphQLString and GraphQLBoolean - do not need aliases. When possible, they will be reflected automatically:
@ObjectType()
class User {
  @Field()
  name: string;
  @Field()
  isOld: boolean;
}
However in some cases we must explicitly declare the string/bool scalar type. Use JS constructor functions (String, Boolean) then:
@ObjectType()
class SampleObject {
  @Field(type => String, { nullable: true })
  // TS reflected type is `Object` :(
  get optionalInfo(): string | undefined {
    if (Math.random() > 0.5) {
      return "Gotcha!";
    }
  }
}
Date Scalars
TypeGraphQL provides built-in scalars for the Date type. There are two versions of this scalar:
- timestamp based ("timestamp") -1518037458374
- ISO format ("isoDate") -"2018-02-07T21:04:39.573Z"
They are exported from the type-graphql package as GraphQLISODateTime and GraphQLTimestamp.
By default, TypeGraphQL uses the ISO date format, however you can change it in the buildSchema options:
import { buildSchema } from "type-graphql";
const schema = await buildSchema({
  resolvers,
  dateScalarMode: "timestamp", // "timestamp" or "isoDate"
});
There's no need then to explicitly declare the field type:
@ObjectType()
class User {
  @Field()
  registrationDate: Date;
}
Custom Scalars
TypeGraphQL also supports custom scalar types!
First of all, we need to create our own GraphQLScalarType instance or import a scalar type from a 3rd-party npm library. For example, Mongo's ObjectId:
import { GraphQLScalarType, Kind } from "graphql";
import { ObjectId } from "mongodb";
export const ObjectIdScalar = new GraphQLScalarType({
  name: "ObjectId",
  description: "Mongo object id scalar type",
  serialize(value: unknown): string {
    // check the type of received value
    if (!(value instanceof ObjectId)) {
      throw new Error("ObjectIdScalar can only serialize ObjectId values");
    }
    return value.toHexString(); // value sent to the client
  },
  parseValue(value: unknown): ObjectId {
    // check the type of received value
    if (typeof value !== "string") {
      throw new Error("ObjectIdScalar can only parse string values");
    }
    return new ObjectId(value); // value from the client input variables
  },
  parseLiteral(ast): ObjectId {
    // check the type of received value
    if (ast.kind !== Kind.STRING) {
      throw new Error("ObjectIdScalar can only parse string values");
    }
    return new ObjectId(ast.value); // value from the client query
  },
});
Then we can just use it in our field decorators:
// import the earlier created const
import { ObjectIdScalar } from "../my-scalars/ObjectId";
@ObjectType()
class User {
  @Field(type => ObjectIdScalar) // and explicitly use it
  readonly id: ObjectId;
  @Field()
  name: string;
  @Field()
  isOld: boolean;
}
Optionally, we can declare the association between the reflected property type and our scalars to automatically map them (no need for explicit type annotation!):
@ObjectType()
class User {
  @Field() // magic goes here - no type annotation for custom scalar
  readonly id: ObjectId;
}
All we need to do is register the association map in the buildSchema options:
import { ObjectId } from "mongodb";
import { ObjectIdScalar } from "../my-scalars/ObjectId";
import { buildSchema } from "type-graphql";
const schema = await buildSchema({
  resolvers,
  scalarsMap: [{ type: ObjectId, scalar: ObjectIdScalar }],
});
However, we must be aware that this will only work when the TypeScript reflection mechanism can handle it. So our class property type must be a class, not an enum, union or interface.
