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;
}
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.