Search code examples
node.jstypescriptgraphqltypeormtypegraphql

Is it semantically correct to use ID scalar type for a foreign key field in a Type-graphql and Typeorm entity?


Here's the definition of String and ID from GraphQL's documentation:

GraphQL comes with a set of default scalar types out of the box:

String: A UTF‐8 character sequence.

ID: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable.

Would you use the ID type for the userId field in Article, which is the foreign key to the User entity? Or would you prefer String type for userId because the id field is the unique identifier for Article, not userId?

@ObjectType()
@Entity()
export class Article {
  @Field((type) => ID)
  @PrimaryGeneratedColumn("uuid")
  id: string;

  @Field()
  @Column({ type: "text" })
  title: string;

  @Field(type => ID) // <<= ID or String?
  @Column({type: "uuid"})
  userId: string

  @ManyToOne((type) => User, (user) => user.id)
  @JoinColumn({ name: "userId" })
  user: User;

  @Field()
  @CreateDateColumn()
  created: Date;

  @Field()
  @UpdateDateColumn()
  updated: Date;

}

@ObjectType()
@Entity()
export class User {
  @Field((type) => ID)
  @PrimaryGeneratedColumn("uuid")
  id: string;

  @Field()
  @Column({ type: "text" })
  name: string;

}

Solution

  • Semantically the scalar ID makes more sense than the scalar String since per the definition it represents a unique identifier, often used to refetch an object or as the key for a cache. It is also not meant for human consumption, another part of the definition of the scalar ID.

    The uniqueness of it is not really important from a GraphQL perspective it is very common to see multiple IDs in the same type. Using it is in fact encourage has it indicates to your consumer that it can (most likely) be used to fetch the full object.

    Now you also have to challenge the need to expose the userId at all since this is a very "REST" way of thinking and it leaks the database relational model used, making it harder to evolve your data model without making breaking changes to the schema.

    Generally you would only expose the user: User field as it already contains an id field. One might argue that it would do an unnecessary join, but this could be easily optimized with a lookahead. If a consumer needs user data, they will likely need more than just the id anyway so they will have to fetch the whole object from the database.