nazolabo

フリーランスのWebエンジニアが近況や思ったことを発信しています。

ent ( entgo.io/ent ) で ULID を使う

ent では UUID 型がデフォルトでサポートされているのですが、それとは別に ULID を使う方法を解説します。

ULID の実装は https://github.com/oklog/ulid を使用します。

ent の UUID 型は、driver.Valuer Interface を利用して値を取得しますが、 oklog/ulid の Value() の実装がバイナリを返却するようになっているため、このままでは可読性が悪いです。 Value() が String を返すようにした ULID 型を新たに定義するのが良いでしょう。そのようにすると、他の interface も実装する必要があります。

結論から言うと https://github.com/ent/contrib/blob/6623819401500db45747a2419172963217cef619/entgql/internal/todopulid/ent/schema/pulid/pulid.go にそのものの実装があります1。この実装はライブラリ化されているものではない( internal )ので、そのままコピペして利用するのが良いでしょう。

使用する場合には以下のようになります。

// Fields of the User.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("id").
            GoType(pulid.ID("")).
            DefaultFunc(func() pulid.ID { return pulid.MustNew("US") }),
        field.Int("age").Positive(),
        field.String("name").Default("unknown"),
    }
}

prefixが不要な場合はprefixの部分を削ってしまうのが良いかと思います。

このままの実装でもう少し手軽に書きたい場合は、同リポジトリmixin が存在しますので、これを持ち出すと良いと思います。

ent は各フィールドを固有の型にできますので、例えば上記例で ID を UserID 型にしたい場合は以下のようになります。

type UserID pulid.ID

// Fields of the User.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("id").
            GoType(UserID("")).
            DefaultFunc(func() UserID { return UserID(pulid.MustNew("US")) }),
        field.Int("age").Positive(),
        field.String("name").Default("unknown"),
    }
}

  1. https://github.com/tmc/pulid のように見えるのですが、こちらは Value() はバイナリを返すようです。