はい、承知いたしました。「Node.jsとMongoDB連携入門:データベース接続と操作方法」についての詳細な記事を作成します。約5000語のボリュームで、初心者にも分かりやすいように丁寧に解説します。
Node.jsとMongoDB連携入門:データベース接続と操作方法
はじめに
現代のWebアプリケーション開発において、バックエンドの技術選定はプロジェクトの成否を大きく左右します。中でも、実行環境としてNode.js、データベースとしてMongoDBを組み合わせる構成は、「MEANスタック(MongoDB, Express, Angular, Node.js)」や「MERNスタック(MongoDB, Express, React, Node.js)」の中核を担い、世界中の開発者から絶大な支持を得ています。
なぜこの組み合わせが人気なのでしょうか?
- JavaScriptによる統一: Node.jsはサーバーサイドJavaScript環境、MongoDBはJSONライクなドキュメント(BSON)を扱うデータベースです。これにより、フロントエンドからバックエンド、データベースに至るまで、一貫してJavaScript(およびJSON)の知識で開発を進めることができ、学習コストの低減と開発効率の向上に繋がります。
- 柔軟性とスケーラビリティ: MongoDBはスキーマレスなNoSQLデータベースであり、アプリケーションの要件変更に柔軟に対応できます。また、水平スケーリング(シャーディング)に優れており、大規模なデータや高いトラフィックにも対応可能です。
- 活発なコミュニティと豊富なエコシステム: Node.jsとMongoDBは、ともに巨大で活発なコミュニティを持っています。これにより、豊富なライブラリやツール、ドキュメントが利用可能であり、問題解決や学習が容易になります。
この記事では、Node.jsとMongoDBの連携に初めて挑戦する開発者の方々を対象に、環境構築からデータベースへの接続、そして基本的なデータ操作(CRUD: Create, Read, Update, Delete)までを、ステップバイステップで詳細に解説していきます。
この記事で学べること:
- 開発環境の準備(Node.js, MongoDB Atlas)
- Node.jsからMongoDBへ安全に接続する方法
- Mongooseを使ったデータモデリング(スキーマとモデル)
- 基本的なCRUD操作(データの作成、読み取り、更新、削除)の実践
- より高度な操作(ミドルウェア、バリデーション、関連データの取得)の基礎
この記事を読み終える頃には、あなたはNode.jsアプリケーションからMongoDBを自在に操作するための確かな第一歩を踏み出していることでしょう。それでは、早速始めていきましょう。
第1章: 環境構築
アプリケーション開発の最初のステップは、必要なツールを準備する環境構築です。ここでは、Node.jsの確認、MongoDBデータベースの準備、そしてプロジェクトの初期設定を行います。
1.1 Node.jsのインストール確認
まず、お使いのコンピュータにNode.jsとnpm(Node Package Manager)がインストールされていることを確認します。ターミナル(またはコマンドプロンプト)を開き、以下のコマンドを実行してください。
bash
node -v
npm -v
バージョン番号が表示されれば(例: v18.17.1
、9.6.7
)、準備は完了です。もしインストールされていない場合は、Node.js公式サイトからお使いのOSに合ったLTS(Long Term Support)版をダウンロードしてインストールしてください。
1.2 MongoDBデータベースの準備
MongoDBを利用する方法は大きく分けて2つあります。ローカル環境にインストールする方法と、クラウドサービスを利用する方法です。今回は、管理が容易で、環境構築の手間が少ないMongoDB Atlasという公式のクラウドサービスを利用します。
MongoDB Atlasを利用するメリット:
- 簡単なセットアップ: 数クリックでデータベースクラスタを構築できます。
- フルマネージド: サーバーの保守、バックアップ、セキュリティなどをMongoDBが管理してくれます。
- 無料枠: 小規模な開発や学習用途であれば、無料で利用できるプラン(Free Tier)があります。
MongoDB Atlasのセットアップ手順:
-
アカウント作成: MongoDB Atlasの公式サイトにアクセスし、「Try Free」からアカウントを登録します。
-
クラスタの作成:
- ログイン後、ダッシュボードで「Create a Database」または「Build a Database」をクリックします。
- プランの選択画面が表示されるので、「Shared」(共有)プランを選択します。これが無料枠です。
- クラウドプロバイダとリージョンを選択します。特に理由がなければ、デフォルトのままで構いません(例えば、AWSの
us-east-1
など)。 - 「Cluster Tier」が「M0 Sandbox (Shared RAM, 512 MB Storage)」であることを確認します。
- 「Cluster Name」を任意の名前に変更できます(例:
MyTestCluster
)。 - 最後に「Create Cluster」をクリックします。クラスタのプロビジョニングには数分かかります。
-
データベースユーザーの作成:
- クラスタの準備ができたら、データベースにアクセスするためのユーザーを作成します。
- 左側のメニューから「Database Access」を選択し、「Add New Database User」をクリックします。
- 認証方法は「Password」を選択します。
- UsernameとPasswordを入力します。このパスワードは後で接続に使用するので、必ず安全な場所に控えておいてください。「Autogenerate Secure Password」を使うのがおすすめです。
- 「Database User Privileges」は「Read and write to any database」(任意のデータベースへの読み書き権限)を選択し、「Add User」をクリックします。
-
IPアクセスリストの設定:
- セキュリティのため、MongoDB Atlasは特定のIPアドレスからの接続のみを許可します。
- 左側のメニューから「Network Access」を選択し、「Add IP Address」をクリックします。
- 開発用途で手早く設定するには、「ALLOW ACCESS FROM ANYWHERE」(どこからでもアクセスを許可)を選択し、IPアドレスとして
0.0.0.0/0
を追加します。確認ボタンを押して完了です。 - 注意: 本番環境では、アプリケーションサーバーのIPアドレスなど、信頼できるIPアドレスのみを許可するように設定してください。
-
接続文字列の取得:
- 左側のメニューで「Database」に戻り、作成したクラスタの「Connect」ボタンをクリックします。
- 接続方法として「Drivers」を選択します。
- 「Driver」として「Node.js」、「Version」として最新のものを選択します。
- Connection Stringが表示されます。この文字列をコピーしておきます。これがNode.jsアプリケーションからデータベースに接続するための重要な情報です。
mongodb+srv://<username>:<password>@mytestcluster.xxxxx.mongodb.net/?retryWrites=true&w=majority
- 後で、
<username>
と<password>
の部分を、先ほど作成したデータベースユーザーの情報に置き換えます。
これで、クラウド上にMongoDBデータベースの準備が整いました。
1.3 Node.jsプロジェクトの初期化とライブラリのインストール
次に、Node.jsプロジェクト用のディレクトリを作成し、必要なライブラリをインストールします。
-
プロジェクトディレクトリの作成と移動:
bash
mkdir node-mongo-intro
cd node-mongo-intro -
プロジェクトの初期化:
package.json
ファイルを作成するため、以下のコマンドを実行します。-y
フラグを付けると、すべての質問にデフォルトで回答してくれます。
bash
npm init -y -
必要なライブラリのインストール:
Node.jsからMongoDBを操作するためのライブラリには、公式のmongodb
ドライバと、より高機能なODM(Object Data Modeling)ライブラリであるmongoose
があります。mongodb
ドライバ: MongoDBと直接通信するための低レベルなAPIを提供します。mongoose
: スキーマ定義、データバリデーション、ミドルウェア、クエリビルディングなど、開発を強力にサポートする機能を提供します。特に初心者や複雑なアプリケーションを構築する場合には、Mongooseの使用が強く推奨されます。
今回は、より実践的で便利なMongooseを中心に解説を進めます。また、接続文字列などの機密情報を安全に管理するために
dotenv
というライブラリも併せてインストールします。bash
npm install mongoose dotenv
これで、開発に必要なすべての準備が整いました。
第2章: MongoDBへの接続
環境が整ったので、いよいよNode.jsアプリケーションからMongoDB Atlasに接続します。安全かつ再利用可能な形で接続処理を実装することが重要です。
2.1 接続文字列の安全な管理
先ほどMongoDB Atlasからコピーした接続文字列には、データベースのユーザー名とパスワードが含まれています。このような機密情報をソースコードに直接書き込む(ハードコーディングする)のは、セキュリティ上非常に危険です。そこで、dotenv
パッケージを使って環境変数として管理します。
-
.env
ファイルの作成:
プロジェクトのルートディレクトリ(package.json
と同じ階層)に、.env
という名前のファイルを作成します。 -
接続文字列の記述:
.env
ファイルに、以下のように環境変数を定義します。MONGODB_URI
という変数名が一般的です。“`env
.envファイル
MongoDB Atlasからコピーした接続文字列を貼り付け、
と を実際に作成したものに置き換える。 に特殊文字が含まれる場合は、URLエンコードが必要な場合があります。 MONGODB_URI=mongodb+srv://your_username:[email protected]/myFirstDatabase?retryWrites=true&w=majority
``
your_username
*と
your_passwordは、第1章で作成したデータベースユーザーの情報に書き換えてください。
myFirstDatabase`の部分は、接続したいデータベース名です。存在しない場合は、最初のデータ書き込み時に自動的に作成されます。
* -
.gitignore
の設定:
.env
ファイルは機密情報を含むため、Gitなどのバージョン管理システムにコミットしてはいけません。プロジェクトのルートに.gitignore
ファイルを作成し、以下の内容を記述します。“`gitignore
.gitignoreファイル
node_modules
.env
“`
2.2 Mongooseを使った接続処理
それでは、実際に接続を行うコードを書いていきましょう。index.js
というファイルを作成し、以下のコードを記述します。
“`javascript
// index.js
// 1. dotenvを読み込み、環境変数をプロセスにロードする
require(‘dotenv’).config();
// 2. mongooseをインポート
const mongoose = require(‘mongoose’);
// 3. 接続文字列を環境変数から取得
const mongoURI = process.env.MONGODB_URI;
// 4. データベースへの接続関数を定義
const connectDB = async () => {
try {
// mongoose.connectはPromiseを返す
await mongoose.connect(mongoURI);
console.log(‘MongoDBに正常に接続しました。’);
} catch (error) {
console.error(‘MongoDBへの接続に失敗しました:’, error);
// 接続に失敗した場合は、プロセスを終了する
process.exit(1);
}
};
// 5. 接続関数を呼び出す
connectDB();
// 接続イベントの監視
const db = mongoose.connection;
db.on(‘error’, console.error.bind(console, ‘connection error:’));
db.once(‘open’, function() {
console.log(“Mongoose is connected.”);
});
db.on(‘disconnected’, () => {
console.log(‘MongoDBとの接続が切れました。’);
});
“`
コードの解説:
require('dotenv').config()
: この行をコードの最初に置くことで、.env
ファイルに定義された変数がprocess.env
オブジェクトに読み込まれます。require('mongoose')
: Mongooseライブラリをインポートします。process.env.MONGODB_URI
:.env
ファイルから接続文字列を取得します。connectDB
関数: データベースへの接続ロジックを非同期関数(async/await
)として定義しています。mongoose.connect(mongoURI)
: このメソッドが実際の接続処理を行います。接続が完了するまで処理を待つためawait
を使用します。try...catch
ブロック: 接続中にエラーが発生した場合(パスワードが違う、IPアドレスが許可されていないなど)に、エラーを捕捉してコンソールに出力し、アプリケーションを終了させます。
connectDB()
: 定義した接続関数を実行します。
実行してみましょう:
ターミナルで以下のコマンドを実行します。
bash
node index.js
コンソールに「MongoDBに正常に接続しました。」と表示されれば、接続は成功です!もしエラーが表示された場合は、以下の点を確認してください。
.env
ファイルの接続文字列は正しいか(ユーザー名、パスワード、クラスタURL)。- MongoDB AtlasのIPアクセスリストに
0.0.0.0/0
が設定されているか。 - インターネット接続は正常か。
2.3 接続ロジックのモジュール化
アプリケーションが大きくなると、様々なファイルでデータベース接続が必要になります。接続ロジックを別のファイルに分離しておくと、コードの再利用性が高まり、管理がしやすくなります。
config
というディレクトリを作成し、その中にdb.js
というファイルを作成します。
“`javascript
// config/db.js
const mongoose = require(‘mongoose’);
require(‘dotenv’).config();
const mongoURI = process.env.MONGODB_URI;
const connectDB = async () => {
try {
await mongoose.connect(mongoURI);
console.log(‘MongoDBに接続しました…’);
} catch (err) {
console.error(err.message);
process.exit(1);
}
};
module.exports = connectDB;
“`
そして、index.js
を以下のように修正します。
“`javascript
// index.js
const connectDB = require(‘./config/db’);
// DBに接続
connectDB();
// … ここからアプリケーションのメインロジックが始まる …
console.log(‘アプリケーションが起動しました。’);
“`
これで、connectDB()
を呼び出すだけでどこからでもデータベースに接続できるようになりました。
第3章: Mongooseの基本 – スキーマとモデル
データベースに接続できたので、次はデータを扱うための準備をします。Mongooseの最も重要なコンセプトがスキーマ(Schema)とモデル(Model)です。
- スキーマ (Schema): データベースに保存するドキュメント(データ)の「構造」を定義するものです。どのようなフィールドがあり、それぞれのフィールドがどのようなデータ型(文字列、数値など)を持つか、また、必須項目やデフォルト値などのルールを定義します。
- モデル (Model): スキーマを元に作成されるコンストラクタ(クラスのようなもの)です。モデルを通じて、実際のデータベース操作(データの作成、読み取り、更新、削除)を行います。モデルは、MongoDBのコレクションに直接対応します。
スキーマレスが特徴のMongoDBにあえて構造を定義することで、データの整合性を保ち、開発者が意図しないデータが保存されるのを防ぐことができます。
3.1 スキーマの定義
例として、ブログの記事を管理するためのPost
スキーマを定義してみましょう。
models
という新しいディレクトリを作成し、その中にPost.js
というファイルを作成します。
“`javascript
// models/Post.js
const mongoose = require(‘mongoose’);
const Schema = mongoose.Schema;
// 新しいスキーマを定義
const PostSchema = new Schema({
title: {
type: String,
required: [true, ‘タイトルは必須です。’],
trim: true,
maxlength: [100, ‘タイトルは100文字以内で入力してください。’]
},
content: {
type: String,
required: [true, ‘本文は必須です。’]
},
author: {
type: String,
default: ‘匿名’
},
tags: [String], // 文字列の配列
likes: {
type: Number,
default: 0
},
isPublished: {
type: Boolean,
default: false
},
publishedAt: {
type: Date,
default: null
}
}, {
// オプション: タイムスタンプを自動的に追加
timestamps: true // createdAtとupdatedAtが自動的に追加される
});
// スキーマからモデルを作成し、エクスポート
module.exports = mongoose.model(‘Post’, PostSchema);
“`
スキーマ定義の解説:
new Schema({...}, {...})
: 第1引数にフィールドの定義、第2引数にスキーマのオプションを渡します。type
: フィールドのデータ型を指定します。MongooseはString
,Number
,Date
,Buffer
,Boolean
,ObjectId
,Array
など、様々な型をサポートしています。required
: このフィールドが必須かどうかを指定します。[true, 'エラーメッセージ']
の形式で、バリデーション失敗時のメッセージも指定できます。default
: データが指定されなかった場合のデフォルト値を設定します。trim
: 文字列の前後の空白を自動的に除去します。maxlength
: 文字列の最大長を指定します。[String]
:tags
フィールドのように、特定の型の配列を定義できます。timestamps: true
: このオプションを指定すると、ドキュメントが作成された日時(createdAt
)と更新された日時(updatedAt
)が自動的に記録され、非常に便利です。
3.2 モデルの作成とエクスポート
スキーマを定義したら、mongoose.model()
メソッドを使ってモデルを作成します。
javascript
module.exports = mongoose.model('Post', PostSchema);
- 第1引数
Post
: モデルの名前です。Mongooseは、この名前を元にMongoDBのコレクション名を自動的に生成します。通常、モデル名を小文字の複数形にした名前(この場合はposts
)がコレクション名になります。 - 第2引数
PostSchema
: 使用するスキーマオブジェクトです。
module.exports
でモデルをエクスポートすることで、他のファイルからrequire()
を使ってこのモデルを読み込み、データベース操作ができるようになります。
第4章: CRUD操作の実践
モデルが準備できたので、いよいよデータベースの基本的な操作であるCRUD(Create, Read, Update, Delete)を実践していきましょう。index.js
ファイルを書き換えて、各操作を試していきます。
まずはindex.js
を以下のように準備します。
“`javascript
// index.js
const connectDB = require(‘./config/db’);
const Post = require(‘./models/Post’); // 作成したPostモデルをインポート
// DBに接続
connectDB();
const run = async () => {
try {
// ここにCRUD操作のコードを書いていく
console.log(‘CRUD操作を開始します…’);
} catch (error) {
console.error(‘エラーが発生しました:’, error);
} finally {
// 最終的に接続を閉じる
await mongoose.disconnect();
console.log(‘MongoDBとの接続を閉じました。’);
}
};
// 実行
run();
“`
これからのコードは、run
関数の中の// ここにCRUD操作のコードを書いていく
の部分に記述していきます。
4.1 Create (作成)
新しいブログ記事(ドキュメント)を作成し、データベースに保存します。
方法1: new Model()
とsave()
“`javascript
// 新しいPostインスタンスを作成
const newPost = new Post({
title: ‘Mongoose入門’,
content: ‘これはMongooseを使った初めての投稿です。’,
author: ‘John Doe’,
tags: [‘Node.js’, ‘MongoDB’, ‘Mongoose’]
});
// データベースに保存
const savedPost = await newPost.save();
console.log(‘新しい投稿が保存されました:’, savedPost);
“`
new Post()
でスキーマに基づいた新しいドキュメントオブジェクトを作成し、save()
メソッドで非同期的にデータベースに保存します。save()
は保存されたドキュメントをPromiseで返します。
方法2: Model.create()
より簡潔に書ける方法です。
“`javascript
const postData = {
title: ‘非同期処理について’,
content: ‘Node.jsでは非同期処理が重要です。’,
author: ‘Jane Smith’,
tags: [‘JavaScript’, ‘Node.js’, ‘Async’],
isPublished: true,
publishedAt: new Date()
};
const createdPost = await Post.create(postData);
console.log(‘新しい投稿が作成されました:’, createdPost);
“`
create()
メソッドは、オブジェクトを受け取り、ドキュメントの作成と保存を一度に行います。
4.2 Read (読み取り)
データベースに保存されたデータを検索・取得します。
全件取得: find()
posts
コレクション内のすべてのドキュメントを取得します。
javascript
const allPosts = await Post.find({});
console.log('すべての投稿:', allPosts);
find()
の引数に空のオブジェクト{}
を渡すと、すべてのドキュメントがマッチします。
条件を指定して検索: find()
/ findOne()
特定の条件に一致するドキュメントを検索します。
“`javascript
// 著者名が ‘John Doe’ の投稿をすべて検索
const johnsPosts = await Post.find({ author: ‘John Doe’ });
console.log(‘John Doeの投稿:’, johnsPosts);
// 公開済み(isPublished: true)の投稿を一件だけ検索
const publishedPost = await Post.findOne({ isPublished: true });
console.log(‘公開済みの投稿(一件):’, publishedPost);
``
find()
*: 条件に一致するすべてのドキュメントを配列で返します。
findOne()
*: 条件に一致する最初のドキュメントを一つだけオブジェクトで返します。見つからない場合は
null`を返します。
IDで検索: findById()
ドキュメントのユニークなID(_id
)で検索します。これは非常によく使われる操作です。
javascript
// 上で作成した投稿のIDを使って検索 (IDは実際のものに置き換えてください)
// const somePostId = '64e8b3f3a1b2c3d4e5f6a7b8';
// const foundPost = await Post.findById(somePostId);
// console.log('IDで検索した投稿:', foundPost);
クエリをチェーンする
Mongooseでは、メソッドを繋げて(チェーンして)より複雑なクエリを構築できます。
“`javascript
const complexQueryPosts = await Post.find({ likes: { $gte: 0 } }) // likesが0以上の投稿
.sort({ createdAt: -1 }) // 作成日時の降順でソート
.limit(5) // 最大5件まで取得
.select(‘title author likes’); // title, author, likesフィールドのみ取得
console.log(‘複雑なクエリの結果:’, complexQueryPosts);
``
{ likes: { $gte: 0 } }
*:
$gteは"greater than or equal to"(以上)を意味するクエリオペレータです。他にも
$gt(より大きい),
$lt(より小さい),
$in(いずれかに一致)など多数あります。
.sort({ field: 1 })
*: 昇順ソート。
-1で降順ソート。
.limit(n)
*: 取得件数を制限します。
.select(‘field1 field2’)`: 取得するフィールドを指定します(Projection)。
*
4.3 Update (更新)
既存のドキュメントの情報を更新します。
一件更新: updateOne()
/ findByIdAndUpdate()
“`javascript
// タイトルが ‘Mongoose入門’ の投稿のlikesを10に更新
const updateResult = await Post.updateOne(
{ title: ‘Mongoose入門’ },
{ $set: { likes: 10, content: ‘内容を更新しました。’ } }
);
console.log(‘更新結果:’, updateResult); // { acknowledged: true, modifiedCount: 1, … }
// IDで検索して更新し、更新後のドキュメントを取得
const firstPost = await Post.findOne({}); // 適当な投稿を一件取得
if (firstPost) {
const updatedPost = await Post.findByIdAndUpdate(
firstPost._id,
{ $inc: { likes: 1 } }, // likesを1増やす
{ new: true } // このオプションが重要!
);
console.log(‘更新後の投稿:’, updatedPost);
}
``
$set
*: 指定したフィールドの値を更新します。
$inc
*: 数値フィールドの値を指定した量だけ増減させます。
{ new: true }
*:
findByIdAndUpdateはデフォルトで更新前のドキュメントを返します。このオプションを
true`にすることで、更新後の新しいドキュメントを返すようになります。これは非常に重要で、忘れがちなポイントです。
4.4 Delete (削除)
ドキュメントをデータベースから削除します。
一件削除: deleteOne()
/ findByIdAndDelete()
“`javascript
// 著者名が ‘Jane Smith’ の投稿を一件削除
const deleteResult = await Post.deleteOne({ author: ‘Jane Smith’ });
console.log(‘削除結果:’, deleteResult); // { acknowledged: true, deletedCount: 1 }
// IDで検索して削除
const postToDelete = await Post.findOne({ author: ‘John Doe’ });
if (postToDelete) {
const deletedPost = await Post.findByIdAndDelete(postToDelete._id);
console.log(‘削除された投稿:’, deletedPost);
}
“`
これらのCRUD操作が、Node.jsとMongoDBを使ったアプリケーション開発の基本となります。まずはこれらの操作に慣れることが重要です。
第5章: 発展的なトピック
基本的なCRUD操作をマスターしたら、Mongooseが提供するさらに強力な機能をいくつか見ていきましょう。これらを活用することで、より堅牢で効率的なアプリケーションを構築できます。
5.1 ミドルウェア (Middleware / Hooks)
Mongooseのミドルウェアは、特定の処理(save
, validate
, remove
など)の前(pre)または後(post)に、独自の関数をフックさせる機能です。これにより、特定のロジックをモデルに組み込むことができます。
ユースケース例: pre('save')
フック
ユーザーモデルを例に、パスワードが保存される前に自動的にハッシュ化する処理を実装してみましょう。
(※別途bcryptjs
ライブラリのインストールが必要です: npm install bcryptjs
)
“`javascript
// models/User.js (新しいファイル)
const mongoose = require(‘mongoose’);
const bcrypt = require(‘bcryptjs’);
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true }
});
// ‘save’が実行される前にこの関数が呼ばれる
UserSchema.pre(‘save’, async function(next) {
// thisはこれから保存されるUserドキュメントを指す
if (!this.isModified(‘password’)) {
// パスワードが変更されていない場合は何もしない
return next();
}
try {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (error) {
next(error);
}
});
module.exports = mongoose.model(‘User’, UserSchema);
``
user.save()`が呼び出されるたびに、パスワードフィールドが変更されていれば自動的にハッシュ化が行われます。アプリケーションのロジックからパスワードのハッシュ化処理を分離でき、コードがクリーンになります。
このミドルウェアにより、
5.2 バリデーション (Validation)
スキーマ定義時にrequired
やmaxlength
といった基本的なバリデーションを設定しましたが、より複雑なカスタムバリデーションを定義することも可能です。
カスタムバリデータ
例えば、tags
配列に少なくとも1つのタグが含まれていることを保証するバリデータを追加してみましょう。
“`javascript
// models/Post.jsの一部を修正
const PostSchema = new Schema({
// … 他のフィールド …
tags: {
type: [String],
validate: {
validator: function(v) {
// vはtags配列
return v && v.length > 0;
},
message: ‘少なくとも1つのタグが必要です。’
}
},
// …
});
``
validateプロパティに関数を設定することで、独自の検証ロジックを実装できます。バリデーションに失敗すると、
save()や
create()`はエラーをスローします。
5.3 関連 (Population)
リレーショナルデータベースのJOIN
のように、あるコレクションのドキュメントが他のコレクションのドキュメントを参照する関係を扱うための機能がPopulationです。
例えば、ブログ記事(Post)が、それを書いたユーザー(User)を参照するケースを考えます。
1. スキーマの修正
まず、Post
スキーマのauthor
フィールドを、User
モデルのObjectId
を参照するように変更します。
“`javascript
// models/Post.js
// …
const PostSchema = new Schema({
title: { type: String, required: true },
content: { type: String, required: true },
author: {
type: mongoose.Schema.Types.ObjectId, // UserのObjectIdを保存する
ref: ‘User’, // ‘User’モデルを参照することを指定
required: true
},
// …
}, { timestamps: true });
module.exports = mongoose.model(‘Post’, PostSchema);
“`
2. データの作成
投稿を作成する際に、author
フィールドにはUser
ドキュメントの_id
を格納します。
“`javascript
// ユーザーを作成
const user = await User.create({ username: ‘testuser’, password: ‘password123’ });
// 投稿を作成 (authorにはユーザーの_idを渡す)
const post = await Post.create({
title: ‘Populationのテスト’,
content: ‘ユーザー情報を紐付けます。’,
author: user._id
});
“`
3. populate()
を使ってデータを取得
このままPost
をfind
しても、author
フィールドにはIDしか入っていません。populate()
を使うことで、このIDを元にUser
コレクションを検索し、実際のユーザー情報を取得して埋め込んでくれます。
“`javascript
// 投稿をIDで検索し、authorフィールドをpopulateする
const populatedPost = await Post.findById(post._id).populate(‘author’, ‘username’);
console.log(populatedPost);
“`
実行結果:
{
_id: new ObjectId("..."),
title: 'Populationのテスト',
content: 'ユーザー情報を紐付けます。',
author: {
_id: new ObjectId("..."),
username: 'testuser' // ← IDだけでなくユーザー情報が入っている!
},
createdAt: ...,
updatedAt: ...
}
populate('author', 'username')
のように第2引数を指定すると、User
ドキュメントの中からusername
フィールドだけを取得するなど、選択的な取得も可能です。Populationは、関連データを扱う上で非常に強力な機能です。
まとめ
この記事では、Node.jsとMongoDBを連携させるための基本から応用までを、Mongooseライブラリを中心に網羅的に解説しました。
振り返り:
- 環境構築: MongoDB Atlasでクラウドデータベースを準備し、Node.jsプロジェクトに
mongoose
とdotenv
をインストールしました。 - データベース接続:
.env
ファイルで接続情報を安全に管理し、mongoose.connect()
を使ってデータベースに接続する、再利用可能なモジュールを作成しました。 - スキーマとモデル: Mongooseの核となる概念であるスキーマでデータの構造を定義し、モデルを作成してデータベース操作のインターフェースとしました。
- CRUD操作: モデルを通じて、データの作成(Create)、読み取り(Read)、更新(Update)、削除(Delete)という一連の基本操作を実践しました。
- 発展的なトピック: ミドルウェアによるロジックの自動化、カスタムバリデーションによるデータ整合性の強化、そしてPopulationによる関連データの効率的な取得方法について学びました。
ここまでの内容を理解し、実践できれば、あなたはNode.jsとMongoDBを使ったバックエンド開発の強固な基礎を築いたと言えるでしょう。
今後の学習へのステップ:
- インデックス: 大量のデータを効率的に検索するためのインデックスの作成と活用。
- 集計パイプライン (Aggregation Pipeline): 複数のステージを経てデータを処理・変換し、複雑な集計を行う強力な機能。
- トランザクション: 複数の操作を一つのまとまりとして扱い、すべて成功するかすべて失敗するかのいずれかを保証する機能。
- Express.jsとの統合: Node.jsのWebフレームワークであるExpress.jsと組み合わせて、REST APIを構築する方法。
技術の学習は、一歩一歩着実に進むことが大切です。この記事が、あなたのNode.jsとMongoDBの旅の素晴らしいスタート地点となることを願っています。ぜひ、この記事のコードを元に、自分だけのアプリケーションを構築してみてください。
Happy Coding