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
@Inject
constructor(
private val characterRepository: CharacterRepository
) : 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)
}
Client usage via node(id:)¶
Clients pass a Global ID to retrieve a specific entity, independent of the underlying storage key format:
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:
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.