Skip to content

Branded primary keys

Notice the id column:

id: z.number().brand<"public.penguins.id">(),

This uses Zod branding to create a nominal type for your primary key.

export type Id = Row["id"];

Now Id is not just a number. TypeScript will treat it as a distinct type:

function getPenguin(id: Id) {}
function getZoo(id: ZooId) {}
getPenguin(zooId); // Compilation error.

The goal is to catch situations like this at compile time:

  • Passing the ID variable as an argument to the wrong table.
  • Passing user input where a database ID is expected.
  • Using raw numbers instead of validated identifiers.

If a table contains a foreign key reference, this is also branded on your row object.

flight_attempts.ts
export const row = z.object({
id: z.number().brand<"public.flight_attempts.id">(),
penguin: z.number().brand<"public.penguins.id">(),
// ...
});