Global IDs
Global IDs in Viaduct combines two pieces of information:
- Type: the GraphQL type name (for example, “Character”, “Film”, “Planet”).
- Internal ID: your application’s internal identifier for that entity.
Format and encoding
The raw form is "<Type>:<InternalID>", which is then base64-encoded.
// Encoded form for Character with internal ID "1":
val gid: String = Character.Reflection.globalId("1") // "Q2hhcmFjdGVyOjE="
When building objects in resolvers, use the execution context helper to attach a typed Global ID:
viaduct.api.grts.Starship.Builder(ctx)
.id(ctx.globalIDFor<viaduct.api.grts.Starship>(starship.id))
.name(starship.name)
Treat Global IDs as opaque. They are intended for retrieval via
nodequeries, not as human-facing identifiers.
Using Global IDs in node resolvers
Node resolvers receive a parsed Global ID; use the internal ID to load the entity:
@Resolver
class CharacterNodeResolver : NodeResolvers.Character() {
// Node resolvers can also be batched to optimize multiple requests
// tag::node_batch_resolver_example[21] Example of a node resolver
override suspend fun batchResolve(contexts: List<Context>): List<FieldValue<Character>> {
// Extract all unique character IDs from the contexts
val characterIds = contexts.map { it.id.internalID }
// Perform a single batch query to get film counts for all characters
// We only compute one time for each character, despite multiple requests
val characters = characterIds.mapNotNull {
CharacterRepository.findById(it)
}
// For each context gets the character ID and map to the viaduct object
return contexts.map { ctx ->
val characterId = ctx.id.internalID
Client usage via node(id:)
Clients pass a Global ID to retrieve a specific entity, independent of the underlying storage key format:
query ($id: ID!) {
node(id: $id) {
... on Character {
id
name
}
}
}
Schema hinting with @idOf
Annotate ID fields and arguments with @idOf to bind them to a concrete GraphQL type, enabling type-safe handling in
resolvers and tooling:
"""
Search by character ID
"""
byId: ID @idOf(type: "Character")
type Character implements Node @scope(to: ["default"]) @resolver {
"""
The GlobalID of this character - uniquely identifies this Character in the graph (internal use only)
"""
id: ID!
Do and don’t
- Do treat Global IDs as opaque and stable across the API surface.
- Do generate them in resolvers using
ctx.globalIDForor<Type>.Reflection.globalId(...). - Do use
@idOfon schema fields/arguments carrying Global IDs. - Don’t expose internal IDs or rely on clients decoding base64.
- Don’t embed business logic or access control information in IDs.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.