PostgreSQLでExpressAPIバックエンドプロジェクトを設定する方法
公開: 2022-03-10テスト駆動開発(TDD)アプローチを採用し、継続的インテグレーション(CI)ジョブを設定して、コード品質とカバレッジレポートを備えたTravisCIとAppVeyorでテストを自動的に実行します。 コントローラ、モデル(PostgreSQLを使用)、エラー処理、および非同期Expressミドルウェアについて学習します。 最後に、Herokuで自動デプロイを構成して、CI / CDパイプラインを完成させます。
たくさんのように聞こえますが、このチュートリアルは、ある程度複雑なバックエンドプロジェクトを試す準備ができていて、実際のプロジェクトですべての要素がどのように組み合わされるかについてまだ混乱している可能性がある初心者を対象としています。 。
圧倒されることなく堅牢であり、妥当な時間で完了することができるセクションに分割されています。
入門
最初のステップは、プロジェクトの新しいディレクトリを作成し、新しいノードプロジェクトを開始することです。 このチュートリアルを続行するには、ノードが必要です。 インストールしていない場合は、公式Webサイトにアクセスし、ダウンロードしてインストールしてから続行してください。
このプロジェクトのパッケージマネージャーとしてyarnを使用します。 ここに、特定のオペレーティングシステムのインストール手順があります。 必要に応じて、npmを自由に使用してください。
ターミナルを開き、新しいディレクトリを作成して、Node.jsプロジェクトを開始します。
# create a new directory mkdir express-api-template # change to the newly-created directory cd express-api-template # initialize a new Node.js project npm init
以下の質問に答えて、 package.jsonファイルを生成します。 このファイルには、プロジェクトに関する情報が含まれています。 このような情報の例には、使用する依存関係、プロジェクトを開始するためのコマンドなどが含まれます。
これで、選択したエディターでプロジェクトフォルダーを開くことができます。 Visual StudioCodeを使用しています。 それはあなたの生活を楽にするためのたくさんのプラグインを備えた無料のIDEであり、すべての主要なプラットフォームで利用可能です。 公式サイトからダウンロードできます。
プロジェクトフォルダに次のファイルを作成します。
- README.md
- .editorconfig
これは、EditorConfigWebサイトからの.editorconfigの機能の説明です。 (ソロで作業している場合はおそらく必要ありませんが、害はないので、ここに残しておきます。)
「EditorConfigは、さまざまなエディターやIDEで同じプロジェクトに取り組んでいる複数の開発者の一貫したコーディングスタイルを維持するのに役立ちます。」
.editorconfig
を開き、次のコードを貼り付けます。
root = true [*] indent_style = space indent_size = 2 charset = utf-8 trim_trailing_whitespace = false insert_final_newline = true
[*]
は、その下にあるルールをプロジェクト内のすべてのファイルに適用することを意味します。 2つのスペースのインデントサイズとUTF-8
文字セットが必要です。 また、末尾の空白を削除して、ファイルに最後の空の行を挿入します。
README.mdを開き、プロジェクト名を第1レベルの要素として追加します。
# Express API template
すぐにバージョン管理を追加しましょう。
# initialize the project folder as a git repository git init
.gitignoreファイルを作成し、次の行を入力します。
node_modules/ yarn-error.log .env .nyc_output coverage build/
これらは、追跡したくないすべてのファイルとフォルダーです。 私たちのプロジェクトにはまだそれらがありませんが、進むにつれてそれらが表示されます。
この時点で、次のフォルダ構造になっているはずです。
EXPRESS-API-TEMPLATE ├── .editorconfig ├── .gitignore ├── package.json └── README.md
これは、変更をコミットしてGitHubにプッシュするための良いポイントだと思います。
新しいエクスプレスプロジェクトの開始
Expressは、Webアプリケーションを構築するためのNode.jsフレームワークです。 公式サイトによると、
Node.js用の高速で非ピニオンのミニマリストWebフレームワーク。
Node.js用の優れたWebアプリケーションフレームワークは他にもありますが、Expressは非常に人気があり、この記事の執筆時点では47,000を超えるGitHubスターがあります。
この記事では、Expressを構成するすべての部分について多くの議論をすることはありません。 その議論については、ジェイミーのシリーズをチェックすることをお勧めします。 最初の部分はここにあり、2番目の部分はここにあります。
Expressをインストールし、新しいExpressプロジェクトを開始します。 Expressサーバーを最初から手動でセットアップすることは可能ですが、私たちの生活を楽にするために、Expressジェネレーターを使用してアプリのスケルトンをセットアップします。
# install the express generator globally yarn global add express-generator # install express yarn add express # generate the express project in the current folder express -f
-f
フラグは、Expressに現在のディレクトリにプロジェクトを作成するように強制します。
次に、いくつかのハウスクリーニング操作を実行します。
- ファイルindex / users.jsを削除します。
-
public/
およびviews/
フォルダーを削除します。 - ファイルの名前をbin / wwwからbin / www.jsに変更します。
- コマンド
yarn remove jade
を使用してjade
をアンインストールします。 -
src/
という名前の新しいフォルダーを作成し、その中に次のものを移動します。1.app.jsファイル2.binbin/
フォルダー3.routesroutes/
フォルダー内。 - package.jsonを開き、
start
スクリプトを次のように更新します。
"start": "node ./src/bin/www"
この時点で、プロジェクトのフォルダ構造は次のようになります。 VSCodeが発生したファイルの変更をどのように強調表示するかを確認できます。
EXPRESS-API-TEMPLATE ├── node_modules ├── src | ├── bin │ │ ├── www.js │ ├── routes │ | ├── index.js │ └── app.js ├── .editorconfig ├── .gitignore ├── package.json ├── README.md └── yarn.lock
src / app.jsを開き、コンテンツを以下のコードに置き換えます。
var logger = require('morgan'); var express = require('express'); var cookieParser = require('cookie-parser'); var indexRouter = require('./routes/index'); var app = express(); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); app.use('/v1', indexRouter); module.exports = app;
いくつかのライブラリが必要になった後、 indexRouter
を使用して/v1
に送信されるすべてのリクエストを処理するようにExpressに指示します。
ルート/index.jsのコンテンツを次のコードに置き換えます。
var express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { return res.status(200).json({ message: 'Welcome to Express API template' }); }); module.exports = router;
Expressを取得し、そこからルーターを作成して/
routeを提供します。これにより、ステータスコード200
とJSONメッセージが返されます。
以下のコマンドでアプリを起動します。
# start the app yarn start
すべてを正しく設定した場合、ターミナルには$ node ./src/bin/www
のみが表示されます。
ブラウザでhttps://localhost:3000/v1
にアクセスします。 次のメッセージが表示されます。
{ "message": "Welcome to Express API template" }
これは、変更をコミットするための良いポイントです。
- 私のリポジトリの対応するブランチは01-install-expressです。
コードをES6
に変換する
express-generator
によって生成されるコードはES5
にありますが、この記事では、すべてのコードをES6
構文で記述します。 それでは、既存のコードをES6
に変換しましょう。
ルート/index.jsのコンテンツを次のコードに置き換えます。
import express from 'express'; const indexRouter = express.Router(); indexRouter.get('/', (req, res) => res.status(200).json({ message: 'Welcome to Express API template' }) ); export default indexRouter;
上記と同じコードですが、importステートメントと/
routeハンドラーのarrow関数があります。
src /app.jsのコンテンツを次のコードに置き換えます。
import logger from 'morgan'; import express from 'express'; import cookieParser from 'cookie-parser'; import indexRouter from './routes/index'; const app = express(); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); app.use('/v1', indexRouter); export default app;
src / bin /www.jsの内容を見てみましょう。 少しずつ構築していきます。 src/bin/www.js
の内容を削除し、以下のコードブロックに貼り付けます。
#!/usr/bin/env node /** * Module dependencies. */ import debug from 'debug'; import http from 'http'; import app from '../app'; /** * Normalize a port into a number, string, or false. */ const normalizePort = val => { const port = parseInt(val, 10); if (Number.isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; }; /** * Get port from environment and store in Express. */ const port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * Create HTTP server. */ const server = http.createServer(app); // next code block goes here
このコードは、カスタムポートが環境変数で指定されているかどうかを確認します。 何も設定されていない場合、normalizePortによって文字列または数値にnormalizePort
化された後、デフォルトのポート値3000
がアプリインスタンスに設定されます。 次に、サーバーはhttp
モジュールから作成され、コールバック関数としてapp
が使用されます。
このファイルを実行するときにノードを指定するため、 #!/usr/bin/env node
行はオプションです。 ただし、 src / bin / www.jsファイルの1行目にあることを確認するか、完全に削除してください。
エラー処理関数を見てみましょう。 このコードブロックをコピーして、サーバーが作成された行の後に貼り付けます。
/** * Event listener for HTTP server "error" event. */ const onError = error => { if (error.syscall !== 'listen') { throw error; } const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': alert(`${bind} requires elevated privileges`); process.exit(1); break; case 'EADDRINUSE': alert(`${bind} is already in use`); process.exit(1); break; default: throw error; } }; /** * Event listener for HTTP server "listening" event. */ const onListening = () => { const addr = server.address(); const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`; debug(`Listening on ${bind}`); }; /** * Listen on provided port, on all network interfaces. */ server.listen(port); server.on('error', onError); server.on('listening', onListening);
onError
関数は、httpサーバーのエラーをリッスンし、適切なエラーメッセージを表示します。 onListening
関数は、サーバーがリッスンしているポートをコンソールに出力するだけです。 最後に、サーバーは指定されたアドレスとポートで着信要求をリッスンします。
この時点で、既存のコードはすべてES6
構文になっています。 サーバーを停止し( Ctrl + Cを使用)、 yarn start
を実行します。 エラーSyntaxError: Invalid or unexpected token
が表示されます。 これは、Node(執筆時点)がコードで使用した構文の一部をサポートしていないために発生します。
次のセクションで修正します。
開発の依存関係の構成: babel
、 nodemon
、 eslint
、およびprettier
プロジェクトのこのフェーズで必要になるスクリプトのほとんどをセットアップするときが来ました。
以下のコマンドを使用して、必要なライブラリをインストールします。 すべてをコピーして、ターミナルに貼り付けるだけです。 コメント行はスキップされます。
# install babel scripts yarn add @babel/cli @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/register @babel/runtime @babel/node --dev
これにより、リストされているすべてのbabelスクリプトが開発の依存関係としてインストールされます。 package.jsonファイルを確認すると、 devDependencies
セクションが表示されます。 インストールされているすべてのスクリプトがそこに一覧表示されます。
使用しているbabelスクリプトについて以下に説明します。
@babel/cli | babel を使用するために必要なインストール。 ターミナルからBabelを使用でき、。 ./node_modules/.bin/babel として利用できます。 |
@babel/core | コアバベル機能。 これは必須のインストールです。 |
@babel/node | これはNode.jsCLIとまったく同じように機能しますが、 babel プリセットとプラグインを使用してコンパイルできるという利点もあります。 これは、 nodemon で使用するために必要です。 |
@babel/plugin-transform-runtime | これは、コンパイルされた出力の重複を回避するのに役立ちます。 |
@babel/preset-env | コード変換の実行を担当するプラグインのコレクション。 |
@babel/register | これはその場でファイルをコンパイルし、テスト中の要件として指定されます。 |
@babel/runtime | これは、 @babel/plugin-transform-runtime と連携して機能します。 |
プロジェクトのルートに.babelrcという名前のファイルを作成し、次のコードを追加します。
{ "presets": ["@babel/preset-env"], "plugins": ["@babel/transform-runtime"] }
nodemon
をインストールしましょう
# install nodemon yarn add nodemon --dev
nodemon
は、プロジェクトのソースコードを監視し、変更を監視するたびにサーバーを自動的に再起動するライブラリです。
プロジェクトのルートにnodemon.jsonという名前のファイルを作成し、以下のコードを追加します。
{ "watch": [ "package.json", "nodemon.json", ".eslintrc.json", ".babelrc", ".prettierrc", "src/" ], "verbose": true, "ignore": ["*.test.js", "*.spec.js"] }
watch
キーは、変更を監視するファイルとフォルダをnodemon
に指示します。 したがって、これらのファイルのいずれかが変更されるたびに、nodemonはサーバーを再起動します。 ignore
キーは、ファイルが変更を監視しないように指示します。
ここで、 package.jsonファイルのscripts
セクションを次のように更新します。
# build the content of the src folder "prestart": "babel ./src --out-dir build" # start server from the build folder "start": "node ./build/bin/www" # start server in development mode "startdev": "nodemon --exec babel-node ./src/bin/www"
-
prestart
スクリプトは、src/
フォルダーのコンテンツをビルドし、build/
フォルダーに配置します。yarn start
コマンドを発行すると、このスクリプトはstart
スクリプトの前に最初に実行されます。 -
start
スクリプトは、以前提供していたsrc/
フォルダーではなく、build/
フォルダーのコンテンツを提供するようになりました。 これは、本番環境でファイルを提供するときに使用するスクリプトです。 実際、Herokuのようなサービスは、デプロイ時にこのスクリプトを自動的に実行します。 -
yarn startdev
は、開発中にサーバーを起動するために使用されます。 今後は、このスクリプトを使用してアプリを開発します。 現在、通常のnode
の代わりにbabel-node
を使用してアプリを実行していることに注意してください。--exec
フラグは、babel-node
にsrc/
フォルダーを提供するように強制します。start
スクリプトには、build/
フォルダー内のファイルがES5にコンパイルされているため、node
を使用します。
yarn startdev
を実行し、https:// localhost:3000 / v1にアクセスします。 サーバーが再び稼働しているはずです。
このセクションの最後のステップは、 ESLint
とprettier
を構成することです。 ESLintは構文規則の適用に役立ち、prettierはコードを読みやすくするために適切にフォーマットするのに役立ちます。
以下のコマンドで両方を追加します。 サーバーが実行されている端末を監視しながら、これを別の端末で実行する必要があります。 サーバーが再起動するのが見えるはずです。 これは、 package.jsonファイルの変更を監視しているためです。
# install elsint and prettier yarn add eslint eslint-config-airbnb-base eslint-plugin-import prettier --dev
次に、プロジェクトroot
に.eslintrc.jsonファイルを作成し、次のコードを追加します。
{ "env": { "browser": true, "es6": true, "node": true, "mocha": true }, "extends": ["airbnb-base"], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" }, "rules": { "indent": ["warn", 2], "linebreak-style": ["error", "unix"], "quotes": ["error", "single"], "semi": ["error", "always"], "no-console": 1, "comma-dangle": [0], "arrow-parens": [0], "object-curly-spacing": ["warn", "always"], "array-bracket-spacing": ["warn", "always"], "import/prefer-default-export": [0] } }
このファイルは主に、 eslint
がコードをチェックするためのいくつかのルールを定義しています。 Airbnbで使用されているスタイルルールを拡張していることがわかります。
"rules"
セクションでは、特定の違反が発生したときにeslint
が警告またはエラーを表示するかどうかを定義します。 たとえば、2つのスペースを使用しないインデントについては、端末に警告メッセージが表示されます。 [0]
の値はルールをオフにします。これは、そのルールに違反しても警告やエラーが発生しないことを意味します。
.prettierrcという名前のファイルを作成し、以下のコードを追加します。
{ "trailingComma": "es5", "tabWidth": 2, "semi": true, "singleQuote": true }
タブ幅を2
に設定し、アプリケーション全体で一重引用符の使用を強制します。 その他のスタイリングオプションについては、よりきれいなガイドを確認してください。
次に、次のスクリプトをpackage.jsonに追加します。
# add these one after the other "lint": "./node_modules/.bin/eslint ./src" "pretty": "prettier --write '**/*.{js,json}' '!node_modules/**'" "postpretty": "yarn lint --fix"
yarn lint
を実行します。 コンソールにいくつかのエラーと警告が表示されます。
pretty
なコマンドは私たちのコードをきれいにします。 postpretty
コマンドは直後に実行されます。 --fix
フラグを追加してlint
コマンドを実行します。 このフラグは、 ESLint
に一般的なリンティングの問題を自動的に修正するように指示します。 このように、私はほとんど、 lint
コマンドを気にせずにyarn pretty
コマンドを実行します。
yarn pretty
を走らせます。 bin /www.jsファイルにalert
が存在することに関する警告が2つしかないことがわかります。
この時点でのプロジェクト構造は次のようになります。
EXPRESS-API-TEMPLATE ├── build ├── node_modules ├── src | ├── bin │ │ ├── www.js │ ├── routes │ | ├── index.js │ └── app.js ├── .babelrc ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── nodemon.json ├── package.json ├── README.md └── yarn.lock
プロジェクトルートに追加のファイルyarn-error.log
がある場合があります。 .gitignore
ファイルに追加します。 変更をコミットします。
- 私のリポジトリのこの時点での対応するブランチは02-dev-dependenciesです。
.envファイルの設定と環境変数
ほぼすべてのプロジェクトで、アプリ全体で使用される設定(AWSシークレットキーなど)を保存する場所が必要になります。 環境変数などの設定を保存します。 これにより、それらを詮索好きな目から遠ざけ、必要に応じてアプリケーション内で使用できます。
すべての環境変数を読み取るsettings.jsファイルがあるのが好きです。 そうすれば、アプリ内のどこからでも設定ファイルを参照できます。 このファイルには自由に名前を付けることができますが、そのようなファイルにsettings.jsまたはconfig.jsという名前を付けることについては何らかのコンセンサスがあります。
環境変数については、それらを.env
ファイルに保持し、そこからsettings
ファイルに読み込みます。
プロジェクトのルートに.envファイルを作成し、次の行を入力します。
TEST_ENV_VARIABLE="Environment variable is coming across"
プロジェクトに環境変数を読み込むことができるようにするために、 .env
ファイルを読み取り、内部で定義された環境変数へのアクセスを提供する優れたライブラリdotenv
があります。 インストールしましょう。
# install dotenv yarn add dotenv
nodemon
によって監視されているファイルのリストに.envファイルを追加します。
次に、 src/
フォルダー内にsettings.jsファイルを作成し、次のコードを追加します。
import dotenv from 'dotenv'; dotenv.config(); export const testEnvironmentVariable = process.env.TEST_ENV_VARIABLE;
dotenv
パッケージをインポートし、そのconfigメソッドを呼び出します。 次に、 .env
ファイルに設定したtestEnvironmentVariable
をエクスポートします。
src / routers / index.jsを開き、コードを次のコードに置き換えます。
import express from 'express'; import { testEnvironmentVariable } from '../settings'; const indexRouter = express.Router(); indexRouter.get('/', (req, res) => res.status(200).json({ message: testEnvironmentVariable })); export default indexRouter;
ここで行った唯一の変更は、 settings
ファイルからtestEnvironmentVariable
をインポートし、 /
ルートからのリクエストのリターンメッセージとして使用することです。
https:// localhost:3000 / v1にアクセスすると、次のようなメッセージが表示されます。
{ "message": "Environment variable is coming across." }
以上です。 これからは、必要な数の環境変数を追加して、 settings.jsファイルからエクスポートできます。
これは、コードをコミットするための良いポイントです。 コードをきれいにし、リントすることを忘れないでください。
- 私のリポジトリの対応するブランチは03-env-variablesです。
最初のテストを書く
テストをアプリに組み込むときが来ました。 開発者にコードへの信頼を与えるものの1つは、テストです。 テスト駆動開発(TDD)を説教するWeb上の記事を数え切れないほど見たことがあると思います。 コードにある程度のテストが必要なことを強調することはできません。 Express.jsを使用している場合、TDDは非常に簡単に追跡できます。
テストでは、APIエンドポイントを呼び出し、返されるものが期待どおりであるかどうかを確認します。
必要な依存関係をインストールします。
# install dependencies yarn add mocha chai nyc sinon-chai supertest coveralls --dev
これらの各ライブラリには、テストで果たす独自の役割があります。
mocha | テストランナー |
chai | アサーションを作成するために使用されます |
nyc | テストカバレッジレポートを収集する |
sinon-chai | チャイの主張を拡張する |
supertest | APIエンドポイントへのHTTP呼び出しを行うために使用されます |
coveralls | テストカバレッジをcoveralls.ioにアップロードするため |
プロジェクトのルートに新しいtest/
フォルダーを作成します。 このフォルダ内に2つのファイルを作成します。
- test / setup.js
- test / index.test.js
Mochaはtest/
フォルダーを自動的に見つけます。
test / setup.jsを開き、以下のコードを貼り付けます。 これは、テストファイルで必要なすべてのインポートを整理するのに役立つ単なるヘルパーファイルです。
import supertest from 'supertest'; import chai from 'chai'; import sinonChai from 'sinon-chai'; import app from '../src/app'; chai.use(sinonChai); export const { expect } = chai; export const server = supertest.agent(app); export const BASE_URL = '/v1';
これは設定ファイルに似ていますが、テスト用です。 このようにして、各テストファイル内のすべてを初期化する必要はありません。 したがって、必要なパッケージをインポートし、初期化したものをエクスポートします。これを必要なファイルにインポートできます。
index.test.jsを開き、次のテストコードを貼り付けます。
import { expect, server, BASE_URL } from './setup'; describe('Index page test', () => { it('gets base url', done => { server .get(`${BASE_URL}/`) .expect(200) .end((err, res) => { expect(res.status).to.equal(200); expect(res.body.message).to.equal( 'Environment variable is coming across.' ); done(); }); }); });
ここでは、ベースエンドポイント( /
)を取得するように要求し、 res.
body
オブジェクトには、 Environment variable is coming across.
の値が検出されたmessage
キーがあります。
describe
、 it
パターンに慣れていない場合は、Mochaの「GettingStarted」ドキュメントをざっと見てみることをお勧めします。
package.jsonのscripts
セクションにtestコマンドを追加します。
"test": "nyc --reporter=html --reporter=text --reporter=lcov mocha -r @babel/register"
このスクリプトは、 nyc
を使用してテストを実行し、3種類のcoverage/
レポートを生成します。 端末に出力されるテキストレポートと.nyc_output/
フォルダに出力されるlcovレポート。
次に、 yarn test
を実行します。 下の写真のように、端末にテキストレポートが表示されます。
2つの追加フォルダーが生成されることに注意してください。
-
.nyc_output/
-
coverage/
.gitignore
の内部を見ると、すでに両方を無視していることがわかります。 ブラウザでcoverage/index.html
を開き、各ファイルのテストレポートを表示することをお勧めします。
これは、変更をコミットするための良いポイントです。
- 私のリポジトリの対応するブランチは04-first-testです。
継続的インテグレーション(CD)とバッジ:Travis、Coveralls、Code Climate、AppVeyor
次に、継続的インテグレーションおよびデプロイメント(CI / CD)ツールを構成します。 travis travis-ci
、 coveralls
、 AppVeyor
、 codeclimate
などの一般的なサービスを構成し、READMEファイルにバッジを追加します。
始めましょう。
Travis CI
Travis CIは、GitHub(および最近ではBitbucket)にコミットをプッシュするたび、およびプルリクエストを作成するたびにテストを自動的に実行するツールです。 これは、新しいコードがテストのいずれかを破ったかどうかを示すことでプルリクエストを行うときに最も役立ちます。
- travis-ci.comまたはtravis-ci.orgにアクセスし、アカウントをお持ちでない場合は作成してください。 GitHubアカウントでサインアップする必要があります。
- プロフィール写真の横にあるドロップダウン矢印にカーソルを合わせ、[設定]をクリックし
settings
。 - [
Repositories
]タブで、[GithubのManage repositories on Github
にリダイレクトします。 - GitHubページで、[
Repository access
]まで下にスクロールし、Only select repositories
]の横にあるチェックボックスをクリックします。 - [
Select repositories
]ドロップダウンをクリックして、express-api-template
リポジトリを見つけます。 クリックして、travis-ci
に追加するリポジトリのリストに追加します。 - [
Approve and install
をクリックし、travis-ci
にリダイレクトされるのを待ちます。 - リポジトリページの上部で、リポジトリ名の近くにある[
build unknown
]アイコンをクリックします。 ステータスイメージモーダルから、フォーマットドロップダウンからマークダウンを選択します。 - 結果のコードをコピーして、 README.mdファイルに貼り付けます。
- プロジェクトページで、[
More options
]>Settings
]をクリックします。 [Environment Variables
]セクションで、TEST_ENV_VARIABLE
環境変数を追加します。 その値を入力するときは、必ずこの"Environment variable is coming across."
のように二重引用符で囲んでください。 - プロジェクトのルートに.travis.ymlファイルを作成し、以下のコードを貼り付けます(コード気候セクションで
CC_TEST_REPORTER_ID
の値を設定します)。
language: node_js env: global: - CC_TEST_REPORTER_ID=get-this-from-code-climate-repo-page matrix: include: - node_js: '12' cache: directories: [node_modules] install: yarn after_success: yarn coverage before_script: - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - chmod +x ./cc-test-reporter - ./cc-test-reporter before-build script: - yarn test after_script: - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESUL
まず、TravisにNode.jsを使用してテストを実行するように指示し、次にCC_TEST_REPORTER_ID
グローバル環境変数を設定します(これについては、コード気候セクションで説明します)。 matrix
セクションでは、Node.jsv12でテストを実行するようにTravisに指示します。 また、 node_modules/
ディレクトリをキャッシュして、毎回再生成する必要がないようにします。
依存関係は、yarninstallの省略形であるyarn
コマンドを使用してyarn install
します。 before_script
コマンドとafter_script
コマンドは、カバレッジ結果をcodeclimate
にアップロードするために使用されます。 codeclimate
をまもなく構成します。 yarn test
が正常に実行されたら、カバレッジレポートをcoveralls.ioにアップロードするyarn coverage
も実行します。
つなぎ服
Coverallsは、簡単に視覚化できるようにテストカバレッジデータをアップロードします。 カバレッジフォルダからローカルマシンのテストカバレッジを表示できますが、Coverallsはローカルマシンの外部でテストカバレッジを利用できるようにします。
- coveralls.ioにアクセスし、Githubアカウントでサインインまたはサインアップします。
- 画面の左側にカーソルを合わせると、ナビゲーションメニューが表示されます。 [
ADD REPOS
の追加]をクリックします。 -
express-api-template
リポジトリを検索し、左側のトグルボタンを使用してカバレッジをオンにします。 見つからない場合は、右上隅にあるSYNC REPOS
をクリックして再試行してください。 PROアカウントを持っていない限り、リポジトリは公開されている必要があることに注意してください。 - 詳細をクリックして、リポジトリの詳細ページに移動します。
- プロジェクトのルートに.coveralls.ymlファイルを作成し、以下のコードを入力します。
repo_token
を取得するには、リポジトリの詳細をクリックします。 そのページで簡単に見つけることができます。repo_token
をブラウザで検索するだけです。
repo_token: get-this-from-repo-settings-on-coveralls.io
このトークンは、カバレッジデータをCoverallsのリポジトリにマッピングします。 次に、 package.jsonファイルのscripts
セクションにcoverage
コマンドを追加します。
"coverage": "nyc report --reporter=text-lcov | coveralls"
このコマンドは、 .nyc_output
フォルダーのカバレッジレポートをcoveralls.ioにアップロードします。 インターネット接続をオンにして、以下を実行します。
yarn coverage
これにより、既存のカバレッジレポートがカバーオールにアップロードされます。 カバーオールのリポジトリページを更新して、完全なレポートを表示します。
詳細ページで、下にスクロールして[ BADGE YOUR REPO
セクションを見つけます。 EMBED
ドロップダウンをクリックし、マークダウンコードをコピーして、 READMEファイルに貼り付けます。
コード気候
Code Climateは、コードの品質を測定するのに役立つツールです。 いくつかの定義されたパターンに対してコードをチェックすることにより、メンテナンスメトリックを示します。 不要な繰り返しや深くネストされたforループなどを検出します。 また、coveralls.ioと同様に、テストカバレッジデータも収集します。
- codeclimate.comにアクセスし、[GitHubにサインアップ]をクリックします。 すでにアカウントをお持ちの場合はログインしてください。
- ダッシュボードが表示されたら、[
Add a repository
]をクリックします。 - リストから
express-api-template
リポジトリを見つけて、[リポジトリのAdd Repo
]をクリックします。 - ビルドが完了するのを待ち、リポジトリダッシュボードにリダイレクトします。
- [コードベースの
Codebase Summary
]で、[Test Coverage
]をクリックします。 [Test coverage
]メニューで、[テストレポートTEST REPORTER ID
]をコピーし、CC_TEST_REPORTER_ID
の値として.travis.ymlに貼り付けます。 - 同じページの左側のナビゲーションの[
EXTRAS
]で、[バッジ]をクリックします。maintainability
とtest coverage
のバッジをマークダウン形式でコピーし、 README.mdファイルに貼り付けます。
保守性チェックを構成する方法は2つあることに注意することが重要です。 すべてのリポジトリに適用されるデフォルト設定がありますが、必要に応じて、プロジェクトのルートに.codeclimate.ymlファイルを提供できます。 リポジトリ設定ページの[ Maintainability
性]タブにあるデフォルト設定を使用します。 少なくともご覧になることをお勧めします。 それでも独自の設定を構成したい場合は、このガイドで必要なすべての情報を入手できます。
AppVeyor
AppVeyorとTravisCIは、どちらも自動テストランナーです。 主な違いは、travis-ciがLinux環境でテストを実行するのに対し、AppVeyorはWindows環境でテストを実行することです。 このセクションは、AppVeyorの使用を開始する方法を示すために含まれています。
- AppVeyorにアクセスして、ログインまたはサインアップします。
- 次のページで、[
NEW PROJECT
]をクリックします。 - リポジトリリストから、
express-api-template
リポジトリを見つけます。 その上にカーソルを合わせて、[ADD
]をクリックします。 - [
Settings
]タブをクリックします。 左側のナビゲーションで[Environment
をクリックします。TEST_ENV_VARIABLE
とその値を追加します。 ページの下部にある[保存]をクリックします。 - プロジェクトのルートにappveyor.ymlファイルを作成し、以下のコードを貼り付けます。
environment: matrix: - nodejs_version: "12" install: - yarn test_script: - yarn test build: off
このコードは、Node.jsv12を使用してテストを実行するようにAppVeyorに指示します。 次に、 yarn
コマンドを使用してプロジェクトの依存関係をインストールします。 test_script
は、テストを実行するコマンドを指定します。 最後の行は、ビルドフォルダーを作成しないようにAppVeyorに指示しています。
[ Settings
]タブをクリックします。 左側のナビゲーションで、バッジをクリックします。 マークダウンコードをコピーして、 README.mdファイルに貼り付けます。
コードをコミットしてGitHubにプッシュします。 指示どおりにすべてを実行した場合、すべてのテストに合格し、以下に示すように光沢のある新しいバッジが表示されます。 TravisとAppVeyorで環境変数を設定したことをもう一度確認してください。
今が私たちの変更をコミットする良い機会です。
- 私のリポジトリの対応するブランチは05-ciです。
コントローラの追加
現在、 src / routers /index.js内のルートURL /v1
へのGET
リクエストを処理しています。 これは期待どおりに機能し、問題はありません。 ただし、アプリケーションが大きくなるにつれて、物事を整頓したいと思うでしょう。 懸念事項を分離する必要があります。要求を処理するコードと、クライアントに返送される応答を生成するコードを明確に分離する必要があります。 これを実現するために、 controllers
を作成します。 コントローラは、特定のURLを介して送信されるリクエストを処理する単なる関数です。
開始するには、 src/
フォルダー内にcontrollers/
フォルダーを作成します。 controllers
内で、 index.jsとhome.jsの2つのファイルを作成します。 index.js内から関数をエクスポートします。 home.jsには任意の名前を付けることができますが、通常は、コントローラーが制御するものにちなんでコントローラーに名前を付けます。 たとえば、アプリ内のユーザーに関連するすべての関数を保持するファイルusersController.jsがあるとします。
src / controllers / home.jsを開き、以下のコードを入力します。
import { testEnvironmentVariable } from '../settings'; export const indexPage = (req, res) => res.status(200).json({ message: testEnvironmentVariable });
/
ルートのリクエストを処理する関数のみを移動したことに気付くでしょう。
src / controllers / index.jsを開き、以下のコードを入力します。
// export everything from home.js export * from './home';
home.jsファイルからすべてをエクスポートします。 これにより、インポートステートメントを短縮してimport { indexPage } from '../controllers';
src / routings / index.jsを開き、そこにあるコードを次のコードに置き換えます。
import express from 'express'; import { indexPage } from '../controllers'; const indexRouter = express.Router(); indexRouter.get('/', indexPage); export default indexRouter;
ここでの唯一の変更は、 /
ルートへのリクエストを処理する関数を提供したことです。
最初のコントローラーを正常に作成しました。 ここからは、必要に応じてファイルや関数を追加する必要があります。
さらにいくつかのルートとコントローラーを追加して、アプリを試してみてください。 アバウトページのルートとコントローラーを追加できます。 ただし、テストを更新することを忘れないでください。
yarn test
を実行して、何も壊れていないことを確認します。 あなたのテストは合格しましたか? カッコいい。
これは、変更をコミットするための良いポイントです。
- 私のリポジトリの対応するブランチは06-controllersです。
PostgreSQL
データベースを接続してモデルを作成する
現在、コントローラーはハードコードされたテキストメッセージを返します。 実際のアプリでは、データベースから情報を保存および取得する必要があることがよくあります。 このセクションでは、アプリをPostgreSQLデータベースに接続します。
データベースを使用して、単純なテキストメッセージの保存と取得を実装します。 データベースを設定するには、2つのオプションがあります。クラウドサーバーから1つをプロビジョニングするか、ローカルで独自に設定することができます。
クラウドサーバーからデータベースをプロビジョニングすることをお勧めします。 ElephantSQLには、このチュートリアルに十分な20MBの無料ストレージを提供する無料プランがあります。 サイトにアクセスして、[ Get a managed database today
]をクリックします。 アカウントを作成し(アカウントをお持ちでない場合)、指示に従って無料プランを作成します。 Take note of the URL on the database details page. We'll be needing it soon.
If you would rather set up a database locally, you should visit the PostgreSQL and PgAdmin sites for further instructions.
Once we have a database set up, we need to find a way to allow our Express app to communicate with our database. Node.js by default doesn't support reading and writing to PostgreSQL
database, so we'll be using an excellent library, appropriately named, node-postgres.
node-postgres
executes SQL
queries in node and returns the result as an object, from which we can grab items from the rows key.
Let's connect node-postgres
to our application.
# install node-postgres yarn add pg
Open settings.js and add the line below:
export const connectionString = process.env.CONNECTION_STRING;
Open your .env
file and add the CONNECTION_STRING
variable. This is the connection string we'll be using to establish a connection to our database. The general form of the connection string is shown below.
CONNECTION_STRING="postgresql://dbuser:dbpassword@localhost:5432/dbname"
If you're using elephantSQL you should copy the URL from the database details page.
Inside your /src
folder, create a new folder called models/
. Inside this folder, create two files:
- pool.js
- model.js
Open pools.js and paste the following code:
import { Pool } from 'pg'; import dotenv from 'dotenv'; import { connectionString } from '../settings'; dotenv.config(); export const pool = new Pool({ connectionString });
First, we import the Pool
and dotenv
from the pg
and dotenv
packages, and then import the settings we created for our postgres database before initializing dotenv
. We establish a connection to our database with the Pool
object. In node-postgres
, every query is executed by a client. A Pool is a collection of clients for communicating with the database.
To create the connection, the pool constructor takes a config object. You can read more about all the possible configurations here. It also accepts a single connection string, which I will use here.
Open model.js and paste the following code:
import { pool } from './pool'; class Model { constructor(table) { this.pool = pool; this.table = table; this.pool.on('error', (err, client) => `Error, ${err}, on idle client${client}`); } async select(columns, clause) { let query = `SELECT ${columns} FROM ${this.table}`; if (clause) query += clause; return this.pool.query(query); } } export default Model;
We create a model class whose constructor accepts the database table we wish to operate on. We'll be using a single pool for all our models.
We then create a select
method which we will use to retrieve items from our database. This method accepts the columns we want to retrieve and a clause, such as a WHERE
clause. It returns the result of the query, which is a Promise
. Remember we said earlier that every query is executed by a client, but here we execute the query with pool. This is because, when we use pool.query
, node-postgres
executes the query using the first available idle client.
The query you write is entirely up to you, provided it is a valid SQL
statement that can be executed by a Postgres engine.
The next step is to actually create an API endpoint to utilize our newly connected database. Before we do that, I'd like us to create some utility functions. The goal is for us to have a way to perform common database operations from the command line.
Create a folder, utils/
inside the src/
folder. Create three files inside this folder:
- queries.js
- queryFunctions.js
- runQuery.js
We're going to create functions to create a table in our database, insert seed data in the table, and to delete the table.
querys.jsを開き、次のコードを貼り付けます。
export const createMessageTable = ` DROP TABLE IF EXISTS messages; CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, name VARCHAR DEFAULT '', message VARCHAR NOT NULL ) `; export const insertMessages = ` INSERT INTO messages(name, message) VALUES ('chidimo', 'first message'), ('orji', 'second message') `; export const dropMessagesTable = 'DROP TABLE messages';
このファイルでは、3つのSQLクエリ文字列を定義します。 最初のクエリは、 messages
テーブルを削除して再作成します。 2番目のクエリは、 messages
テーブルに2つの行を挿入します。 ここにアイテムを追加してください。 最後のクエリは、 messages
テーブルを削除/削除します。
queryFunctions.jsを開き、次のコードを貼り付けます。
import { pool } from '../models/pool'; import { insertMessages, dropMessagesTable, createMessageTable, } from './queries'; export const executeQueryArray = async arr => new Promise(resolve => { const stop = arr.length; arr.forEach(async (q, index) => { await pool.query(q); if (index + 1 === stop) resolve(); }); }); export const dropTables = () => executeQueryArray([ dropMessagesTable ]); export const createTables = () => executeQueryArray([ createMessageTable ]); export const insertIntoTables = () => executeQueryArray([ insertMessages ]);
ここでは、前に定義したクエリを実行する関数を作成します。 executeQueryArray
関数はクエリの配列を実行し、ループ内で各クエリが完了するのを待つことに注意してください。 (ただし、本番コードではそのようなことをしないでください)。 次に、リストの最後のクエリを実行した後でのみ、Promiseを解決します。 配列を使用する理由は、データベース内のテーブルの数が増えるにつれて、そのようなクエリの数が増えるためです。
runQuery.jsを開き、次のコードを貼り付けます。
import { createTables, insertIntoTables } from './queryFunctions'; (async () => { await createTables(); await insertIntoTables(); })();
ここで、関数を実行してテーブルを作成し、テーブルにメッセージを挿入します。 package.jsonのscripts
セクションにコマンドを追加して、このファイルを実行してみましょう。
"runQuery": "babel-node ./src/utils/runQuery"
今実行します:
yarn runQuery
データベースを調べると、 messages
テーブルが作成され、メッセージがテーブルに挿入されていることがわかります。
ElephantSQLを使用している場合は、データベースの詳細ページで、左側のナビゲーションメニューから[ BROWSER
]をクリックします。 messages
テーブルを選択し、[ Execute
]をクリックします。 querys.jsファイルからのメッセージが表示されます。
データベースからのメッセージを表示するためのコントローラーとルートを作成しましょう。
新しいコントローラーファイルsrc / controllers / messages.jsを作成し、次のコードを貼り付けます。
import Model from '../models/model'; const messagesModel = new Model('messages'); export const messagesPage = async (req, res) => { try { const data = await messagesModel.select('name, message'); res.status(200).json({ messages: data.rows }); } catch (err) { res.status(200).json({ messages: err.stack }); } };
Model
クラスをインポートし、そのモデルの新しいインスタンスを作成します。 これは、データベース内のmessages
テーブルを表します。 次に、モデルのselect
メソッドを使用して、データベースにクエリを実行します。 取得したデータ( name
とmessage
)は、応答でJSONとして送信されます。
messagesPage
コントローラーをasync
関数として定義します。 node-postgres
クエリはpromiseを返すため、そのクエリの結果をawait
ます。 クエリ中にエラーが発生した場合は、それをキャッチしてスタックをユーザーに表示します。 エラーの処理方法を決定する必要があります。
getmessagesエンドポイントをsrc / routers / index.jsに追加し、インポート行を更新します。
# update the import line import { indexPage, messagesPage } from '../controllers'; # add the get messages endpoint indexRouter.get('/messages', messagesPage)
https:// localhost:3000 / v1 / messagesにアクセスすると、次のようなメッセージが表示されます。
それでは、テストファイルを更新しましょう。 TDDを実行するときは、通常、テストに合格するコードを実装する前にテストを記述します。 私たちはまだデータベースのセットアップに取り組んでいるので、ここでは反対のアプローチを取っています。
test/
フォルダーに新しいファイルhooks.jsを作成し、次のコードを入力します。
import { dropTables, createTables, insertIntoTables, } from '../src/utils/queryFunctions'; before(async () => { await createTables(); await insertIntoTables(); }); after(async () => { await dropTables(); });
テストが開始されると、Mochaはこのファイルを見つけて実行してから、テストファイルを実行します。 before
フックを実行してデータベースを作成し、それにいくつかのアイテムを挿入します。 その後、テストファイルが実行されます。 テストが終了すると、Mochaはデータベースを削除するafter
フックを実行します。 これにより、テストを実行するたびに、データベース内のクリーンで新しいレコードを使用してテストを実行できます。
新しいテストファイルtest / messages.test.jsを作成し、次のコードを追加します。
import { expect, server, BASE_URL } from './setup'; describe('Messages', () => { it('get messages page', done => { server .get(`${BASE_URL}/messages`) .expect(200) .end((err, res) => { expect(res.status).to.equal(200); expect(res.body.messages).to.be.instanceOf(Array); res.body.messages.forEach(m => { expect(m).to.have.property('name'); expect(m).to.have.property('message'); }); done(); }); }); });
/messages
の呼び出しの結果は配列であると断言します。 メッセージオブジェクトごとに、 name
プロパティとmessage
プロパティがあることを表明します。
このセクションの最後のステップは、CIファイルを更新することです。
次のセクションを.travis.ymlファイルに追加します。
services: - postgresql addons: postgresql: "10" apt: packages: - postgresql-10 - postgresql-client-10 before_install: - sudo cp /etc/postgresql/{9.6,10}/main/pg_hba.conf - sudo /etc/init.d/postgresql restart
これは、テストを実行する前にPostgreSQL10データベースを起動するようにTravisに指示します。
before_script
セクションの最初のエントリとしてデータベースを作成するコマンドを追加します。
# add this as the first line in the before_script section - psql -c 'create database testdb;' -U postgres
TravisでCONNECTION_STRING
環境変数を作成し、以下の値を使用します。
CONNECTION_STRING="postgresql://postgres:postgres@localhost:5432/testdb"
次のセクションを.appveyor.ymlファイルに追加します。
before_test: - SET PGUSER=postgres - SET PGPASSWORD=Password12! - PATH=C:\Program Files\PostgreSQL\10\bin\;%PATH% - createdb testdb services: - postgresql101
接続文字列環境変数をappveyorに追加します。 次の行を使用します。
CONNECTION_STRING=postgresql://postgres:Password12!@localhost:5432/testdb
次に、変更をコミットしてGitHubにプッシュします。 テストは、TravisCIとAppVeyorの両方に合格する必要があります。
- 私のリポジトリの対応するブランチは07-connect-postgresです。
注:すべてが正常に機能することを願っていますが、何らかの理由で問題が発生した場合は、いつでもリポジトリで私のコードを確認できます!
それでは、データベースにメッセージを追加する方法を見てみましょう。 このステップでは、 POST
リクエストをURLに送信する方法が必要です。 Postmanを使用してPOST
リクエストを送信します。
TDDルートに進み、テストを更新して、達成が期待されることを反映させましょう。
test / message.test.jsを開き、以下のテストケースを追加します。
it('posts messages', done => { const data = { name: 'some name', message: 'new message' }; server .post(`${BASE_URL}/messages`) .send(data) .expect(200) .end((err, res) => { expect(res.status).to.equal(200); expect(res.body.messages).to.be.instanceOf(Array); res.body.messages.forEach(m => { expect(m).to.have.property('id'); expect(m).to.have.property('name', data.name); expect(m).to.have.property('message', data.message); }); done(); }); });
このテストは/v1/messages
エンドポイントにPOSTリクエストを行い、配列が返されることを期待しています。 また、配列のid
、 name
、およびmessage
プロパティを確認します。
テストを実行して、このケースが失敗することを確認します。 それを修正しましょう。
POSTリクエストを送信するには、サーバーのpostメソッドを使用します。 挿入したい名前とメッセージも送信します。 応答は、クエリを構成するプロパティid
とその他の情報を含む配列であることが期待されます。 id
は、レコードがデータベースに挿入されたことを証明します。
src / models / model.jsを開き、 insert
メソッドを追加します。
async insertWithReturn(columns, values) { const query = ` INSERT INTO ${this.table}(${columns}) VALUES (${values}) RETURNING id, ${columns} `; return this.pool.query(query); }
これは、データベースにメッセージを挿入できるようにする方法です。 アイテムを挿入すると、 id
、 name
、 message
が返されます。
src / controllers / messages.jsを開き、以下のコントローラーを追加します。
export const addMessage = async (req, res) => { const { name, message } = req.body; const columns = 'name, message'; const values = `'${name}', '${message}'`; try { const data = await messagesModel.insertWithReturn(columns, values); res.status(200).json({ messages: data.rows }); } catch (err) { res.status(200).json({ messages: err.stack }); } };
名前とメッセージを取得するために、リクエストの本文を分解します。 次に、値を使用してSQLクエリ文字列を作成し、モデルのinsertWithReturn
メソッドを使用して実行します。
以下のPOST
エンドポイントを/src/routes/index.jsに追加し、インポート行を更新します。
import { indexPage, messagesPage, addMessage } from '../controllers'; indexRouter.post('/messages', addMessage);
テストを実行して、合格するかどうかを確認します。
Postmanを開き、 POST
リクエストをmessages
エンドポイントに送信します。 テストを実行したばかりの場合は、 yarn query
を実行してmessages
テーブルを再作成することを忘れないでください。
yarn query
変更をコミットしてGitHubにプッシュします。 テストはTravisとAppVeyorの両方に合格する必要があります。 テストカバレッジは数ポイント低下しますが、それは問題ありません。
- 私のリポジトリの対応するブランチは08-post-to-dbです。
ミドルウェア
Expressの説明は、ミドルウェアについて話さずに完了することはできません。 Expressのドキュメントでは、ミドルウェアを次のように説明しています。
「[...]要求オブジェクト(req
)、応答オブジェクト(res
)、およびアプリケーションの要求/応答サイクルの次のミドルウェア関数にアクセスできる関数。 次のミドルウェア関数は、通常、next
という名前の変数で示されます。」
ミドルウェアは、認証、リクエスト本文の変更など、任意の数の機能を実行できます。 ミドルウェアの使用に関するExpressのドキュメントを参照してください。
リクエストの本文を変更する簡単なミドルウェアを作成します。 私たちのミドルウェアは、データベースに保存される前に、着信メッセージにSAYS:
という単語を追加します。
始める前に、達成したいことを反映するようにテストを変更しましょう。
test / messages.test.jsを開き、 posts message
テストケースの最後のexpect行を変更します。
it('posts messages', done => { ... expect(m).to.have.property('message', `SAYS: ${data.message}`); # update this line ... });
SAYS:
文字列がメッセージに追加されていることを表明しています。 テストを実行して、このテストケースが失敗することを確認します。
それでは、テストに合格するためのコードを書いてみましょう。
src/
フォルダー内に新しいmiddleware/
フォルダーを作成します。 このフォルダ内に2つのファイルを作成します。
- ミドルウェア.js
- index.js
次のコードをmiddleware.jsに入力します。
export const modifyMessage = (req, res, next) => { req.body.message = `SAYS: ${req.body.message}`; next(); };
ここでは、リクエスト本文のメッセージに文字列SAYS:
を追加します。 その後、 next()
関数を呼び出して、要求と応答のチェーン内の次の関数に実行を渡す必要があります。 すべてのミドルウェアは、要求/応答サイクルで次のミドルウェアに実行を渡すためにnext
関数を呼び出す必要があります。
index.jsに次のコードを入力します。
# export everything from the middleware file export * from './middleware';
これにより、 / middleware.jsファイルにあるミドルウェアがエクスポートされます。 今のところ、 modifyMessage
ミドルウェアしかありません。
src / routers / index.jsを開き、ミドルウェアをメッセージ後の要求/応答チェーンに追加します。
import { modifyMessage } from '../middleware'; indexRouter.post('/messages', modifyMessage, addMessage);
modifyMessage
addMessage
の前にあることがわかります。 modifyMessage
ミドルウェアでnext
を呼び出すことにより、 addMessage
関数を呼び出します。 実験として、 modifyMessage
の中央にあるnext()
行をコメントアウトし、リクエストがハングするのを確認します。
Postmanを開き、新しいメッセージを作成します。 追加された文字列が表示されます。
これは、変更をコミットするための良いポイントです。
- 私のリポジトリの対応するブランチは09-ミドルウェアです。
エラー処理と非同期ミドルウェア
どのアプリケーションでもエラーは避けられません。 開発者の前のタスクは、エラーを可能な限り適切に処理する方法です。
Expressの場合:
「エラー処理とは、Expressが同期と非同期の両方で発生するエラーをキャッチして処理する方法を指します。
同期関数のみを記述している場合、Expressはすでにそれらを処理する優れた仕事をしているので、エラー処理についてそれほど心配する必要はないかもしれません。 ドキュメントによると:
「ルートハンドラーとミドルウェア内の同期コードで発生するエラーは、追加の作業を必要としません。」
ただし、非同期ルーターハンドラーとミドルウェアの作成を開始したら、エラー処理を行う必要があります。
私たちのmodifyMessage
ミドルウェアは同期機能です。 その関数でエラーが発生した場合、Expressはそれを問題なく処理します。 非同期ミドルウェアのエラーをどのように処理するかを見てみましょう。
たとえば、メッセージを作成する前に、このURLhttps://picsum.photos/id/0/infoを使用してLoremPicsumAPIから写真を取得したいとしhttps://picsum.photos/id/0/info
。 これは、成功または失敗する可能性のある非同期操作であり、対処する必要があります。
Axiosをインストールすることから始めます。
# install axios yarn add axios
src / Middleware / Middleware.jsを開き、以下の関数を追加します。
export const performAsyncAction = async (req, res, next) => { try { await axios.get('https://picsum.photos/id/0/info'); next(); } catch (err) { next(err); } };
このasync
関数では、APIの呼び出しをawait
(実際には返されたデータは必要ありません)、その後、リクエストチェーンのnext
関数を呼び出します。 リクエストが失敗した場合、エラーをキャッチしてnext
に渡します。 Expressはこのエラーを検出すると、チェーン内の他のすべてのミドルウェアをスキップします。 next(err)
を呼び出さなかった場合、リクエストはハングします。 err
なしでnext()
のみを呼び出した場合、要求は何も起こらなかったかのように進行し、エラーはキャッチされません。
この関数をインポートして、メッセージ投稿ルートのミドルウェアチェーンに追加します。
import { modifyMessage, performAsyncAction } from '../middleware'; indexRouter.post('/messages', modifyMessage, performAsyncAction, addMessage);
src / app.jsを開き、 export default app
行の直前に以下のコードを追加します。
app.use((err, req, res, next) => { res.status(400).json({ error: err.stack }); }); export default app;
これがエラーハンドラです。 Expressエラー処理ドキュメントによると:
「[...]エラー処理関数には、3つではなく4つの引数があります: (err, req, res, next)
。」
このエラーハンドラは、 app.use()
を呼び出すたびに最後に来る必要があることに注意してください。 エラーが発生すると、ステータスコード400
のスタックトレースが返されます。 エラーで好きなことをすることができます。 ログに記録するか、どこかに送信することをお勧めします。
これは、変更をコミットするのに適した場所です。
- 私のリポジトリの対応するブランチは10-async-middlewareです。
Herokuにデプロイする
- 開始するには、https://www.heroku.com/にアクセスして、ログインまたは登録します。
- ここからHerokuCLIをダウンロードしてインストールします。
- プロジェクトフォルダでターミナルを開き、コマンドを実行します。
# login to heroku on command line heroku login
これにより、ブラウザウィンドウが開き、Herokuアカウントにログインするように求められます。
ログインして端末にHerokuアカウントへのアクセスを許可し、次のコマンドを実行して新しいHerokuアプリを作成します。
#app name is up to you heroku create app-name
これにより、Herokuでアプリが作成され、2つのURLが返されます。
# app production url and git url https://app-name.herokuapp.com/ | https://git.heroku.com/app-name.git
右側のURLをコピーして、以下のコマンドを実行します。 HerokuがすでにリモートURLを追加している場合があるため、この手順はオプションであることに注意してください。
# add heroku remote url git remote add heroku https://git.heroku.com/my-shiny-new-app.git
サイドターミナルを開き、以下のコマンドを実行します。 これにより、画像に示すように、アプリのログインがリアルタイムで表示されます。
# see process logs heroku logs --tail
次の3つのコマンドを実行して、必要な環境変数を設定します。
heroku config:set TEST_ENV_VARIABLE="Environment variable is coming across." heroku config:set CONNECTION_STRING=your-db-connection-string-here. heroku config:set NPM_CONFIG_PRODUCTION=false
スクリプトでは、次のように設定していることを覚えておいてください。
"prestart": "babel ./src --out-dir build", "start": "node ./build/bin/www",
アプリを起動するには、 prestart
ステップでbabelを使用してES5にコンパイルする必要があります。これは、babelが開発の依存関係にのみ存在するためです。 これらもインストールできるようにするには、 NPM_CONFIG_PRODUCTION
をfalse
に設定する必要があります。
すべてが正しく設定されていることを確認するには、以下のコマンドを実行します。 アプリページの[ settings
]タブにアクセスして、[ Reveal Config Vars
の表示]をクリックすることもできます。
# check configuration variables heroku config
次に、 git push heroku
を実行します。
アプリを開くには、次のコマンドを実行します。
# open /v1 route heroku open /v1 # open /v1/messages route heroku open /v1/messages
私のように、開発と本番の両方で同じPostgresSQLデータベースを使用している場合、テストを実行するたびにデータベースが削除されることがあります。 再作成するには、次のいずれかのコマンドを実行できます。
# run script locally yarn runQuery # run script with heroku heroku run yarn runQuery
Travisを使用した継続的展開(CD)
ここで、継続的展開(CD)を追加して、CI / CDフローを完成させましょう。 テストが成功するたびに、Travisからデプロイします。
最初のステップは、TravisCIをインストールすることです。 (インストール手順はここにあります。)Travis CIが正常にインストールされたら、以下のコマンドを実行してログインします。 (これはプロジェクトリポジトリで実行する必要があることに注意してください。)
# login to travis travis login --pro # use this if you're using two factor authentication travis login --pro --github-token enter-github-token-here
プロジェクトがtravis-ci.orgでホストされている場合は、 --pro
フラグを削除します。 GitHubトークンを取得するには、アカウントの開発者設定ページにアクセスして生成します。 これは、アカウントが2FAで保護されている場合にのみ適用されます。
.travis.ymlを開き、デプロイセクションを追加します。
deploy: provider: heroku app: master: app-name
ここでは、Herokuにデプロイすることを指定します。 appサブセクションは、リポジトリのmaster
ブランチをHerokuのapp-name
アプリにデプロイすることを指定します。 さまざまなブランチをさまざまなアプリにデプロイすることが可能です。 利用可能なオプションについて詳しくは、こちらをご覧ください。
以下のコマンドを実行してHerokuAPIキーを暗号化し、デプロイセクションに追加します。
# encrypt heroku API key and add to .travis.yml travis encrypt $(heroku auth:token) --add deploy.api_key --pro
これにより、以下のサブセクションがデプロイセクションに追加されます。
api_key: secure: very-long-encrypted-api-key-string
次に、変更をコミットして、ログを監視しながらGitHubにプッシュします。 Travisテストが完了するとすぐに、ビルドがトリガーされます。 このように、テストが失敗した場合、変更が展開されることはありません。 同様に、ビルドが失敗した場合、テストの実行全体が失敗します。 これでCI / CDフローが完了します。
- 私のリポジトリの対応するブランチは11-cdです。
結論
ここまで進んだら、「いいね!」と言います。 このチュートリアルでは、新しいExpressプロジェクトを正常にセットアップしました。 開発の依存関係と継続的インテグレーション(CI)の構成を進めました。 次に、APIエンドポイントへのリクエストを処理する非同期関数を作成しました—テストで完了しました。 次に、エラー処理について簡単に説明しました。 最後に、プロジェクトをHerokuにデプロイし、継続的デプロイを構成しました。
これで、次のバックエンドプロジェクトのテンプレートができました。 私たちはあなたが始めるのに十分なことをしましたが、あなたは続けるために学び続けるべきです。 Express.jsのドキュメントも確認してください。 PostgreSQL
の代わりにMongoDB
を使用したい場合は、まさにそれを行うテンプレートがここにあります。 セットアップを確認できます。 わずかな違いしかありません。
資力
- 「MongoDBを使用してExpressAPIバックエンドを作成する」、GitHubのOrji Chidi Matthew
- 「ミドルウェアを接続するためのショートガイド」、Stephen Sugden
- 「ExpressAPIテンプレート」、GitHub
- 「AppVeyorとTravisCI」、StackShare
- 「HerokuCLI」、Heroku Dev Center
- 「HerokuDeployment」、Travis CI
- 「ミドルウェアの使用」Express.js
- 「エラー処理」、Express.js
- 「はじめに」モカ
nyc
(GitHub)- ElephantSQL
- 郵便配達員
- 特急
- Travis CI
- コード気候
- PostgreSQL
- pgAdmin