flashman
← All guides

UUID contracts across APIs and databases

Use UUIDs consistently across text, JSON, binary storage, database drivers, validation, indexing, logging, authorization, and version-aware application contracts.

2026-08-29 · 8 min read

  • uuid
  • api-design
  • database

A UUID is a 128-bit identifier, but applications encounter several representations: canonical hyphenated text, compact hexadecimal, braces, database-native values, and raw bytes. The identifier stays the same only when every conversion preserves those bits in the same order.

A clear contract avoids accidental regeneration, validation disagreements, and driver-specific byte-order bugs. It also separates identifier syntax from resource existence and authorization.

Standardize the API boundary

Use the lowercase hyphenated textual form in JSON unless an established protocol requires another representation. Accept legacy forms only when compatibility is intentional, normalize once, and emit one canonical form.

  • Document allowed UUID versions rather than assuming version 4.
  • Reject wrong length, invalid hexadecimal, and misplaced separators.
  • Do not trim or rewrite arbitrary surrounding text.
  • Treat IDs as opaque outside services that own their generation policy.

Test database and driver conversions

Prefer a database-native UUID type where available for validation and predictable comparisons. If compact binary storage is necessary, specify byte ordering and test it across every language driver that reads the column.

Text sorting, binary sorting, and creation-time ordering are separate concerns. If index locality matters, choose an identifier strategy deliberately and verify how the database orders its stored representation.

Keep validation and authorization separate

Parsing proves only that input has an accepted UUID shape. It does not prove that the record exists, belongs to the current tenant, or can be disclosed to the caller.

  • Use parameterized queries with typed UUID parameters.
  • Apply tenant and authorization predicates during lookup.
  • Choose consistent not-found behavior that does not leak resource existence.
  • Log correlation IDs without attaching unnecessary personal data.

Build representation fixtures

Use Flashman's UUID tool to generate public fixtures, JSON formatter to inspect string boundaries, diff tool to compare normalized payloads, case converter for adjacent field-name work, and number-base converter to reason about hexadecimal groups. Never replace a production identifier merely to make parsing succeed.

Keep test vectors for canonical text, uppercase input if supported, compact or braced legacy forms, malformed values, allowed versions, database round trips, and cross-language serialization. Assert bit equality, not only visually similar output.

Try these tools