Skip to content

Instantly share code, notes, and snippets.

@oldmonad
Forked from MendyLanda/drizzle-ulid.md
Created May 9, 2025 13:44
Show Gist options
  • Select an option

  • Save oldmonad/2a6200211b4e5aafbb71b42e3e7d24d6 to your computer and use it in GitHub Desktop.

Select an option

Save oldmonad/2a6200211b4e5aafbb71b42e3e7d24d6 to your computer and use it in GitHub Desktop.
Implementing Efficient Binary ULID Storage in MySQL with Drizzle ORM

For anyone considering the use of ULIDs in MySQL with drizzle, here's a ready-to-use ULID type for your convenience.

import { Ulid as ULID } from "id128";

export const ulid = customType<{
  data: string;
  notNull: true;
  default: false;
  driverData: Buffer;
  config: never;
}>({
  dataType() {
    return "binary(16)";
  },
  toDriver(value) {
    return sql`UNHEX(${ULID.fromCanonical(value).toRaw()})`;
  },
  fromDriver(value) {
    return ULID.fromRawTrusted(value.toString("hex")).toCanonical();
  },
});

The challenging aspect was ensuring that drizzle and MySQL worked seamlessly when storing ULIDs in a binary(16) format.

Note: MySQL only provides binary conversion functions for UUIDs, and the built-in binary type in drizzle is known to be problematic .

For ease of use, you might consider wrapping this further:

export const ulidPk = (dbName: string) =>
  ulid(dbName)
    .primaryKey()
    .$default(() => ULID.generate().toCanonical());

Note the dependency on id128 for ULID functionality. Chosen as it's currently the only library supporting binary ULIDs.

Currently, the only caveat is that updates and deletes will not function with Drizzle Studio, as it bypasses the toDriver method in the custom type definition. This limitation is likely to persist until the underlying behavior is modified. Keep in mind, issues only arise when storing ULIDs in binary; string storage is an alternative if you don't care about it, but here's why you should.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment