MongoDBをノードアプリケーションと統合する方法

提供:Dev Guides
移動先:案内検索

序章

Node.js を使用していると、データを保存してクエリするプロジェクトを開発していることに気付くかもしれません。 この場合、アプリケーションのデータとクエリの種類に適したデータベースソリューションを選択する必要があります。

このチュートリアルでは、MongoDBデータベースを既存のノードアプリケーションと統合します。 MongoDBのようなNoSQLデータベースは、データ要件にスケーラビリティと柔軟性が含まれている場合に役立ちます。 MongoDBは、 JSON オブジェクトと非同期で動作するように設計されているため、Nodeともうまく統合できます。

MongoDBをプロジェクトに統合するには、 Object Document Mapper (ODM) Mongoose を使用して、アプリケーションデータのスキーマとモデルを作成します。 これにより、 model-view-controller(MVC)アーキテクチャパターンに従ってアプリケーションコードを整理できます。これにより、アプリケーションがユーザー入力を処理する方法のロジックを、データの構造化およびレンダリング方法から分離できます。ユーザー。 このパターンを使用すると、関心の分離をコードベースに導入することで、将来のテストと開発を容易にすることができます。

チュートリアルの最後に、お気に入りのサメに関するユーザーの入力を受け取り、結果をブラウザーに表示する、機能するサメ情報アプリケーションがあります。

前提条件

  • Ubuntu 18.04を実行しているローカル開発マシンまたはサーバー、およびsudo特権とアクティブなファイアウォールを持つ非rootユーザー。 18.04サーバーでこれらをセットアップする方法のガイダンスについては、この初期サーバーセットアップガイドを参照してください。
  • Node.jsとnpmがマシンまたはサーバーにインストールされ、NodeSourceによって管理されるPPAを使用してインストールするためのこれらの手順に従います。
  • Ubuntu 18.04にMongoDBをインストールする方法のステップ1に従って、マシンまたはサーバーにMongoDBをインストールします。

ステップ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データベースへの無制限のアクセスが原因で、シェルを開くと、いくつかの管理上の警告が表示されます。 このアクセスの制限について詳しくは、 Ubuntu 16.04にMongoDBをインストールして保護する方法を読んで、本番環境に移行する際に確認してください。

今のところ、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ライブラリを追加します。これにより、データベース内のコレクションのスキーマとモデルを実装できます。

ステップ2—マングースとデータベースの情報をプロジェクトに追加する

次のステップは、アプリケーションスターターコードのクローンを作成し、MongooseとMongoDBデータベース情報をプロジェクトに追加することです。

root以外のユーザーのホームディレクトリで、 DigitalOceanCommunityGitHubアカウントからnodejs-image-demoリポジトリのクローンを作成します。 このリポジトリには、Dockerを使用してNode.jsアプリケーションを構築する方法で説明されているセットアップのコードが含まれています。

リポジトリをnode_projectというディレクトリに複製します。

git clone https://github.com/do-community/nodejs-image-demo.git node_project

node_projectディレクトリに移動します。

cd  node_project

プロジェクトコードを変更する前に、treeコマンドを使用してプロジェクトの構造を見てみましょう。

ヒント: tree is a useful command for viewing file and directory structures from the command line. You can install it with the following command:

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コマンドを使用して、mongoosenpmパッケージをプロジェクトに追加します。

npm install mongoose

このコマンドは、プロジェクトのpackage.jsonファイルにリストされている依存関係を使用して、プロジェクトディレクトリにnode_modulesディレクトリを作成し、そのディレクトリにmongooseを追加します。 また、package.jsonファイルにリストされている依存関係にmongooseを追加します。 package.jsonの詳細については、Dockerを使用してNode.jsアプリケーションを構築する方法のステップ1を参照してください。

Mongooseスキーマまたはモデルを作成する前に、アプリケーションがデータベースに接続できるように、データベース接続情報を追加します。

アプリケーションの懸念を可能な限り分離するために、db.jsと呼ばれるデータベース接続情報用の別のファイルを作成します。 このファイルは、nanoまたはお気に入りのエディターで開くことができます。

nano db.js

まず、require関数を使用して、mongooseモジュールをインポートします。

〜/ node_project / db.js

const mongoose = require('mongoose');

これにより、データベースへの接続を作成するために使用するMongooseの組み込みメソッドにアクセスできるようになります。

次に、次の定数を追加して、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で、ユーザーのauthSourceadminデータベースとして指定していることに注意してください。 これは、接続文字列にユーザー名を指定したために必要です。 useNewUrlParserフラグをmongoose.connect()とともに使用すると、Mongoの新しいURLパーサーを使用することを指定します。

編集が終了したら、ファイルを保存して閉じます。

最後のステップとして、データベース接続情報を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コレクションのデータを形成するスキーマとモデルを作成する準備が整います。

ステップ3—マングースのスキーマとモデルを作成する

次のステップは、ユーザーが入力を使用してsharkinfoデータベースに作成するsharksコレクションの構造について考えることです。 これらの作成されたドキュメントにどのような構造を持たせたいですか? 現在のアプリケーションのサメ情報ページには、さまざまなサメとその行動に関する詳細が含まれています。

このテーマに沿って、ユーザーに全体的な性格の詳細を含む新しいサメを追加してもらうことができます。 この目標は、スキーマの作成方法を形作ります。

スキーマとモデルをアプリケーションの他の部分と区別するために、現在のプロジェクトディレクトリにmodelsディレクトリを作成します。

mkdir models

次に、sharks.jsというファイルを開いて、スキーマとモデルを作成します。

nano models/sharks.js

ファイルの先頭にあるmongooseモジュールをインポートします。

〜/ node_project / models / sharks.js

const mongoose = require('mongoose');

この下に、サメスキーマの基礎として使用するSchemaオブジェクトを定義します。

〜/ node_project / models / sharks.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

これで、スキーマに含めるフィールドを定義できます。 個々のサメとその行動に関する情報を含むコレクションを作成したいので、nameキーcharacterキーを含めましょう。 次のSharkスキーマを定数定義の下に追加します。

〜/ node_project / models / sharks.js

...
const Shark = new Schema ({
        name: { type: String, required: true },
        character: { type: String, required: true },
});

この定義には、ユーザーに期待される入力のタイプ(この場合は、 string )と、その入力が必要かどうかに関する情報が含まれています。

最後に、Mongooseの model()関数を使用してSharkモデルを作成します。 このモデルを使用すると、コレクションからドキュメントをクエリして、新しいドキュメントを検証できます。 ファイルの最後に次の行を追加します。

〜/ node_project / models / sharks.js

...
module.exports = mongoose.model('Shark', Shark)

この最後の行は、[X23X]module.exportsプロパティを使用して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スキーマとモデルを配置すると、アプリケーションがユーザー入力を処理する方法を決定するロジックの作業を開始できます。

ステップ4—コントローラーの作成

次のステップは、ユーザー入力をデータベースに保存してユーザーに返す方法を決定するコントローラーコンポーネントを作成することです。

まず、コントローラーのディレクトリを作成します。

mkdir controllers

次に、そのフォルダにあるsharks.jsというファイルを開きます。

nano controllers/sharks.js

ファイルの先頭で、Sharkモデルを使用してモジュールをインポートし、コントローラーのロジックで使用できるようにします。 また、パスモジュールをインポートして、ユーザーがサメを入力するフォームへのパスを設定できるユーティリティにアクセスします。

次のrequire関数をファイルの先頭に追加します。

〜/ node_project / controllers / sharks.js

const path = require('path');
const Shark = require('../models/sharks');

次に、ノードのエクスポートショートカットを使用して、コントローラーモジュールでエクスポートする一連の関数を記述します。 これらの関数には、ユーザーのサメデータに関連する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と呼ばれる新しいサメドキュメントオブジェクトを作成します。インポートしました。 POSTメソッドが意図したとおりに機能していることを確認するために、コンソールにsharkエントリを出力する console.logメソッドを追加しましたが、必要に応じてこれを省略してください。

次に、newSharkオブジェクトを使用して、create関数はMongooseのmodel.save()メソッドを呼び出し、で定義したキーを使用して新しいサメドキュメントを作成します。 【X173X】モデル。 このコールバック関数は、標準ノードコールバックパターンcallback(error, results)に従います。 エラーの場合は、エラーを報告するメッセージをユーザーに送信し、成功した場合は、 res.redirect()メソッドを使用して、ユーザーをエンドポイントに送信します。彼らのサメの情報をブラウザで彼らに返します。

最後に、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モデルとMongooseのmodel.find()メソッドを使用して、sharksコレクションに入力されたサメを返します。 これは、Mongooseの exec()関数を使用して、クエリオブジェクト(この場合は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
             });
        });
};

ここでは矢印関数を使用していませんが、独自の開発プロセスでこのコードを反復処理するときに、それらを含めることをお勧めします。

編集が終了したら、ファイルを保存して閉じます。

次の手順に進む前に、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

これで、ユーザー入力を保存してユーザーに返す方法を指示するコントローラーコンポーネントができたので、コントローラーのロジックを実装するビューの作成に進むことができます。

ステップ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データにアクセスできるようになります。 trueextendedオプションで指定して、アプリケーションが解析するデータのタイプ(ネストされたオブジェクトなど)の柔軟性を高めています。 オプションの詳細については、機能ドキュメントを参照してください。

編集が終了したら、ファイルを保存して閉じます。

次に、ビューにテンプレート機能を追加します。 まず、ejsパッケージnpm installとともにインストールします。

npm install ejs

次に、viewsフォルダにあるsharks.htmlファイルを開きます。

nano views/sharks.html

ステップ3では、このページを見て、マングースのスキーマとモデルをどのように作成するかを決定しました。

ここでは、2列の layout ではなく、ユーザーがサメに関する情報を入力できるフォームを備えた3番目の列を紹介します。

最初のステップとして、既存の列の寸法を4に変更して、3つの同じサイズの列を作成します。 現在<div class="col-lg-6">と表示されている2行でこの変更を行う必要があることに注意してください。 これらは両方とも<div class="col-lg-4">になります。

〜/ node_project / views / sharks.html

...
<div class="container">
    <div class="row">
        <div class="col-lg-4">
            <p>
                <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
                </div>
                <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
            </p>
        </div>
        <div class="col-lg-4">
            <p>
                <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
            </p>
        </div>
    </div>
  </div>

 </html> 

行と列のレイアウトを含むBootstrapのグリッドシステムの概要については、このBootstrapの概要を参照してください。

次に、POSTリクエストの名前付きエンドポイントを含む別の列を追加します。この列には、ユーザーのsharkデータとそのデータをキャプチャするEJSテンプレートタグが含まれます。 この列は、前の列の終了</p>および</div>タグの下、および行、コンテナー、およびHTMLドキュメントの終了タグの上に配置されます。 これらの終了タグは、コードにすでに配置されています。 それらはまたコメントで下にマークされています。 次のコードを追加して新しい列を作成するときは、そのままにしておきます。

〜/ node_project / views / sharks.html

...
       </p> <!-- closing p from previous column -->
   </div> <!-- closing div from previous column -->
<div class="col-lg-4">
            <p>
                <form action="/sharks/addshark" method="post">
                    <div class="caption">Enter Your Shark</div>
                    <input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>
                    <input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>
                    <button type="submit">Submit</button>
                </form>
            </p>
        </div> 
    </div> <!-- closing div for row -->
</div> <!-- closing div for container -->

</html> <!-- closing html tag -->

formタグで、ユーザーのサメデータの"/sharks/addshark"エンドポイントを追加し、送信するPOSTメソッドを指定します。 入力フィールドでは、"Shark Name"および"Shark Character"のフィールドを指定し、前に定義したSharkモデルに合わせます。

sharksコレクションにユーザー入力を追加するには、JavaScript構文とともにEJSテンプレートタグ(<%=%>)を使用して、ユーザーのエントリを新しく作成されたドキュメント。 JavaScriptオブジェクトの詳細については、JavaScriptオブジェクトについての記事を参照してください。 EJSテンプレートタグの詳細については、EJSドキュメントを参照してください。

shark入力フォームの列を含む、3つの列すべてを含むコンテナ全体は、終了すると次のようになります。

〜/ node_project / views / sharks.html

...
<div class="container">
    <div class="row">
        <div class="col-lg-4">
            <p>
                <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
                </div>
                <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
            </p>
        </div>
        <div class="col-lg-4">
            <p>
                <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
            </p>
        </div>
    <div class="col-lg-4">
            <p>
                <form action="/sharks/addshark" method="post">
                    <div class="caption">Enter Your Shark</div>
                    <input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>
                    <input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>
                    <button type="submit">Submit</button>
                </form>
            </p>
        </div>
    </div>
  </div>

</html>

編集が終了したら、ファイルを保存して閉じます。

ユーザーの入力を収集する方法ができたので、返されたサメとそれに関連するキャラクター情報を表示するエンドポイントを作成できます。

新しく変更したsharks.htmlファイルをgetshark.htmlというファイルにコピーします。

cp views/sharks.html views/getshark.html

getshark.htmlを開きます:

nano views/getshark.html

ファイル内で、サメの入力フォームの作成に使用した列を、sharksコレクションのサメを表示する列に置き換えることで変更します。 この場合も、コードは、前の列の既存の</p>タグと</div>タグと、行、コンテナー、およびHTMLドキュメントの終了タグの間に配置されます。 次のコードを追加して列を作成するときは、これらのタグをそのままにしておくことを忘れないでください。

〜/ node_project / views / getshark.html

...
       </p> <!-- closing p from previous column -->
   </div> <!-- closing div from previous column -->
<div class="col-lg-4">
           <p>
              <div class="caption">Your Sharks</div>
                  <ul>
                     <% sharks.forEach(function(shark) { %>
                        <p>Name: <%= shark.name %></p>
                        <p>Character: <%= shark.character %></p>
                     <% }); %>
                  </ul>
            </p>
        </div>
    </div> <!-- closing div for row -->
</div> <!-- closing div for container -->

</html> <!-- closing html tag -->

ここでは、EJSテンプレートタグと forEach()メソッドを使用して、最近追加されたサメに関する情報を含むsharksコレクションの各値を出力しています。

sharksコレクションの列を含む、3つの列すべてのコンテナー全体は、終了すると次のようになります。

〜/ node_project / views / getshark.html

...
<div class="container">
    <div class="row">
        <div class="col-lg-4">
            <p>
                <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
                </div>
                <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
            </p>
        </div>
        <div class="col-lg-4">
            <p>
                <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
            </p>
        </div>
    <div class="col-lg-4">
            <p>
              <div class="caption">Your Sharks</div>
                  <ul>
                     <% sharks.forEach(function(shark) { %>
                        <p>Name: <%= shark.name %></p>
                        <p>Character: <%= shark.character %></p>
                     <% }); %>
                  </ul>
            </p>
        </div>
    </div>
  </div>

</html>

編集が終了したら、ファイルを保存して閉じます。

作成したテンプレートをアプリケーションで使用するには、app.jsファイルに数行追加する必要があります。 もう一度開きます。

nano app.js

express.urlencoded()関数を追加した場所の上に、次の行を追加します。

〜/ node_project / app.js

...
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));

...

app.engine メソッドは、EJSテンプレートエンジンをHTMLファイルにマップするようにアプリケーションに指示します。 app.set は、デフォルトのビューエンジンを定義します。

app.jsファイルは次のようになります。

〜/ node_project / app.js

const express = require('express');
const app = express();
const router = express.Router();
const db = require('./db');

const path = __dirname + '/views/';
const port = 8080;

router.use(function (req,res,next) {
  console.log('/' + req.method);
  next();
});

router.get('/',function(req,res){
  res.sendFile(path + 'index.html');
});

router.get('/sharks',function(req,res){
  res.sendFile(path + 'sharks.html');
});

app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
app.use('/', router);

app.listen(port, function () {
  console.log('Example app listening on port 8080!')
})

ユーザーデータを動的に操作できるビューを作成したので、次に、プロジェクトのルートを作成して、ビューとコントローラーロジックをまとめます。

ステップ6—ルートを作成する

アプリケーションのコンポーネントをまとめる最後のステップは、ルートを作成することです。 アプリケーションのランディングページへのルートとサメのページへの別のルートを含め、機能ごとにルートを分けます。 sharksルートは、コントローラーのロジックを前の手順で作成したビューと統合する場所になります。

まず、routesディレクトリを作成します。

mkdir routes

次に、このディレクトリにあるindex.jsというファイルを開きます。

nano routes/index.js

このファイルは、最初にexpressrouter、およびpathオブジェクトをインポートし、routerオブジェクトを使用してエクスポートするルートを定義できるようにします。ファイルパスを動的に操作できるようにします。 ファイルの先頭に次のコードを追加します。

〜/ node_project / routers / index.js

const express = require('express');
const router = express.Router();
const path = require('path');

次に、次のrouter.use関数を追加します。この関数は、ルーターの要求をログに記録してアプリケーションのルートに渡すミドルウェア関数をロードします。

〜/ node_project / routers / index.js

...

router.use (function (req,res,next) {
  console.log('/' + req.method);
  next();
});

アプリケーションのルートへのリクエストは最初にここに送信され、ここからユーザーはアプリケーションのランディングページ(次に定義するルート)に移動します。 router.use関数の下に次のコードを追加して、ランディングページへのルートを定義します。

〜/ node_project / routers / index.js

...

router.get('/',function(req,res){
  res.sendFile(path.resolve('views/index.html'));
});

ユーザーがアプリケーションにアクセスしたときに、最初に送信する場所は、viewsディレクトリにあるindex.htmlランディングページです。

最後に、これらのルートをアプリケーションの他の場所でインポート可能なモジュールとしてアクセスできるようにするには、ファイルの最後に終了式を追加して、routerオブジェクトをエクスポートします。

〜/ node_project / routers / index.js

...

module.exports = router;

完成したファイルは次のようになります。

〜/ node_project / routers / index.js

const express = require('express');
const router = express.Router();
const path = require('path');

router.use (function (req,res,next) {
  console.log('/' + req.method);
  next();
});

router.get('/',function(req,res){
  res.sendFile(path.resolve('views/index.html'));
});

module.exports = router;

編集が終了したら、このファイルを保存して閉じます。

次に、sharks.jsというファイルを開いて、ユーザーのサメの入力を処理するために作成したさまざまなエンドポイントとビューをアプリケーションがどのように使用するかを定義します。

nano routes/sharks.js

ファイルの先頭で、expressおよびrouterオブジェクトをインポートします。

〜/ node_project / routers / sharks.js

const express = require('express');
const router = express.Router();

次に、sharkというモジュールをインポートします。これにより、コントローラーで定義したエクスポートされた関数を操作できるようになります。

〜/ node_project / routers / sharks.js

const express = require('express');
const router = express.Router();
const shark = require('../controllers/sharks');

これで、sharksコントローラーファイルで定義したindexcreate、およびlist関数を使用してルートを作成できます。 各ルートは適切なHTTPメソッドに関連付けられます。メインのサメ情報のランディングページをレンダリングしてサメのリストをユーザーに返す場合はGET、新しいサメのエントリを作成する場合はPOSTです。

〜/ node_project / routers / sharks.js

...

router.get('/', function(req, res){
    shark.index(req,res);
});

router.post('/addshark', function(req, res) {
    shark.create(req,res);
});

router.get('/getshark', function(req, res) {
    shark.list(req,res);
});

このファイルの先頭にインポートすることでモジュールにアクセスできるようにしたため、各ルートはcontrollers/sharks.jsの関連機能を利用します。

最後に、これらのルートをrouterオブジェクトに添付してエクスポートし、ファイルを閉じます。

〜/ node_project / routers / index.js

...

module.exports = router;

完成したファイルは次のようになります。

〜/ node_project / routers / sharks.js

const express = require('express');
const router = express.Router();
const shark = require('../controllers/sharks');

router.get('/', function(req, res){
    shark.index(req,res);
});

router.post('/addshark', function(req, res) {
    shark.create(req,res);
});

router.get('/getshark', function(req, res) {
    shark.list(req,res);
});

module.exports = router;

編集が終了したら、ファイルを保存して閉じます。

これらのルートをアプリケーションからアクセスできるようにする最後のステップは、これらのルートをapp.jsに追加することです。 そのファイルをもう一度開きます。

nano app.js

db定数の下に、ルートに次のインポートを追加します。

〜/ node_project / app.js

...
const db = require('./db');
const sharks = require('./routes/sharks');

次に、 replace app.use関数は、現在routerオブジェクトを次の行でマウントします。これにより、sharksルーターモジュールがマウントされます。

〜/ node_project / app.js

...
app.use(express.static(path));
app.use('/sharks', sharks);

app.listen(port, function () {
        console.log("Example app listening on port 8080!")
})

sharksルーターモジュールを使用してアプリケーションのルートをインポートしているため、このファイルで以前に定義されたルートを削除できるようになりました。

app.jsファイルの最終バージョンは次のようになります。

〜/ node_project / app.js

const express = require('express');
const app = express();
const router = express.Router();
const db = require('./db');
const sharks = require('./routes/sharks');

const path = __dirname + '/views/';
const port = 8080;

app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
app.use('/sharks', sharks);

app.listen(port, function () {
  console.log('Example app listening on port 8080!')
})

編集が終了したら、ファイルを保存して閉じます。

これで、treeを再度実行して、プロジェクトの最終的な構造を確認できます。

tree -I node_modules

プロジェクト構造は次のようになります。

Output├── Dockerfile
├── README.md
├── app.js
├── controllers
│   └── sharks.js
├── db.js
├── models
│   └── sharks.js
├── package-lock.json
├── package.json
├── routes
│   ├── index.js
│   └── sharks.js
└── views
    ├── css
    │   └── styles.css
    ├── getshark.html
    ├── index.html
    └── sharks.html

すべてのアプリケーションコンポーネントが作成され、配置されたら、データベースにテストサメを追加する準備が整いました。

前提条件の初期サーバーセットアップチュートリアルに従った場合、ファイアウォールは現在SSHトラフィックのみを許可しているため、ファイアウォールを変更する必要があります。 ポート8080へのトラフィックを許可するには、次のコマンドを実行します。

sudo ufw allow 8080

アプリケーションを起動します。

node app.js

次に、ブラウザをhttp://your_server_ip:8080に移動します。 次のランディングページが表示されます。

Get SharkInfoボタンをクリックします。 サメの入力フォームが追加された次の情報ページが表示されます。

フォームに、選択したサメを追加します。 このデモンストレーションの目的で、Megalodon SharkShark Name フィールドに追加し、AncientSharkCharacterフィールドに追加します。

送信ボタンをクリックします。 このサメの情報が表示されたページが表示されます。

また、サメがコレクションに追加されたことを示す出力がコンソールに表示されます。

OutputExample app listening on port 8080!
{ name: 'Megalodon Shark', character: 'Ancient' }

新しいサメのエントリを作成する場合は、サメページに戻り、サメを追加するプロセスを繰り返します。

これで、ユーザーがお気に入りのサメに関する情報を追加できる、機能するサメ情報アプリケーションができました。

結論

このチュートリアルでは、MongoDBデータベースを統合し、MVCアーキテクチャパターンを使用してアプリケーションのロジックを書き直すことで、Nodeアプリケーションを構築しました。 このアプリケーションは、本格的なCRUDアプリケーションの出発点として適しています。

他のコンテキストでのMVCパターンに関するその他のリソースについては、Django開発シリーズまたはDjangoを使用して顧客情報を管理しUbuntu18.04でReactする最新のWebアプリケーションを構築する方法を参照してください。

MongoDBの操作の詳細については、MongoDBチュートリアルのライブラリを参照してください。