そろそろGraphQLを本格的に勉強してみようと思って、いろいろ試しているところです。その中で、GraphQL用のデータベースにMongoDBを使ったパターンを試したくて、今回MongoDBについても使い方など改めてまとめてみることにしました。環境構築方法、Mongo ShellやNode.js DriverでのCRUDの方法などをまとめてみました。
目次
はじめに
MongoDBは、開発とスケーリングを容易にするために設計されたオープンソースのドキュメント指向データベースです。Key-Valueストア(KVS)を使用した、いわゆるNoSQLデータベースのひとつです。現時点での最新バージョンはv4.0.2です。
MongoDBは、この記事でも紹介しますが、「Mongo Shell」と呼ばれるコマンドラインインターフェースや「Driver」と呼ばれるクライアント・ライブラリなどを使って操作します。なお、当記事では特に使いませんが、GUIのツールもあれば、何かと便利だと思います。公式のサイトからダウンロードできるMongoDB Compass(約143MBあります)というGUIツールを紹介しておきます。
それから、MongoDBの使い方を一通り学ぶには以下がオススメです。これだけのボリュームのものが無料で、しかも日本語で読めるのはとてもありがたいです。ただ2012年に書かれたものなので、現在のバージョンと齟齬があるかもしれないので、そこは注意が必要です。
それでは、さっそくMongoDBの環境構築から説明を進めていきます。
MongoDBの環境構築
私の環境がMacなので、ここではMacにおける環境構築方法について説明していきます。LinuxやWindowsをお使いの場合は、以下の公式ドキュメントを参考に環境構築を行ってください。
MongoDBには、通常版のCommunity Edtionと上位版のEnterpise Edtionの2つが用意されています。今回は、Community Edtionを使用します。
Homebrewを使ってMongoDBをインストールする
MongoDBのCommunity Edtionは、Homebrewを使ってインストールすることができます。インストールの前に念のためHomebrewを最新のパッケージにアップデートしておきます(状況によりアップデートは時間がかかる場合があります)。
$ brew update
Homebrewのアップデートが完了したら、以下のコマンドでMongoDBをインストールします。
$ brew install mongodb
最新のデベロップバージョンをインストールする場合は、--devel
オプションをつけてコマンドを実行します。
$ brew install mongodb --devel
インストールが完了したら、念のためバージョンを確認します。バージョンが表示されたら、MongoDBは問題なくインストールされていることになります。
$ mongod --version
db version v4.0.2
MongoDBを起動する
初めてMongoDBを使う場合は、まずMongoDBがデータを書き込むために使うディレクトリを作成する必要があります。MongoDBのデフォルト設定では、/data/db
ディレクトリが使われるようになっているので、以下のコマンドで/data/db
ディレクトリを作成します。権限がない(Permission denied)と怒られる場合は、sudo
をつけてコマンドを実行します。
$ sudo mkdir -p /data/db
作成したディレクトリに対して読み書きできる権限が、MongoDBを起動するアカウントに付与されているか確認してください。
なお、MongoDBがデータを書き込むディレクトリを任意のディレクトリに変更したい場合は、変更先のディレクトリを作成した上で、以下を実行します。
$ mongod --dbpath=/data/newdb
準備が完了したら、mongod
コマンドにsudo
をつけてMongoDBを起動します。
$ sudo mongod
コマンドを実行後に出力されるプロセスに以下の一文(「27017番ポートで接続を待機中」というメッセージ)が含まれていれば成功です。
[initandlisten] waiting for connections on port 27017
それでは、次からMongoDBを操作する方法について説明していきます。
Mongo ShellでMongoDBを操作する
Mongo Shellを使用すると、ターミナルからMongoDBを操作することができます。ここでは、Mongo Shellを使って、MongoDBを操作する方法を紹介していきます。
MongoDBは、初期設定ではユーザー認証が必要ないため、ここでも便宜的にユーザー認証については無視していきます。もちろん通常は必要な機能となるので、必要に応じて公式ドキュメントをご確認ください。
Mongo Shellを起動する
sudo mongod
コマンドでMongoDBを起動したら、別のターミナルを開いて、以下のコマンドを実行するとmongo Shellが使えるようになります。--host
オプションには、ローカルホストのアドレス(127.0.0.1)とポート番号(27017)を指定します。
$ mongo --host 127.0.0.1:27017
ちなみに、mongo shellを終了する場合は、Control+C
を実行します。
Mongo Shellについては、以下なども参考にしてください。
- The mongo Shell — MongoDB Manual
- mongo Shell Quick Reference — MongoDB Manual
- MongoDB コマンドメモとか書き – Qiita
- MongoDBのデータ構造な簡単な操作など – Qiita
- mongo shellの操作まとめ – Qiita
- MongoDBコマンド一覧(自分用メモ) – Qiita
Mongo ShellでDatabase操作を行う
Databaseの作成・切り替え
以下のコマンドを実行すると、use
の後に指定したDatabaseが使用できるようになります。存在しないDatabase名を指定した場合、新規にDatabaseが作成されます。
> use myDB
switched to db myDB
Database一覧の確認
以下のコマンドを実行すると、作成済みのDatabase一覧が表示されます。
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
myDB 0.000GB
使用中のDatabaseの確認
以下のコマンドを実行すると、現在使用しているDatabase名が表示されます。
> db
myDB
Databaseの削除
以下のコマンドを実行すると、Databaseが削除されます。削除したいDatabaseに切り替えてから行ってください。
> use myDB
> db.dropDatabase()
{ "dropped" : "myDB, "ok" : 1 }
Mongo ShellでCollection操作を行う
MongoDBでは、他のデータベースで言うTableのことをCollectionと呼びます。ひとつのDatabaseに複数のCollectionが紐付きます。従って、まず使用するDatabaseを選択してから、Collectionの操作を行います。
Collectionの作成・切り替え
以下のコマンドを実行すると、db.createCollection
の引数に指定したCollectionが使用できるようになります。存在しないCollection名を指定した場合、新規にCollectionが作成されます。
> db.createCollection('myCollection')
{ "ok" : 1 }
Collection一覧の確認
以下のコマンドを実行すると、作成済みのCollection一覧が表示されます。
> show collections
myCollection
Databaseの削除
以下のコマンドを実行すると、Collectionが削除されます。
> db.myCollection.drop()
true
Mongo ShellでCRUD操作を行う
次に、DB操作の基本となるCRUD: Create(生成)、Read(読み取り)、Update(更新)、Delete(削除)の操作を紹介していきます。説明が大変なので、軽く事例を紹介するに留めます。詳細は、公式ドキュメントを参照してください。
なお、CRUD操作を行うCollectionを予め作成しておく必要があります。また、MongoDBでは、扱うデータのことをDocumentと呼びます。説明の中でDocumentと書いてある部分はデータのことだと思ってください。
Create: 生成
Collectionに対してDocumentを登録します。1件のDocumentを登録する場合は、db.[Collection名].insertOne()
メソッドを、複数のDocumentを登録する場合は、db.[Collection名].insertMany()
メソッドを使います。メソッドの引数には、登録したいDocumentをオブジェクト形式で渡します。オブジェクトのキーがField(他のデータベースで言うColumn)となります。
1件のDocumentを登録する場合
> db.myCollection.insertOne({ name: 'Dan', age: 18 })
WriteResult({ "nInserted" : 1 })
複数のDocumentを登録する場合
> db.myCollection.insertMany([ { name: 'Bob', age: 22 }, { name: 'John', age: 30 } ])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
Read: 読み取り
Collectionから任意のDocumentを取得します。いわゆる検索を行います。Documentを取得する場合、db.[Collection名].find()
メソッドを使います。メソッドの第一引数には検索条件、第二引数には取得したいField名を指定します。何も指定しない場合は、全件取得します。また、第二引数に何も指定しない場合は、検索条件にマッチしたDocumentの全Fieldの値を取得します。
全てのDocumentを取得する場合
> db.myCollection.find()
{ "_id" : ObjectId("5b94015fb51547df30eecd9d"), "name" : "Dan", "age" : 18 }
{ "_id" : ObjectId("5b9401bcb51547df30eecd9e"), "name" : "Bob", "age" : 22 }
{ "_id" : ObjectId("5b9401bcb51547df30eecd9f"), "name" : "John", "age" : 30 }
nameが’Dan’のDocumentを取得する場合
> db.myCollection.find({ name: 'Dan' })
{ "_id" : ObjectId("5b94015fb51547df30eecd9d"), "name" : "Dan", "age" : 18 }
ageが21以上のDocumentのnameの値を取得する場合
> db.myCollection.find({ age: { $gt: 20 } }, { name: true })
{ "_id" : ObjectId("5b9401bcb51547df30eecd9e"), "name" : "Bob" }
{ "_id" : ObjectId("5b9401bcb51547df30eecd9f"), "name" : "John" }
ageが21以上のDocumentのnameの値を最初の1件のみを取得する場合
> db.myCollection.find({ age: { $gt: 20 } }, { name: 1 }).limit(1)
{ "_id" : ObjectId("5b9401bcb51547df30eecd9e"), "name" : "Bob" }
上記の例で$gt
という演算子を使用していますが、これは指定した値より大きければtrue
を返す比較演算子です。Documentの検索は、このような比較演算子を使用して行いうことになります。
Update: 更新
Collectionの中の任意のDocumentを更新します。Documentを更新する場合は、db.[Collection名].update()
メソッドを使います。メソッドの第一引数には検索条件、第二引数には更新したい内容をオブジェクト形式で指定します。
nameが’Dan’のDocumentを{ name: ‘Dan’, age: ’19’ }で置き換える場合
> db.myCollection.update({ name: 'Dan' }, { name: 'Dan', age: '19' });
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.myCollection.find()
{ "_id" : ObjectId("5b94015fb51547df30eecd9d"), "name" : "Dan", "age" : 19 }
{ "_id" : ObjectId("5b9401bcb51547df30eecd9e"), "name" : "Bob", "age" : 22 }
{ "_id" : ObjectId("5b9401bcb51547df30eecd9f"), "name" : "John", "age" : 30 }
なお、第二引数に指定した値は、更新前のDocumentを置き換えてしまうので注意が必要です。もし、任意のFieldの値だけを更新したり、新しいFieldを追加したい場合は、$set
を使って以下のように指定します。
ageが21以上のDocumentに、{ status: false }を追加する場合
> db.myCollection.update({ age: { $lt: 20 } }, { $set: { status: false } })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.myCollection.find()
{ "_id" : ObjectId("5b94015fb51547df30eecd9d"), "name" : "Dan", "age" : 18, "status" : false }
{ "_id" : ObjectId("5b9401bcb51547df30eecd9e"), "name" : "Bob", "age" : 22 }
{ "_id" : ObjectId("5b9401bcb51547df30eecd9f"), "name" : "John", "age" : 30 }
Delete: 削除
Collectionの中から任意のDocumentを削除します。Documentから検索条件にマッチした最初の1件のみを削除する場合は、db.[Collection名].deleteOne()
メソッドを、複数のDocuentを削除する場合は、db.[Collection名].deleteMany()
メソッドを使います。
ageが21以上のDocumentの最初の1件のみ削除する場合
> db.myCollection.deleteOne({ age: { $gt: 20 } })
{ "acknowledged" : true, "deletedCount" : 1 }
> db.myCollection.find()
{ "_id" : ObjectId("5b9afbb2d62115d72e7ca494"), "name" : "Dan", "age" : 18, "status" : false }
{ "_id" : ObjectId("5b9afbb7d62115d72e7ca496"), "name" : "John", "age" : 30 }
statusがfalseのDocumentを(複数あればすべて)削除する場合
> db.myCollection.deleteMany({ status: false })
{ "acknowledged" : true, "deletedCount" : 1 }
>
> db.myCollection.find()
{ "_id" : ObjectId("5b9401bcb51547df30eecd9f"), "name" : "John", "age" : 30 }
Documentを全件削除する場合
> db.myCollection.deleteMany({})
{ "acknowledged" : true, "deletedCount" : 1 }
Mongo ShellでCRUD操作を行う例を一部紹介しました。本格的にMongoDBを使用する場合は、様々なパターンのクエリーを駆使することになります。一通りCRUDの操作を試しておくだけでも、MongoDBの作法を多少は理解できるようになるかと思います。Mongo Shellを使えばお手軽にMongoDBの操作を試せるので、Mongo Shellを使って一通りCRUDを試されることをオススメします。
Node.js DriverでMongoDBを操作する
MongoDBは、さまざまなプログラミング言語からMongoDBを扱うためのドライバーと呼ばれるクライアントライブラリーを用意しています。そのドライバーの中には、Node.jsからMongoDBを操作することを可能とするNode.js Driverも含まれています。ここでは、このNode.js Driverを使ってMongoDBを操作する方法を紹介します。
MongoDB Node.js Driverをインストールする
プロジェクト用のディレクト内で以下のコマンドを実行すると(事前にnpm init
しておく必要があります)、プロジェクトディレクトリ内にNode.js Driverがインストールされます。
npm install mongodb --save
Node.js Driverをインストールしたら、さっそく新たにapp.js
ファイルを作成して(ファイル名は任意です)、以下を記述します。内容はMongoDBに接続するための処理です(詳細はコード内のコメントを参照してください)。
const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
/** 接続URL */
const url = 'mongodb://localhost:27017';
/** DB名 */
const dbName = 'myDB';
/** DBサーバに接続 */
MongoClient.connect(url, (err, client) => {
/** errがnullではなかったら処理を停止 */
assert.equal(null, err);
/** 成功した旨をコンソールに出力 */
console.log('Connected successfully to server');
/** DBを取得 */
const db = client.db(dbName);
/** DBサーバとの接続解除 */
client.close();
});
この状態で、app.js
ファイルを実行すると以下のようなエラーが出ました。
DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
エラーメッセージの内容は、「現在のURL文字列のパーサは非推奨になり、将来のバージョンで取り除かれる予定です。新しいパーサを利用するためにMongoClient.connectにオプションである{ useNewUrlParser: true }を渡してください」といったものです。従って、上記のエラーが出た場合は、以下のようにMongoClient.connect()
メソッドの第二引数に{ useNewUrlParser: true }
を指定します。
/** DBサーバに接続 */
MongoClient.connect(url, { useNewUrlParser: true }, (err, client) => {
/** errがnullではなかったら処理を停止 */
assert.equal(null, err);
/** 成功した旨をコンソールに出力 */
console.log('Connected successfully to server');
/** DBを取得 */
const db = client.db(dbName);
/** DBサーバとの接続解除 */
client.close();
});
この状態で、app.js
を実行してみます。別のターミナルでsudo mongod
コマンドを実行してMongoDBを立ち上げた状態にして、以下のコマンドを実行します。
$ node app.js
Connected successfully to server
特に問題がなければ、「Connected successfully to server」と表示されます。
Node.js Driverの基本的な使い方は以下を参照してください。
Node.js DriverでCRUD操作を行う
先ほどはMongo Shellを使ってCRUD操作を行う方法を紹介しましたが、今度はこのNode.js Driverを使ってCRUD処理を書く方法を紹介します。基本的には、Mongo Shellで使用したコマンドを処理の中で使います。
詳細については、Node.js Driverの公式ドキュメントをご参照ください。
上記の公式ドキュメントで紹介されている書き方を参考に紹介しますが、ポイントを箇条書きにして解説すると以下のような書き方となっています。
- CRUDの処理ごとに関数として定義している
- それぞれの関数は、取得したDatabaseとコールバック関数を引数を通して受け取っている
- 関数内では、使用するCollectionを取得して、
collection
変数に代入している collection
変数を使ってCollectionの操作(CRUDなど)を行っている- Collectionのメソッドの第二引数には、「エラーと結果」を受け取るコールバック関数を指定している
- Collectionのメソッドで受け取るコールバック関数内では以下を行っている
- 受け取った結果やエラーを元にしたアサーションテストを行っている
- 関数で受け取ったコールバック関数
callback()
の引数に結果を渡している
上記を踏まえて、CRUDを行う関数をそれぞれ定義すると以下のようになります。詳細はコード内のコメントを参照してください。
Create: 生成
/** Create */
function insertDocuments(db, callback) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** collectionに3つのdocumentを追加 */
collection.insertMany(
[{ name: 'Dan', age: 18 }, { name: 'Bob', age: 22 }, { name: 'John', age: 30 }],
(err, result) => {
// アサーションテスト
assert.equal(err, null);
assert.equal(3, result.result.n);
assert.equal(3, result.ops.length);
/** 成功した旨をコンソールに出力 */
console.log('Inserted 3 documents into the collection');
/** 結果を引数に指定してコールバック関数を実行 */
callback(result);
},
);
}
Read: 読み取り
/** Read */
function findDocuments(db, callback) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** documentを検索(documentのnameを全件取得) */
collection
.find({})
.project({ name: 1 })
.toArray((err, docs) => {
// アサーションテスト
assert.equal(err, null);
/** 成功した旨をコンソールに出力 */
console.log('Found the following records');
/** 検索結果をコンソールに出力 */
console.log(docs);
/** 結果を引数に指定してコールバック関数を実行 */
callback(docs);
});
}
Update: 更新
/** Update */
function updateDocument(db, callback) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** documentを更新(ageが19以下のdocumentに{ status: false }を追加) */
collection.updateMany({ age: { $lt: 20 } }, { $set: { status: false } }, (err, result) => {
// アサーションテスト
assert.equal(err, null);
assert.equal(1, result.result.n);
/** 成功した旨をコンソールに出力 */
console.log('Updated the document with the field a equal to 2');
/** 結果を引数に指定してコールバック関数を実行 */
callback(result);
});
}
Create: 削除
/** Delete */
function removeDocument(db, callback) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** documentを削除(statusがfalseのdocuentを削除) */
collection.deleteMany({ status: false }, (err, result) => {
// アサーションテスト
assert.equal(err, null);
assert.equal(1, result.result.n);
/** 成功した旨をコンソールに出力 */
console.log('Removed the document with the field a equal to 3');
/** 結果を引数に指定してコールバック関数を実行 */
callback(result);
});
};
定義したCRUDの関数は、MongoClient.connect()
メソッドのコールバック関数内で呼び出して使います。
/** DBサーバに接続 */
MongoClient.connect(url, { useNewUrlParser: true }, (err, client) => {
/** errがnullではなかったら処理を停止 */
assert.equal(null, err);
/** 成功した旨をコンソールに出力 */
console.log('Connected successfully to server');
/** DBを取得 */
const db = client.db(dbName);
/** Create */
insertDocuments(db, () => {
/** Update */
updateDocument(db, () => {
/** Read */
findDocuments(db, () => {
/** Delete */
removeDocument(db, () => {
/** Read */
findDocuments(db, () => {
/** DBサーバとの接続解除 */
client.close();
});
});
});
});
});
});
上記のコードを実行すると、以下のようにコンソールに結果が表示されます。
$ node app.js
Now browse to localhost:4000/graphql
Connected successfully to server
Inserted 3 documents into the collection
Updated the document with the field a equal to 2
Found the following records
[ { _id: 5b98f7de62b11ea6259a609f, name: 'Dan' },
{ _id: 5b98f7de62b11ea6259a60a0, name: 'Bob' },
{ _id: 5b98f7de62b11ea6259a60a1, name: 'John' } ]
Removed the document with the field a equal to 3
Found the following records
[ { _id: 5b98f7de62b11ea6259a60a0, name: 'Bob' },
{ _id: 5b98f7de62b11ea6259a60a1, name: 'John' } ]
上記のコードはGitHubにもアップしています。コード全体は以下よりご確認ください。
async/awaitで書き換える
なお、上記のコードのように連続して処理を行おうとすると、コールバック地獄に陥りやすいということがわかります。そこで、このコールバック地獄を避けるために、async/await
を使って書く方法も公式ドキュメントでは紹介されています。
MongoClient.connect()
メソッド内の処理を以下のように書き換えます。詳細はコード内のコメントを参照してください。
/** 即時関数をasync functionとして定義 */
(async function () {
let client;
try {
/** DBサーバに接続 */
client = await MongoClient.connect(
url,
{ useNewUrlParser: true },
);
/** DBを取得 */
const db = client.db(dbName);
/** CRUDを行う関数をawitで待機させる */
await insertDocuments(db); // Create
await updateDocument(db); // Update
await findDocuments(db); // Read
await removeDocument(db); // Delete
await findDocuments(db); // Read
} catch (err) {
/** DBサーバ接続に失敗した時の処理 */
console.log(err.stack);
}
/** DBサーバとの接続解除 */
client.close();
})();
CRUDを行う関数も若干変更が必要です。コールバック関数が必要なくなるので、以下のようにコールバック関数を指定していた部分をそれぞれ削除します。
/** Create */
function insertDocuments(db) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** collectionにdocumentを追加 */
collection.insertMany(
[{ name: 'Dan', age: 18 }, { name: 'Bob', age: 22 }, { name: 'John', age: 30 }],
(err, result) => {
// アサーションテスト
assert.equal(err, null);
assert.equal(3, result.result.n);
assert.equal(3, result.ops.length);
/** 成功した旨をコンソールに出力 */
console.log('Inserted 3 documents into the collection');
},
);
}
/** Read */
function findDocuments(db) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** documentを検索(ageが20以上のdocumentのnameを取得) */
collection
.find({})
.project({ name: 1 })
.toArray((err, docs) => {
// アサーションテスト
assert.equal(err, null);
/** 成功した旨をコンソールに出力 */
console.log('Found the following records');
/** 検索結果をコンソールに出力 */
console.log(docs);
});
}
/** Update */
function updateDocument(db) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** documentを更新(ageが19以下のdocumentに{ status: false }を追加) */
collection.updateMany({ age: { $lt: 20 } }, { $set: { status: false } }, (err, result) => {
// アサーションテスト
assert.equal(err, null);
assert.equal(1, result.result.n);
/** 成功した旨をコンソールに出力 */
console.log('Updated the document with the field a equal to 2');
});
}
/** Delete */
function removeDocument(db) {
/** collectionを取得 */
const collection = db.collection('myCollection');
/** documentを削除(statusがfalseのdocuentを削除) */
collection.deleteMany({ status: false }, (err, result) => {
// アサーションテスト
assert.equal(err, null);
assert.equal(1, result.result.n);
/** 成功した旨をコンソールに出力 */
console.log('Removed the document with the field a equal to 3');
});
}
上記のコードを実行すると、以下のようにコンソールに結果が表示されます。
$ node app2.js
Now browse to localhost:4000/graphql
Inserted 3 documents into the collection
Updated the document with the field a equal to 2
Found the following records
[ { _id: 5b9900db70d05cb1cc01b8d5, name: 'Dan' },
{ _id: 5b9900db70d05cb1cc01b8d6, name: 'Bob' },
{ _id: 5b9900db70d05cb1cc01b8d7, name: 'John' } ]
Removed the document with the field a equal to 3
Found the following records
[ { _id: 5b9900db70d05cb1cc01b8d6, name: 'Bob' },
{ _id: 5b9900db70d05cb1cc01b8d7, name: 'John' } ]
上記のコードはGitHubにもアップしています。コード全体は以下よりご確認ください。
まとめ
MongoDBの基本的な使い方を紹介してきましたが、RDBMSで扱うようなSQLは書く必要がなく、JavaScriptを書く感覚でデータベースを操作できるというところが面白いと思っています。インデックスやMongoDB 4.0からサポートされたトランザクションなどもこれから徐々に試していきたいと思っているところです。
今回の記事でも紹介したNode.js Driverを使うと、Node.jsでMongoDBを扱うのもそれほど難しくないと思うので、expressを使ってREST APIやGraphQLの実装も試してみたくなります。今回の記事を書いたのも、GraphQLの勉強がてら、MongoDBとGraphQLを連携させて見ようというところから、MongoDBを試そうと思ったからです。したがって、MongoDBを使ってGraphQLを実装する方法などもまとめ次第このブログで紹介していければと思っています。
コメント