【Drizzle】画期的?Viewに__typeフィールドを埋め込んでセキュリティを高める
こんにちは、フリーランスエンジニアの太田雅昭です。
DrizzleのView
DirzzleではスキーマレベルでViewを作成することができます。
export const usersTable = pgTable('users', {
id: text().unique(),
handle: text().unique().$defaultFn(() => 'user_' + createId()),
email: text().unique(),
phone: text().unique(),
passwordHash: text(),
});
export const usersPublicView = pgView('users_public')
.as(qb => qb
.select({
id: usersTable.id,
handle: usersTable.handle,
})
.from(usersTable));
この例では、usersTableからidとhandleのみのViewを作成しています。便利ですね。
__typeを入れてみる
これに__typeを入れてみます。
__type: sql<'user_public'>`'user_public'`.as('__type'),
これで、Viewから生成される型に__typeフィールドが固定値’user_public’が追加されるようになります。
type UserPublic = typeof usersPublicView.$inferSelect
// type UserPublic = {
// __type: "user_public";
// id: string;
// handle: string | null;
// }
__typeの設定が複雑ですので、関数にします。
export function mkType<T extends string>(type: T) {
return { __type: sql<T>`${sql.raw(`'${type}'`)}`.as('__type') }
}
これを使うと、Viewは以下のようになります。
export const usersPublicView = pgView('users_public')
.as(qb => qb
.select({
...mkType('user_public'),
id: usersTable.id,
handle: usersTable.handle,
})
.from(usersTable));
試してみる
試してみました。
type User = typeof usersTable.$inferSelect
type UserPublic = typeof usersPublicView.$inferSelect
const showUser = (user: User) => {
console.log(user);
}
const showUserPublic = (user: UserPublic) => {
console.log(user);
}
const user: User = {} as User;
const userPublic: UserPublic = {} as UserPublic;
showUser(user);
showUser(userPublic); // 型エラー
showUserPublic(user); // 型エラー
showUserPublic(userPublic);
うまい具合に、型レベルで漏洩を検知することができるようになりました。意図しない挿入が防止できています。これは使えそうですね。
おまけ: Remedaを入れてみる
RemedaでViewをさらに型安全に書けます。
import * as R from 'remeda';
export const usersPublicView = pgView('users_public')
.as(qb => qb
.select({
...mkType('user_public'),
...R.pick(usersTable, ['id', 'handle']),
})
.from(usersTable));
注意: バージョンによっては使えない
Drizzleのバージョンによっては、Viewの生成でエラーになります。2025年8月23日 v0.44.3時点ではまだ解決していないようです。
https://github.com/drizzle-team/drizzle-orm/issues/4731
代替案として、今回はViewを作成せず、下記のSelectの場合の手法を絡めて対応しました。queryでも使えます。将来的にFixされれば、Viewの方がシンプルでいいかと思います。ただqueryでの書きやすさも捨て難いです。