Skip to content
/ typeid Public

TypeIDs are type-safe, globally unique identifiers based on UUIDv7 and UUIDv4 standard.

License

sumup/typeid

TypeID

Stars Go Reference CI Status License Contributor Covenant

TypeIDs are a draft standard for type-safe, globally unique identifiers based on the UUIDv7 standard. Their properties, particularly k-sortability, make them suitable primary identifiers for classic database systems like PostgreSQL. However, k-sortability may not always be desirable. For instance, you might require an identifier with high randomness entropy for security reasons. Additionally, in distributed database systems like CockroachDB, having a k-sortable primary key can lead to hotspots and performance issues.

While this package draws inspiration from the original typeid-go package (github.com/jetify-com/typeid-go), it provides multiple ID types:

  • typeid.Sortable is based on UUIDv71 and is k-sortable. Its implementation adheres to the draft standard. The suffix part is encoded in lowercase crockford base32.
  • typeid.Random is also based on UUIDv42 and is completely random. Unlike typeid.Sortable, the suffix part is encoded in uppercase crockford base32.

Please refer to the respective type documentation for more details.

Install

go get github.com/sumup/typeid

Usage

To create a new ID type, define a prefix type that implements the typeid.Prefix interface. Then, define a TypeAlias for your ID type to typeid.Random or typeid.Sortable with your prefix type as generic argument.

Example:

import "github.com/sumup/typeid"

type UserPrefix struct{}

func (UserPrefix) Prefix() string {
    return "user"
}

type UserID = typeid.Sortable[UserPrefix]

userID, err := typeid.New[UserID]()
if err != nil {
    fmt.Println("create user id:", err)
}

fmt.Println(userID) // --> user_01hf98sp99fs2b4qf2jm11hse4

Database Support

ID types in this package can be used with database/sql and github.com/jackc/pgx.

When using the standard library SQL, IDs will be stored as their string representation and can be scanned and valued accordingly. When using pgx, both TEXT and UUID columns can be used directly. However, note that the type information is lost when using UUID columns, unless you take additional steps at the database layer. Be mindful of your identifier semantics, especially in complex JOIN queries.

If using pgx with PostgreSQL, you can generate UUIDv4 (for usage with typeid.Random) as the default value for your primary key:

CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT GEN_RANDOM_UUID(),
    ...
);

For k-sortable TypeIDs (typeid.Sortable, using UUIDv7) you will have to generate the UUID in your application unless you are running PostgreSQL 18 or new where both are supported:

-- With random TypeID
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT uuidv4(),
    ...
);

-- With sortable TypeID
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT uuidv7(),
    ...
);

Using with sqlc

TypeIDs work seamlessly with sqlc by using column overrides in your sqlc.yaml configuration:

version: "2"
sql:
  - schema: "schema.sql"
    queries: "queries"
    engine: postgresql
    gen:
      go:
        package: postgres
        out: postgres
        sql_package: pgx/v5
        overrides:
          - column: users.id
            go_type:
              import: github.com/yourorg/yourproject/internal/domain
              type: "UserID"

With this configuration, sqlc will generate Go code that uses your TypeID type directly:

package domain

import "github.com/sumup/typeid"

type UserPrefix struct{}

func (UserPrefix) Prefix() string {
    return "user"
}

type UserID = typeid.Sortable[UserPrefix]

type User struct {
    ID          UserID
    Name        string
    // ... other fields
}

You can then use your TypeID types directly in your queries:

-- name: CreateUser :one
INSERT INTO users (
    id,
    name
) VALUES (
    @id,
    @name
) RETURNING *;

-- name: GetUser :one
SELECT * FROM users
WHERE id = @id;

And call them from Go:

userID := typeid.Must(typeid.New[domain.UserID]())
user, err := queries.CreateUser(ctx, postgres.CreateUserParams{
    ID:          userID,
    Name:        "Karl",
})

Using with oapi-codegen

TypeIDs can be used with oapi-codegen to generate type-safe API clients and servers. Use the x-go-type and x-go-type-import extensions in your OpenAPI specification:

paths:
  /users/{user_id}:
    parameters:
      - in: path
        name: user_id
        description: The ID of the uesr to retrieve.
        required: true
        schema:
          type: string
          example: user_01hf98sp99fs2b4qf2jm11hse4
          x-go-type: "domain.UserID"
          x-go-type-import:
            path: github.com/yourorg/yourproject/internal/domain

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier of the user.
          example: user_01hf98sp99fs2b4qf2jm11hse4
          x-go-type: "domain.UserID"
          x-go-type-import:
            path: github.com/yourorg/yourproject/internal/domain
        name:
          type: string

The generated code will use your TypeID types:

type User struct {
    Id   domain.UserID `json:"id"`
    Name string        `json:"name"`
}

TypeIDs implement encoding.TextMarshaler and encoding.TextUnmarshaler, so they work with JSON encoding/decoding in generated API code without any additional configuration.

Maintainers


Based on the go implementation of typeid found at: https://github.com/jetify-com/typeid-go by Jetify. Modifications made available under the same license as the original.

Footnotes

  1. https://datatracker.ietf.org/doc/html/rfc9562

  2. https://datatracker.ietf.org/doc/html/rfc4122

About

TypeIDs are type-safe, globally unique identifiers based on UUIDv7 and UUIDv4 standard.

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •