データベースツール
TypeScript ORM、クエリビルダー、データベースライブラリ トップ 8:型安全性の評価
はじめに
TypeScript ORM が提供する型安全性のレベルをすぐに評価するのは時間がかかる場合があります。この記事では、Node.js ORM、クエリビルダー、データベースライブラリ トップ 11 (2022 年) で検討されたライブラリの型安全性を簡単に評価します。
この記事で検討されているすべてのライブラリには、API 用の TypeScript バインディングがありますが、実際に提供する型安全性のレベルは大きく異なります。Waterline のように、エラーなしでコンパイルされるものの、any
型を自由に渡して、あらゆる種類の型チェックをスキップするものもあれば、Prisma のように、戻りデータの形状を変更する部分クエリなどの高度な機能に対して完全な型安全性を持つものもあります。
この記事では、以下について説明します。
- ソース: ライブラリの型定義は公式に組み込まれているか、DefinitelyTyped @types リポジトリから提供されていますか?
- レコード作成: モデルは型安全ですか?また、レコードは型安全な方法で作成できますか?
- レコードフェッチ: データをフェッチするとき、オブジェクトは型安全ですか?部分モデルやリレーションの場合でも?
この記事では、TypeScript と型安全性についてある程度の知識があることを前提としています。詳細については、公式の TypeScript ドキュメントを参照してください。また、ORM とクエリビルダーについてもある程度の知識があることを前提としています。これらのデータベースツールについて詳しくは、Prisma の データガイド からの SQL、クエリビルダー、ORM の比較 を参照してください。
注: この記事は、もともと 2020 年 10 月 2 日に公開されました。最後に更新されたのは 2022 年 2 月 15 日です。
Prisma
評価サマリー
- 型定義: 組み込み
- レコード作成: 型安全
- レコードフェッチ: 型安全
概要
Prisma は、モデルがクラスではなく Prisma スキーマ で定義される点で、ほとんどの ORM と異なります。Prisma スキーマは、Prisma ツールキットで使用されるメインの構成ファイルおよびデータモデル定義ファイルです。Prisma スキーマでは、PostgreSQL データベースなどのデータソースと、users
や posts
などのモデル、およびそれらの間のリレーションを定義します。このスキーマを使用して、Prisma は、データベースをクエリするために使用する、型安全な クライアント を生成します。この Prisma クライアントは、Node.js アプリで使用して、モデルクラスのインスタンスではなく、プレーンな JavaScript オブジェクトを返すことができる、リッチなクエリビルダーとして機能します。
Prisma とは?
Prisma は新しい ORM であり、いくつかのイテレーションと再設計を経てきました。そのユニークなスキーマ中心のアーキテクチャは、モデルを定義するためにクラスを使用する一般的な ORM とは対照的です。これにより、開発者は JavaScript Node.js アプリケーションでも型安全性の利点の一部を得ることができます。Prisma の型安全性の詳細については、Prisma のゼロコスト型安全性による生産的な開発 を参照してください。
Prisma が優れた選択肢であると私たちが考える理由について詳しく知りたい場合は、Prisma を選ぶ理由 ページをご覧ください。
型定義:組み込み
Prisma クライアントの型定義は、クライアントを生成するときに自動生成されます。Prisma スキーマで定義されたモデル(User
や Post
など)は、生成された index.d.ts
ファイルで型として自動的にエクスポートされ、データをクエリするときに完全な型安全性をすぐに有効にできます。
レコード作成:型安全
Prisma で新しいレコードを作成するときに、モデルで定義されていないプロパティを追加しようとすると、型エラーが発生します。モデルプロパティはオートコンプリートされます。さらに、ネストされた書き込みも型安全です。ネストされた書き込みは、リレーションを使用して複数のテーブルにデータを挿入します。これは、同じ prisma.user.create()
呼び出しを使用して User
とネストされた Post
を作成するときに、Post
モデルフィールドも型チェックおよびオートコンプリートされ、ネストされたレコードも有効であることが保証されることを意味します。
レコードフェッチ:型安全
データベースからレコードをフェッチすると、リレーションクエリの場合でも、戻りオブジェクトは完全に型指定されます。たとえば、データベースからすべてのユーザーをフェッチし、ユーザーのすべての投稿をさらにフェッチするために投稿リレーションを含めると、型は (User & {posts: Post[];})[]
として推論されます。さらに、オートコンプリートは、フェッチされたリレーションを追加するために include
を使用する場合にも機能するため、この記事で検討されている多くのライブラリに欠けている機能である、存在しないリレーションをクエリすることはできません。
Prisma に組み込まれている型安全性のレベルをさらに示すために、特定のプロパティのみがクエリされ、戻りオブジェクトの型が変更される部分クエリを考えてみましょう。
const usersWithPartialPosts = await prisma.user.findMany({include: {posts: {select: {title: true,published: true,},},},})
このクエリでは、すべてのユーザーが返されますが、posts
リレーションモデルでは、title
フィールドと published
フィールドのみが選択されます。usersWithPartialPosts
は、次のように型指定されます。
(User & {posts: {title: string;published: boolean;}[];})[]
これは、content
のように、選択されなかった post
フィールドにアクセスしようとすると失敗することを意味します。Prisma は、この記事で検討されている ORM ライクなライブラリの中で、この粒度の型安全性を実現できる唯一のものです。
型安全性:強力
データモデルをエンコードするローカル CRUD クライアントを生成するという Prisma のユニークな設計により、TypeScript ORM の中で比類のないレベルの型安全性を実現できます。Prisma を使用してデータベースからデータを操作およびクエリすると、ネストされたリレーションクエリと、返されたモデルの形状を変更する部分クエリに対して正確な型指定ができます。
Sequelize
評価サマリー
- 型定義: 組み込み
- レコード作成: 型安全ではない
- レコードフェッチ: 型安全ではない
概要
Sequelize とは
Sequelize は、確立された成熟した Promise ベースの Node.js ORM であり、PostgreSQL、MySQL、MariaDB、SQLite、Microsoft SQL Server をサポートしています。ベースの Model
クラスを拡張してモデルを定義する、従来の ORM ActiveRecord パターンに従います。SELECT
や INSERT
などの操作は、クラスメソッドを使用して実行されます。リレーションも、hasMany()
や belongsTo()
などのクラスメソッドを使用して定義されます。JavaScript コミュニティで非常に人気があり、長い間存在しています。ただし、プロジェクトは最近停滞しており、かつてほど活発ではないようです。
Prisma と Sequelize のより焦点を絞った比較については、Sequelize 比較ページ をご覧ください。
型定義:組み込み
v5 以降(執筆時点では、Sequelize は v6.16.1 です)、Sequelize には組み込みの型定義が含まれています。これより前は、型定義は @types
経由で利用可能でした。Sequelize はもともと JavaScript ORM として設計されており、TypeScript サポートは近年追加されました。
レコード作成:型安全ではない
Sequelize は、モデルプロパティの厳密な型チェックをすぐに提供しません。これを実装するには、開発者は、リレーションの CRUD メソッドの interfaces
、クラス、定義を含む 重要でない 定型コードを記述する必要があります。複数のリレーションを持つ複雑なデータモデルの場合、これはすぐに面倒で扱いにくくなる可能性があります。モデルに追加されたミックスインまたはネストされたモデルを使用してレコードを作成する場合、型定義を提供するのは再び開発者の責任です。
Sequelize では、属性の型チェックなしでモデルを定義することもできます。このアプローチを使用すると、Sequelize と TypeScript をすぐに使い始めることができますが、データを操作するときにすべての型安全性が失われます。
レコードフェッチ:型安全ではない
Sequelize では、モデル属性の厳密な型チェックと緩い型チェックの両方が許可されているため、開発者が必要な型定義をすべて提供した場合にのみ、コンパイラーはクエリを正しく型チェックします。さらに、include
を使用してアソシエーションをフェッチする場合、戻り値の型にはフェッチされたデータのネストされた形状に関する情報は含まれていません。エラーなしで適切にコンパイルするには、開発者は !
非 null アサーションと rejectOnEmpty
パラメーターを使用してコンパイラーをオーバーライドする必要があります。
型安全性:弱い
v5 以降、Sequelize は組み込みの型定義を提供しますが、モデルとレコードを操作するときに何らかの実際の型安全性を持たせるためには、インターフェイスを記述し、アソシエーションとクラスの型指定を完全に定義するのは開発者の責任です。すぐに使える状態では、あまり型安全性が提供されていません。
TypeORM
評価サマリー
- 型定義: 組み込み
- レコード作成: 型安全
- レコードフェッチ: 部分的に型安全
概要
TypeORM とは?
TypeORM は、Hibernate の影響を受けた JavaScript および TypeScript ORM であり、Node.js、Web ブラウザー、Cordova などの複数のプラットフォームで実行できます。TypeScript と型安全性を念頭に置いて構築されており、メインの ORM アーキテクチャパターンであるデータマッパーとアクティブレコードの両方をサポートし、開発者に 2 つの選択肢を提供する柔軟性を提供します。また、クエリビルダーも含まれており、多くの一般的なデータベースをサポートしています。
Prisma と TypeORM のより焦点を絞った比較については、TypeORM 比較ページ をご覧ください。
型定義:組み込み
TypeORM は、TypeScript での使用を明示的に設計された TypeScript ファーストの ORM です。型はライブラリに組み込まれており、モデルを定義するときにデコレーターなどの TypeScript 機能を利用します。
レコード作成:型安全
TypeORM では、モデルは Entity
クラスを使用して定義されます。@Entity()
デコレーターでモデルクラス(User
など)をデコレートし、id
や name
などのプロパティを @PrimaryGeneratedColumn()
や @Column
などの列デコレーターでデコレートします。DataMapper パターンを使用している場合、レコードは、型安全になったモデルクラスの新しいインスタンスを作成し、そのプロパティを設定することで定義されます。レコードは、モデル固有の Repository
オブジェクトを使用して保存されます。これも型指定されています。
ネストされた書き込みは、関連クラスのインスタンス(たとえば、User
の Post
)を作成し、User
オブジェクトと Post
オブジェクトの両方を保存することで実現されます。cascade
機能を使用すると、これは 1 回の save
呼び出しで実行できます。TypeORM では、モデルの型安全性がすぐに利用できます。
クエリビルダーを使用すると、モデルプロパティも型チェックされます。
await conn.createQueryBuilder().insert().into(User).values([{ firstName: 'Timber', lastName: 'Saw' },{ firstName: 'Phantom', lastName: 'Lancer' },]).execute()
User
クラスに firstName
フィールドがない場合、コンパイラーはエラーを発行します。
クエリビルダーでリレーションを使用すると、型安全性が損なわれます。以下はコンパイラーエラーを発行しません。
await conn.createQueryBuilder().relation(User, 'postsssss').of(user).add(post)
有効な postssss
リレーションがない場合でも。
レコードフェッチ:部分的に型安全
データベースからレコードをフェッチする方法は数多くあります。型指定されたモデル固有のRepository
オブジェクトを使用すると、開発者はリポジトリのメソッド(userRepo.find()
など)を呼び出します。ここで、戻り値の型はUser[]
と正しく推論されます。
userRepo.find({relations: ["posts"]});
のようにリレーションを含める場合でも、戻り値の型は依然としてUser[]
と推論され、コンパイラは含まれるリレーションを認識しません。 user.posts
プロパティには、開発者が防御的な方法でアクセスする必要があります。
ビルトインのクエリビルダーを使用すると、次のようなクエリはUser
型として型付けされます
const firstUser = await conn.getRepository(User).createQueryBuilder('user').where('user.id = :id', { id: 1 }).getOne()
そして、次のようなクエリでは
const user = await conn.manager.findOne(User, 1)user.photos = await getConnection().createQueryBuilder().relation(User, 'photos').of(user) // you can use just post id as well.loadMany()
user.photos
の型はPhoto[]
です。
型安全性:強い
TypeORMは、モデル周りの型安全性が高いTypeScript ORMです。そのクエリビルダーも、高いレベルの型安全性を備えています。リレーションの型安全性はそれほど厳密ではなく、この制限に対して防御的にプログラミングするのは開発者の責任です。
Bookshelf.js
評価のまとめ
- 型定義: @types
- レコード作成: 型安全ではない
- レコードフェッチ: 型安全ではない
概要
Bookshelf.jsとは?
Bookshelf.jsは、Knex.jsクエリビルダーライブラリの上に構築されたNode.js ORMです。データマッパーORMパターンに触発されており、データのモデリングと対話のための簡素化されたインターフェースを提供します。 Bookshelf.jsは、データモデリング、クエリ、および操作ツールの標準セットを提供します。 Knex.jsクエリビルダーの上に構築されているため、インターフェースによって制限されていると感じた場合は、いつでもより複雑なクエリを記述できます。この記事で検討されている他のツールほど活発なプロジェクトではありませんが、長い間存在しており、その合理化されたスタイルを好むコアユーザーベースがあります。
型定義: @types
Bookshelf.jsの型定義は、TypeScript型定義のDefinitely Typedリポジトリにあります。これらはライブラリに組み込まれていません。
レコード作成:型安全ではない
Bookshelf.jsモデルは、bookshelf.Model
クラスを拡張するか、モデル名とテーブル名を指定してbookshelf.model()
を呼び出すことによって作成されます。テーブルとスキーマは事前に作成する必要があり、これらのモデル内で定義されていません。たとえば、テーブル名users
に対応するUser
モデルを作成した後、name
プロパティを設定するには、開発者はuser.set('name', 'Joe')
を呼び出します。name
列がusers
テーブルに存在しない場合、この呼び出しはランタイム時に失敗します。そのため、Bookshelfでのモデル作成は、デフォルトでは型安全ではありません。渡されるほとんどのオブジェクトの型はany
です。
レコードフェッチ:型安全ではない
上記を考慮すると、データベースからレコードをフェッチすることも型安全ではないことは驚くことではありません。const user = await User.where({'name': 'Joe'}).fetch();
を使用してuser
レコードをフェッチすると、結果の型はany
になります。fetch()
内でwithRelated
を使用してリレーションを含めても、これは変わりません。where()
句のクエリパラメータは型チェックされず、データベースに存在しない列を含めると、コンパイルはパスしますが、ランタイム時に失敗します。
型安全性:弱い
Bookshelf.jsには@types
型定義がありますが、これらはTypeScriptコードをエラーなしでコンパイルするための最低限のものを提供します。強力なTypeScriptサポートを備えたKnex.jsベースのORMのようなライブラリを使用したい場合は、Objection.jsとMikroORMの両方が徹底的な型安全性を提供し、より適切にサポートおよびメンテナンスされています。
Objection.js
評価のまとめ
- 型定義: 組み込み
- レコード作成: 型安全
- レコードフェッチ: 部分的に型安全
概要
Objection.jsとは?
Objection.jsは、ORMというよりも「リレーショナルクエリビルダー」であると自称しています。 Bookshelf.jsと同様に、強力なKnex.jsクエリビルダーライブラリの上に構築されており、常にドロップダウンできる柔軟なクエリビルダーの上にORMのような機能を構築します。 Objection.jsは、Bookshelf.jsよりも活発にメンテナンスされ、ドキュメントも充実しているようで、本番環境でobjection.jsを使用しているのは誰ですか?によると、多くのObjection.js開発者は以前にBookshelf.jsで働いていました。
型定義:組み込み
Objection.jsは、組み込みのTypeScriptサポートを提供します。 Bookshelf.jsと同様に、Objection.jsはJavaScriptライブラリとして始まり、TypeScriptの人気と採用の増加に伴い、型定義が後から追加されました。ただし、Bookshelf.jsとは異なり、Objection.jsはモデルとクエリを扱う際に徹底的な型安全性を提供します。
レコード作成:型安全
モデルは、Model
クラスを拡張することにより、Objection.jsで定義されます。たとえば、User
モデル内で、開発者はname!
やage?
などのnull許容ではないプロパティとオプションのプロパティを定義し、必須のtableName
プロパティを提供します。開発者は、モデル検証のためにオプションのJSONスキーマを提供することもできます。 HasMany
のような他のモデルへのリレーションも、モデルクラスで定義されます。
新しいレコードを作成する場合、User.query().insert()
メソッドは型安全です。モデルプロパティは自動補完され、モデルクラスで定義されていないプロパティを追加しようとすると、コンパイラエラーが発生します。
リレーションの新しいレコードを作成する場合(たとえば、User
の新しいPost
)、開発者はuser.$relatedQuery('posts').insert()
呼び出しを使用します。これも型安全であり、posts
を存在しないモデルまたはリレーションに置き換えることはできますが、チェーンされたinsert
呼び出しはコンパイラエラーを出力します。モデルプロパティはinsert()
コマンド内で自動補完され、未定義のPost
プロパティを含めると、コンパイラエラーが発生します。
ネストされた書き込みは、insertGraph()
操作を使用して行うこともできます
const user = await User.query().insertGraph({firstName: 'Sylvester',lastName: 'Stallone',posts: [{title: 'My first post',},],})
この操作も型安全であり、モデルプロパティはネストされたモデルに対して自動補完されます。
レコードフェッチ:部分的に型安全
データベースからレコードをフェッチすると、クエリと戻りオブジェクトは型付けされます。relatedQuery
を使用してリレーションをフェッチすると、リレーションの戻り値の型も正しく推論されます。次の例では、postsの戻り値の型はPost[]
です
const posts = await User.relatedQuery('posts').for(1).orderBy('title')console.log(posts[0].name)
'posts'
の代わりに、存在しないモデルまたはリレーションを入力すると、モデルプロパティにアクセスしようとするまで、コンパイラはエラーを発行しません。この時点で、コンパイラはProperty does not exist
エラーを出力します。
遅延ロードとwithGraphFetched()
メソッドを使用すると、リレーションデータが同時にロードされ、上記のコードスニペットは次のようになります
const userWithPosts = await User.query().findById(1).withGraphFetched('posts');console.log(userWithPosts.posts![0].title);
この場合、userWithPosts
の型はUser
として推論されます。非nullアサーションが含まれていない限り、コンパイラは投稿のtitle
プロパティにアクセスしようとすると、Object is possibly undefined
エラーを発行します。
'posts'
の代わりに、存在しないモデルまたはリレーションを入力すると、コンパイラはエラーを発行しません。たとえば、次のコードはコンパイラに従って有効になります
const userWithPosts = await User.query().findById(1).withGraphFetched('postssss')
型安全性:強い
MikroORMおよびBookshelf.jsとともに、Objection.jsはKnex.jsクエリビルダーを中心に構築されたORMのようなライブラリです。そのTypeScriptサポートと型安全性は、Bookshelf.jsよりもはるかに強力であり、MikroORMの型安全性に匹敵します。これは、強力なTypeScript型付けを備えた、簡素化された最小限のORMのようなライブラリを求める開発者にとって強力な選択肢です。
MikroORM
評価のまとめ
- 型定義: 組み込み
- レコード作成: 型安全
- レコードフェッチ: 型安全
概要
MikroORMとは?
MikroORMは、バニラJavaScriptをサポートする、より新しいTypeScript ORMです。 GitHubで非常に活発であり、開発者によって強力にサポートされている、急速に成長しているプロジェクトです。 Doctrine(PHP ORM)の影響を受けており、データマッパー、アイデンティティマップ、およびユニットオブワークの影響を受けたORMです。その機能の一部には、自動トランザクション処理、複数のデータベースのサポート、組み込みのKnex.jsベースのクエリビルダー、およびスキーマおよびエンティティジェネレーターが含まれます。
型定義:組み込み
TypeScriptファーストのORMとして、MikroORMは独自の広範な型定義セットを組み込んでいます。
レコード作成:型安全
MikroORMでモデルを定義するには、モデルのプロパティが宣言され、型付けされ、@Property
およびリレーションデコレータで装飾されているBaseEntity
クラスを拡張します。これらのクラスを定義すると、これらのモデルクラスのインスタンスを作成することにより、型安全な方法でレコードを作成できます。モデルフィールドは型チェックされ、自動補完されます。リレーションでリンクされたモデルは、persistAndFlush()
を使用してトランザクション内で同時に永続化できます。例:
const user = new User('Dave Johnson', 'dave@johns.on')user.age = 14const post1 = new Post("Dave's First Post", user)const post2 = new Post("Dave's Second Post", user)// Persist the post, author will be automatically cascade persistedawait DI.em.persistAndFlush([post1, post2])
ここで、Post
モデルはコンストラクターでtitle
とUser
を必要とし、これらが提供されない場合、レコード作成は失敗します。プロパティ(例:post1.author.title
)を使用して、投稿の作成者オブジェクトにアクセスできます。
レコードフェッチ:型安全
MikroORMは、データベースからレコードをフェッチする際にも強力な型安全性を提供します。レコードは、EntityRepositoriesまたはEntityManagerを使用してフェッチできます。
特定のモデル(たとえば、userRepository
)のリポジトリを使用してレコードをフェッチすると、戻りオブジェクトは型付けされ、モデルで定義されていないプロパティに基づいてクエリを実行することはできません。さらに、リレーションを含めると、オブジェクトの型には、どのリレーションがロードされたかが反映されます。たとえば、User
モデルがPost
モデルとItem
モデルにリンクされている場合、次のコマンド
const UserWithPosts = await DI.userRepository.findOne(1, ['posts'])
次の型になります
const UserWithPosts: (User & {posts: LoadedCollection<Post, Post>;items: Collection<Item, unknown>;}) | null
ここでは、投稿はロードされましたが、アイテムはロードされなかったことがわかります。 1つの制限は、findOne
リレーション配列では、コンパイラからのエラー出力なしに、存在しないリレーションに対応する追加の文字列を追加できる(たとえば、配列に「postsss」を追加する)ことです。さらに、コンパイラからのエラーなしに、明示的に設定されていない場合でも、リレーションにアクセスできます。
同様のレベルの型安全性が、次の例のように、EntityManager
のfind()
またはfindOne()
関数を使用する場合にも適用されます
const userWithPosts = await DI.em.findOne(User, { email: 'dave@johns.on' }, ['posts'])
型は再び次のように推論されます
const user: (User & {posts: LoadedCollection<Post, Post>;items: Collection<Item, unknown>;}) | null
型安全性:強い
MikroORMは、柔軟なKnex.jsクエリビルダーも搭載した強力なORMです。 Knex.jsの結果は、EntityManager.map()
(ユニークで強力な機能)を使用してモデルにマッピングできます。モデルとクエリ結果を扱う際に強力な型安全性を提供します。
Waterline
評価のまとめ
- 型定義: @types
- レコード作成: 型安全ではない
- レコードフェッチ: 型安全ではない
概要
Waterlineとは?
Waterlineは、Sails Node.jsフレームワークで使用されるデフォルトのORMです。その設計の一部は、「一度書けばどこでも使える」データ操作コードを使用できるようにすることです。これにより、MySQL、PostgreSQL、MongoDB、またはその他のデータベースのどこにデータが存在していても、データのクエリまたは操作を行うコードを記述できます。
型定義: @types
Waterlineの型定義は、TypeScript型定義のDefinitely Typedリポジトリにあります。これらはライブラリに組み込まれていません。
レコード作成:型安全ではない
Waterlineでは、モデルはWaterline.Collection.extend()
を使用して定義されます。テーブル名が指定され、id
やname
などのモデルの属性がその型とともに宣言されます。モデルはWaterlineインスタンスに追加され、レコードの作成に使用されます。 Waterlineでのレコード作成は型安全ではなく、モデルで定義されていない新しいレコードに属性を設定できます。さらに、戻り値の型はany
であり、Waterlineを使用する際に頻繁に渡されます。
レコードフェッチ:型安全ではない
Waterlineインスタンスと指定されたモデルを使用してデータベースからレコードをフェッチする場合、存在しない属性であっても、コンパイラエラーをトリガーすることなくfind()
メソッドに挿入できます。メソッドの戻り値の型はany
です。 Waterlineと@types
型定義を使用したデータのクエリは、一般的に型安全ではありません。
型安全性:弱い
Waterlineのモデルは型安全ではなく、データ操作および作成操作も同様に型安全ではありません。 Waterlineは主にJavaScriptライブラリであり、その型定義はTypeScriptコードをコンパイルするための最低限のものを提供します。
TypegooseとMongoose
評価の概要
- 型定義: @types
- レコード作成: 型安全
- レコードフェッチ: 型安全ではない
概要
Mongooseとは?
Mongooseは、MongoDB向けのNode.jsデータモデリングツールとして人気があり、十分にメンテナンスされています。スキーマを使用してデータをモデル化でき、組み込みの型キャスト、バリデーション、クエリ構築、およびビジネスロジックフックが含まれています。Node.jsでMongoDBデータベースを使用しており、オブジェクトをデータベースドキュメント(またはODM)にマッピングするためにORMのようなツールを使用したい場合、Mongooseは安全な選択肢です。人気があり成熟したプロジェクトであり、現在も活発にメンテナンスが続けられています。
MongooseでTypeScriptの強力な型付けを使用するには、主に2つの方法があります。1つは、@types
リポジトリから型を使用し、モデルのカスタムインターフェースを作成する方法です。もう1つは、@types
からの型定義とともにTypegooseを使用する方法です。Typegooseを使用すると、クラスを使用してMongooseモデルを定義できます。この記事では、Typegooseについて検討します。
PrismaとMongooseのより焦点を絞った比較については、Mongoose比較ページをご覧ください。
型定義: @types
Typegooseを使用するには、まずMongooseとその@types
型定義をインストールする必要があります。これらは、DefinitelyTypedリポジトリにあります。ライブラリには組み込まれていません。
レコード作成: 型安全
Typegooseでモデルを作成するには、User
のようなモデルクラスと、name
やage
のようなプロパティを定義します。プロパティは、@prop()
デコレータで装飾され、プロパティが必須かどうかや、他のモデルとの関連方法などの追加情報を指定します。
モデルが定義されると、MongooseのModel
オブジェクトを使用して、型安全な方法でレコードを作成できます。モデルプロパティは自動補完され、未定義のプロパティを追加しようとするとコンパイラエラーが発生します。戻りオブジェクトの型は、定義されたModelクラス(DocumentType<User>
)に対応し、そのプロパティには型安全な方法でアクセスできます。この型安全性は、ネストされたモデルにも拡張されます(たとえば、ネストされたPost
オブジェクトを持つUser
を保存するなど)。
レコード取得: 型安全ではない
Model.find()
を使用してデータベースからレコードをクエリする場合、フィルタープロパティは型チェックされず、定義されていないプロパティをコンパイラエラーなしで追加できます。これにより、Mongooseはフィルターのキャストを試みます。これが失敗した場合、ランタイム時にCastError
がスローされます。
モデルで.populate()
を使用して他のドキュメントへの参照をポピュレートする場合、コンパイラエラーなしで.populate()
メソッドに何でも入力できるため、この操作も同様に型安全ではありません。
find()
またはfindOne()
コマンドからの戻り値の型は、データベースのクエリに使用されたモデルに従って正しく型付けされています。
型安全性: ほどほど
Typegooseは、クラスとデコレータを活用して、Mongooseモデルを迅速に構築するのに役立ちます。レコードを作成する場合、パラメータは型チェックされますが、クエリを実行する場合は、開発者が追加の安全対策を組み込む必要があります。型安全なTypeScriptとMongoDBを始めるには最適な場所です。
簡単に検討
この記事では、Prismaのデータガイドの2022年 Node.js ORM、クエリビルダー、データベースライブラリ トップ11で参照されている最も人気のあるORMの型安全性に焦点を当てています。TypeScript、Node.js、およびデータベースを使用する場合に検討すべき他のライブラリもあります。
Knex.js
Knex.jsは、複数のデータベースをサポートし、トランザクションサポート、接続プーリング、ストリーミングインターフェースなどの機能を含むNode.jsクエリビルダー(ORMではない)です。データベースドライバよりも上のレベルで作業し、SQLを手書きすることを避けることができます。ただし、より低レベルのライブラリであるため、SQLと、結合やインデックスなどのリレーショナルデータベースの概念に精通していることが期待されます。公式のTypeScriptバインディングは、knex
NPMパッケージに組み込まれています。TypeScriptのサポートはベストエフォートであり、「すべての使用パターンを型チェックできるわけではありません」。Knexのドキュメントには、「型エラーがないからといって、生成されたクエリが正しいとは現在保証されていません」とも記載されています。
PgTyped
PgTypedの目標は、生のSQLを記述できるようにし、記述するクエリの型安全性を保証することです。SQLファイルを処理し、実行中のPostgreSQLデータベースに直接接続することにより、SQLクエリのパラメータと結果のTypeScript型定義を自動的に生成します。現在、PostgreSQLのみをサポートしています。
@slonik/typegen
PgTypedと同様のパッケージは、Slonik PostgreSQLクライアントを使用して、生のSQLクエリからTypeScriptインターフェースを生成するSlonik typegenライブラリです。typegenライブラリを使用するには、インポートして、クエリを実行するために生成するプロキシオブジェクトを使用します。クエリを実行すると、typegenはクエリ結果のフィールド型を検査し、そのクエリのTypeScriptインターフェースを生成します。その後、クエリを型安全な方法で実行できます。
結論
この記事では、最も人気のあるNode.js ORM、データベースツールキット、およびクエリビルダーの型安全性を簡単に評価します。ライブラリのリストは、2022年 Node.js ORM、クエリビルダー、データベースライブラリ トップ11から引用しており、これらのライブラリの健全性は、リポジトリのアクティビティや開発者サポートなどの基準に従って評価されています。
型安全性は、データベースと対話するためのツールを選択する際に使用する必要がある唯一の基準ではありません。パッケージのプログラミングインターフェース、設計、データベース機能のサポート、および柔軟性も考慮することが重要です。Node.jsプロジェクトが異なれば、必要なツールも異なる場合があります。
クエリビルダーとORMの詳細については、PrismaのデータガイドのSQL、クエリビルダー、ORMの比較をご覧ください。これは、データベース、データモデリングなどについて学ぶための無料の役立つ知識ベースです。