TypeORM adapter
@smonn/ids/typeorm provides a column transformer for integrating Id<Brand>
with TypeORM’s @Column decorator transformer option. It requires typeorm
as an optional peer dependency.
pnpm add typeormimport { idTransformer } from "@smonn/ids/typeorm";import { createTimestampId } from "@smonn/ids";import type { Id } from "@smonn/ids";import { Column, Entity } from "typeorm";
const usr = createTimestampId("usr");
@Entity()class User { @Column({ type: "text", transformer: idTransformer(usr) }) id!: Id<"usr">;}idTransformer(codec) works with any codec variant.
- Write path:
tovalidates the value viacodec.safeParsebefore passing it to the driver. A cast-smuggled or otherwise invalid string throwsIdsError("invalid_id")at write time. Passingnullorundefinedalso throws — usenullableIdTransformerfor nullable columns. - Read path: values are normalised via
codec.safeParse(). An unrecognised value throws at read time so corrupt data surfaces immediately.
Auto-generating IDs on insert — beforeInsertHook
Section titled “Auto-generating IDs on insert — beforeInsertHook”beforeInsertHook(fieldName, codec) returns a function suitable for use inside a TypeORM @BeforeInsert() lifecycle hook. It auto-generates an Id<Brand> for fieldName when the field is absent (null or undefined) on the entity at insert time; if the field already has a value it is left unchanged.
Pair it with idTransformer on the same column: idTransformer handles the database read/write path; beforeInsertHook handles generation.
import { idTransformer, beforeInsertHook } from "@smonn/ids/typeorm";import { createTimestampId } from "@smonn/ids";import type { Id } from "@smonn/ids";import { BeforeInsert, Column, Entity } from "typeorm";
const usr = createTimestampId("usr");const fillUserId = beforeInsertHook("id", usr);
@Entity()class User { @Column({ type: "text", transformer: idTransformer(usr) }) id!: Id<"usr">;
@BeforeInsert() generateId() { fillUserId(this); }}beforeInsertHook requires IdGeneratingCodec — a codec that exposes a synchronous generate(). Only the Timestamp codec and Reverse Timestamp codec qualify; Opaque, Signed, Wrapped, and Digest codecs are a compile-time error. For those codecs, generate the ID explicitly at the call site and assign it before persisting.
Nullable columns
Section titled “Nullable columns”nullableIdTransformer(codec) returns a TypeORM ValueTransformer whose from returns null for null / undefined database values and whose to normalises null and undefined to null. Use it for optional foreign keys.
import { nullableIdTransformer } from "@smonn/ids/typeorm";import { createTimestampId } from "@smonn/ids";import type { Id } from "@smonn/ids";import { Column, Entity } from "typeorm";
const usr = createTimestampId("usr");
@Entity()class Post { @Column({ type: "text", nullable: true, transformer: nullableIdTransformer(usr) }) authorId!: Id<"usr"> | null;}- Read path (
from): returnsnullfornull/undefineddatabase values. Non-null values go throughcodec.safeParse()and throwIdsError("invalid_id")if they do not parse as a validId<Brand>. - Write path (
to):nullandundefinedare normalised tonull; non-null values are validated viacodec.safeParseand an invalid string throwsIdsError("invalid_id")at write time.
Error handling
Section titled “Error handling”The read path throws IdsError with code "invalid_id" when the stored value does not parse
as a valid Id<Brand>. The underlying ParseError is attached as err.cause. Catch and
narrow using isIdsError:
import { idTransformer, isIdsError } from "@smonn/ids/typeorm";import { createTimestampId } from "@smonn/ids";
const usr = createTimestampId("usr");const transformer = idTransformer(usr);
try { const id = transformer.from(row.id);} catch (err) { if (isIdsError(err) && err.code === "invalid_id") { // err.cause is the ParseError returned by safeParse }}IdsError, isIdsError, and IdsErrorCode are re-exported from @smonn/ids/typeorm — no
separate import from "@smonn/ids" is needed. For the full list of IdsErrorCode values, see
the error-code reference.