前書き
Node.jsを使用していると、データを格納および照会するプロジェクトを開発していることに気付く場合があります。 この場合、アプリケーションのデータとクエリの種類に合ったデータベースソリューションを選択する必要があります。
このチュートリアルでは、MongoDBデータベースを既存のノードアプリケーションと統合します。 MongoDBのようなNoSQL databasesは、データ要件にスケーラビリティと柔軟性が含まれている場合に役立ちます。 MongoDBは、JSONオブジェクトと非同期で動作するように設計されているため、Nodeともうまく統合できます。
MongoDBをプロジェクトに統合するには、Object Document Mapper(ODM)Mongooseを使用して、アプリケーションデータのスキーマとモデルを作成します。 これにより、model-view-controller (MVC)アーキテクチャパターンに従ってアプリケーションコードを整理できます。これにより、アプリケーションがユーザー入力を処理する方法のロジックを、データが構造化されてユーザーにレンダリングされる方法から分離できます。 このパターンを使用すると、懸念事項の分離をコードベースに導入することで、将来のテストと開発を促進できます。
チュートリアルの最後に、お気に入りのサメに関するユーザーの入力を受け取り、結果をブラウザに表示する、機能するサメ情報アプリケーションがあります。
前提条件
-
Ubuntu 18.04を実行しているローカル開発マシンまたはサーバー、および
sudo
特権とアクティブなファイアウォールを持つ非rootユーザー。 18.04サーバーでこれらを設定する方法のガイダンスについては、このInitial Server Setup guideを参照してください。 -
Node.js and npm installed on your machine or server, following these instructions on installing with the PPA managed by NodeSource.
-
How To Install MongoDB in Ubuntu 18.04のステップ1に従って、マシンまたはサーバーにインストールされたMongoDB。
[[step-1 -—- creating-a-mongo-user]] ==ステップ1—Mongoユーザーの作成
アプリケーションコードの操作を開始する前に、アプリケーションのデータベースにアクセスできる管理ユーザーを作成します。 このユーザーには、任意のデータベースに対する管理者権限があります。これにより、必要に応じて新しいデータベースを切り替えて作成する柔軟性が得られます。
まず、サーバーでMongoDBが実行されていることを確認します。
sudo systemctl status mongodb
次の出力は、MongoDBが実行されていることを示しています。
Output● mongodb.service - An object/document-oriented database
Loaded: loaded (/lib/systemd/system/mongodb.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2019-01-31 21:07:25 UTC; 21min ago
...
次に、Mongoシェルを開いてユーザーを作成します。
mongo
これにより、管理シェルに移動します。
OutputMongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.3
...
>
admin
データベースへの無制限のアクセスが原因で、シェルを開くと、いくつかの管理上の警告が表示されます。 このアクセスを制限する方法について詳しくは、本番環境に移行するときのHow To Install and Secure MongoDB on Ubuntu 16.04を参照してください。
今のところ、admin
データベースへのアクセスを使用して、userAdminAnyDatabase
権限を持つユーザーを作成できます。これにより、アプリケーションのデータベースへのパスワードで保護されたアクセスが可能になります。
シェルで、admin
データベースを使用してユーザーを作成することを指定します。
use admin
次に、db.createUser
コマンドでユーザー名とパスワードを追加して、役割とパスワードを作成します。 このコマンドを入力すると、シェルはコマンドが完了するまで各行の前に3つのドットを付加します。 ここで提供されるユーザーとパスワードを、必ず自分のユーザー名とパスワードに置き換えてください。
db.createUser(
{
user: "sammy",
pwd: "your_password",
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)
これにより、admin
データベースにユーザーsammy
のエントリが作成されます。 選択したユーザー名とadmin
データベースは、ユーザーの識別子として機能します。
エントリが成功したことを示すメッセージを含む、プロセス全体の出力は次のようになります。
Output> db.createUser(
... {
... user: "sammy",
... pwd: "your_password",
... roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
... }
...)
Successfully added user: {
"user" : "sammy",
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
}
]
}
ユーザーとパスワードを作成したら、Mongoシェルを終了できます。
exit
データベースユーザーを作成したので、次に、スタータープロジェクトコードのクローン作成とMongooseライブラリの追加に進むことができます。これにより、データベースのコレクションのスキーマとモデルを実装できます。
[[step-2 -—- adding-mongoose-and-database-information-to-the-project]] ==ステップ2—マングースとデータベースの情報をプロジェクトに追加する
次の手順では、アプリケーションのスターターコードを複製し、MongooseとMongoDBデータベース情報をプロジェクトに追加します。
root以外のユーザーのホームディレクトリで、DigitalOcean Community GitHub accountからnodejs-image-demo
repositoryのクローンを作成します。 このリポジトリには、How To Build a Node.js Application with Dockerで説明されているセットアップのコードが含まれています。
リポジトリをnode_project
というディレクトリに複製します。
git clone https://github.com/do-community/nodejs-image-demo.git node_project
node_project
ディレクトリに移動します。
cd node_project
プロジェクトコードを変更する前に、tree
コマンドを使用してプロジェクトの構造を見てみましょう。
[。注意]##
Tip:tree
は、コマンドラインからファイルとディレクトリの構造を表示するための便利なコマンドです。 次のコマンドでインストールできます:
sudo apt install tree
これを使用するには、cd
を特定のディレクトリに挿入し、tree
と入力します。 次のようなコマンドを使用して、開始点へのパスを指定することもできます。
tree /home/sammy/sammys-project
次のように入力して、node_project
ディレクトリを確認します。
tree
現在のプロジェクトの構造は次のようになります。
Output├── Dockerfile
├── README.md
├── app.js
├── package-lock.json
├── package.json
└── views
├── css
│ └── styles.css
├── index.html
└── sharks.html
チュートリアルを進めながら、このプロジェクトにディレクトリを追加します。tree
は、進行状況を追跡するのに役立つコマンドです。
次に、npm install
コマンドを使用して、mongoose
npmパッケージをプロジェクトに追加します。
npm install mongoose
このコマンドは、プロジェクトのpackage.json
ファイルにリストされている依存関係を使用して、プロジェクトディレクトリにnode_modules
ディレクトリを作成し、そのディレクトリにmongoose
を追加します。 また、package.json
ファイルにリストされている依存関係にmongoose
を追加します。 package.json
の詳細については、How To Build a Node.js Application with DockerのStep 1を参照してください。
Mongooseのスキーマまたはモデルを作成する前に、データベース接続情報を追加して、アプリケーションがデータベースに接続できるようにします。
アプリケーションの懸念事項を可能な限り分離するために、データベース接続情報用にdb.js
という別のファイルを作成します。 このファイルは、nano
またはお気に入りのエディターで開くことができます。
nano db.js
まず、require
関数を使用してmongoose
moduleをインポートします。
~/node_project/db.js
const mongoose = require('mongoose');
これにより、データベースへの接続を作成するために使用するMongooseの組み込みメソッドにアクセスできます。
次に、次のconstantsを追加して、Mongoの接続URIの情報を定義します。 ユーザー名とパスワードはオプションですが、データベースに認証を要求できるようにそれらを含めます。 以下にリストされているユーザー名とパスワードを自分の情報に置き換えてください。必要に応じて、データベースを'sharkinfo'
以外の名前で自由に呼び出してください。
~/node_project/db.js
const mongoose = require('mongoose');
const MONGO_USERNAME = 'sammy';
const MONGO_PASSWORD = 'your_password';
const MONGO_HOSTNAME = '127.0.0.1';
const MONGO_PORT = '27017';
const MONGO_DB = 'sharkinfo';
データベースをローカルで実行しているため、ホスト名として127.0.0.1
を使用しました。 これは、他の開発コンテキストで変更されます。たとえば、別のデータベースサーバーを使用している場合や、コンテナ化されたワークフローで複数のノードを操作している場合です。
最後に、URIの定数を定義し、mongoose.connect()
メソッドを使用して接続を作成します。
~/node_project/db.js
...
const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;
mongoose.connect(url, {useNewUrlParser: true});
URIで、ユーザーのauthSource
をadmin
データベースとして指定していることに注意してください。 接続文字列でユーザー名を指定したため、これが必要です。 useNewUrlParser
フラグをmongoose.connect()
とともに使用すると、Mongoのnew URL parserを使用することを指定します。
編集が終了したら、ファイルを保存して閉じます。
最後のステップとして、データベース接続情報をapp.js
ファイルに追加して、アプリケーションがそれを使用できるようにします。 app.js
を開きます:
nano app.js
ファイルの最初の行は次のようになります。
~/node_project/app.js
const express = require('express');
const app = express();
const router = express.Router();
const path = __dirname + '/views/';
...
ファイルの先頭近くにあるrouter
定数定義の下に、次の行を追加します。
~/node_project/app.js
...
const router = express.Router();
const db = require('./db');
const path = __dirname + '/views/';
...
これは、db.js
で指定されたデータベース接続情報を使用するようにアプリケーションに指示します。
編集が終了したら、ファイルを保存して閉じます。
データベース情報を配置し、Mongooseをプロジェクトに追加すると、sharks
コレクションのデータを形成するスキーマとモデルを作成する準備が整います。
[[step-3 -—- creating-mongoose-schemas-and-models]] ==ステップ3—Mongooseスキーマとモデルの作成
次のステップは、ユーザーが入力を使用してsharkinfo
データベースに作成するsharks
コレクションの構造について考えることです。 これらの作成されたドキュメントにどのような構造を持たせたいですか? 現在のアプリケーションのサメ情報ページには、さまざまなサメとその行動に関する詳細が含まれています。
このテーマに沿って、ユーザーにキャラクター全体の詳細を含む新しいサメを追加してもらうことができます。 この目標により、スキーマの作成方法が決まります。
スキーマとモデルをアプリケーションの他の部分と区別するために、現在のプロジェクトディレクトリにmodels
ディレクトリを作成します。
mkdir models
次に、sharks.js
というファイルを開いて、スキーマとモデルを作成します。
nano models/sharks.js
ファイルの先頭にあるmongoose
モジュールをインポートします。
~/node_project/models/sharks.js
const mongoose = require('mongoose');
この下に、sharkスキーマの基礎として使用するSchema
オブジェクトを定義します。
~/node_project/models/sharks.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
これで、スキーマに含めるフィールドを定義できます。 個々のサメとその行動に関する情報を含むコレクションを作成したいので、name
keyキーとcharacter
キーを含めましょう。 定数定義の下に次のShark
スキーマを追加します。
~/node_project/models/sharks.js
...
const Shark = new Schema ({
name: { type: String, required: true },
character: { type: String, required: true },
});
この定義には、ユーザーに期待する入力のタイプ(この場合はstring)と、その入力が必要かどうかに関する情報が含まれています。
最後に、Mongooseのmodel()
functionを使用してShark
モデルを作成します。 このモデルを使用すると、コレクションからドキュメントを照会し、新しいドキュメントを検証できます。 ファイルの下部に次の行を追加します。
~/node_project/models/sharks.js
...
module.exports = mongoose.model('Shark', Shark)
この最後の行は、module.exports
propertyを使用するモジュールとしてShark
モデルを使用できるようにします。 このプロパティは、モジュールがエクスポートする値を定義し、アプリケーションの他の場所で使用できるようにします。
完成したmodels/sharks.js
ファイルは次のようになります。
~/node_project/models/sharks.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Shark = new Schema ({
name: { type: String, required: true },
character: { type: String, required: true },
});
module.exports = mongoose.model('Shark', Shark)
編集が終了したら、ファイルを保存して閉じます。
Shark
スキーマとモデルを配置したら、アプリケーションがユーザー入力を処理する方法を決定するロジックの作業を開始できます。
[[step-4 -—- creating-controllers]] ==ステップ4—コントローラーの作成
次のステップでは、ユーザー入力がデータベースに保存され、ユーザーに返される方法を決定するコントローラーコンポーネントを作成します。
まず、コントローラーのディレクトリを作成します。
mkdir controllers
次に、そのフォルダー内のsharks.js
というファイルを開きます。
nano controllers/sharks.js
ファイルの先頭で、Shark
モデルを使用してモジュールをインポートし、コントローラーのロジックで使用できるようにします。 また、path
moduleをインポートしてユーティリティにアクセスし、ユーザーがサメを入力するフォームへのパスを設定できるようにします。
次のrequire
関数をファイルの先頭に追加します。
~/node_project/controllers/sharks.js
const path = require('path');
const Shark = require('../models/sharks');
次に、ノードのexports
shortcutを使用して、コントローラーモジュールでエクスポートする一連の関数を記述します。 これらの機能には、ユーザーのサメデータに関連する3つのタスクが含まれます。
-
ユーザーにサメの入力フォームを送信します。
-
新しいサメのエントリを作成します。
-
サメをユーザーに表示します。
まず、index
関数を作成して、入力フォームを含むサメのページを表示します。 この関数をインポートの下に追加します。
~/node_project/controllers/sharks.js
...
exports.index = function (req, res) {
res.sendFile(path.resolve('views/sharks.html'));
};
次に、index
関数の下に、create
という関数を追加して、sharks
コレクションに新しいサメのエントリを作成します。
~/node_project/controllers/sharks.js
...
exports.create = function (req, res) {
var newShark = new Shark(req.body);
console.log(req.body);
newShark.save(function (err) {
if(err) {
res.status(400).send('Unable to save shark to database');
} else {
res.redirect('/sharks/getshark');
}
});
};
この関数は、ユーザーがサメのデータをsharks.html
ページのフォームに投稿したときに呼び出されます。 チュートリアルの後半でアプリケーションのルートを作成するときに、このPOSTエンドポイントを使用してルートを作成します。 POSTリクエストのbody
を使用して、create
関数は、インポートしたShark
モデルを使用して、ここではnewShark
と呼ばれる新しいsharkドキュメントオブジェクトを作成します。 POSTメソッドが意図したとおりに機能していることを確認するために、コンソールにsharkエントリを出力するconsole.log
methodを追加しましたが、必要に応じてこれを省略してください。
次に、newShark
オブジェクトを使用して、create
関数はMongooseのmodel.save()
methodを呼び出し、Shark
モデルで定義したキーを使用して新しいサメドキュメントを作成します。 このcallback functionは、standard Node callback patternの後に続きます:callback(error, results)
。 エラーの場合は、エラーを報告するメッセージをユーザーに送信します。成功した場合は、res.redirect()
methodを使用してユーザーをエンドポイントに送信し、サメの情報をユーザーに返します。ブラウザで。
最後に、list
関数は、コレクションのコンテンツをユーザーに表示します。 create
関数の下に次のコードを追加します。
~/node_project/controllers/sharks.js
...
exports.list = function (req, res) {
Shark.find({}).exec(function (err, sharks) {
if (err) {
return res.send(500, err);
}
res.render('getshark', {
sharks: sharks
});
});
};
この関数は、Shark
モデルとマングースのmodel.find()
methodを使用して、sharks
コレクションに入力されたサメを返します。 これは、Mongooseのexec()
functionを使用して、クエリオブジェクト(この場合はsharks
コレクション内のすべてのエントリ)をpromiseとして返すことによって行われます。 エラーの場合、コールバック関数は500エラーを送信します。
sharks
コレクションで返されたクエリオブジェクトは、EJSテンプレート言語を使用して次のステップで作成するgetshark
ページにレンダリングされます。
完成したファイルは次のようになります。
~/node_project/controllers/sharks.js
const path = require('path');
const Shark = require('../models/sharks');
exports.index = function (req, res) {
res.sendFile(path.resolve('views/sharks.html'));
};
exports.create = function (req, res) {
var newShark = new Shark(req.body);
console.log(req.body);
newShark.save(function (err) {
if(err) {
res.status(400).send('Unable to save shark to database');
} else {
res.redirect('/sharks/getshark');
}
});
};
exports.list = function (req, res) {
Shark.find({}).exec(function (err, sharks) {
if (err) {
return res.send(500, err);
}
res.render('getshark', {
sharks: sharks
});
});
};
ここではarrow functionsを使用していませんが、独自の開発プロセスでこのコードを反復処理するときに、arrow functionsを含めることをお勧めします。
編集が終了したら、ファイルを保存して閉じます。
次の手順に進む前に、node_project
ディレクトリからtree
を再度実行して、この時点でのプロジェクトの構造を表示できます。 今回は、簡潔にするために、-I
オプションを使用してnode_modules
ディレクトリを省略するようにtree
に指示します。
tree -I node_modules
追加したことで、プロジェクトの構造は次のようになります。
Output├── Dockerfile
├── README.md
├── app.js
├── controllers
│ └── sharks.js
├── db.js
├── models
│ └── sharks.js
├── package-lock.json
├── package.json
└── views
├── css
│ └── styles.css
├── index.html
└── sharks.html
ユーザー入力を保存してユーザーに返す方法を指示するコントローラーコンポーネントが用意できたので、コントローラーのロジックを実装するビューの作成に進むことができます。
[[step-5 --- using-ejs-and-express-middleware-to-collect-and-render-data]] ==ステップ5—EJSとExpressミドルウェアを使用してデータを収集およびレンダリングする
アプリケーションがユーザーデータを処理できるようにするために、2つのことを行います。1つは、アプリケーションがユーザーの入力データを解析できるようにする組み込みのExpressミドルウェア関数urlencoded()
を含めることです。 次に、テンプレートタグをビューに追加して、コード内のユーザーデータとの動的な対話を可能にします。
Expressのurlencoded()
関数を使用するには、最初にapp.js
ファイルを開きます。
nano app.js
express.static()
関数の上に、次の行を追加します。
~/node_project/app.js
...
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
...
この関数を追加すると、サメ情報フォームから解析済みのPOSTデータにアクセスできるようになります。 extended
オプションでtrue
を指定して、アプリケーションが解析するデータのタイプ(ネストされたオブジェクトなどを含む)の柔軟性を高めています。 オプションの詳細については、function documentationを参照してください。
編集が終了したら、ファイルを保存して閉じます。
次に、テンプレート機能をビューに追加します。 まず、npm install
とともにejs
packageをインストールします。
npm install ejs
次に、views
フォルダーにあるsharks.html
ファイルを開きます。
nano views/sharks.html
ステップ3では、このページを見て、Mongooseのスキーマとモデルを記述する方法を決定しました。
ここで、2つの列layoutを使用するのではなく、ユーザーがサメに関する情報を入力できるフォームを備えた3番目の列を紹介します。
最初のステップとして、既存の列の次元を4
に変更して、3つの同じサイズの列を作成します。 現在<div class="col-lg-6">
を読み取っている2行でこの変更を行う必要があることに注意してください。 これらは両方とも<div class="col-lg-4">
になります。
~/node_project/views/sharks.html
...
行と列のレイアウトを含むBootstrapのグリッドシステムの概要については、このintroduction to Bootstrapを参照してください。
次に、ユーザーのサメデータとそのデータをキャプチャするEJSテンプレートタグを含むPOSTリクエストの名前付きエンドポイントを含む別の列を追加します。 この列は、前の列の終了</p>
タグと</div>
タグの下、および行、コンテナー、およびHTMLドキュメントの終了タグの上に配置されます。 これらの終了タグは既にコード内に配置されています。また、それらにはコメントが付いています。 次のコードを追加して新しい列を作成するときに、それらをそのまま残します。
~/node_project/views/sharks.html
...