Skip to content

Latest commit

 

History

History
1624 lines (985 loc) · 197 KB

README.japanese.md

File metadata and controls

1624 lines (985 loc) · 197 KB

Node.js ベストプラクティス

Node.js Best Practices


101 items Last update: March, 2020 Updated for Node 13.1.0

nodepractices 私たちの Twitter をフォローしましょう! @nodepractices


他の言語で読む: CNCN, BRBR, RURU, PLPL, JAJA, EUEU (ESES, FRFR, HEHE, KRKR and TRTR in progress!)


ステアリングコミッティーコラボレーターによって運営されています

最新のベストプラクティス・お知らせ

  • 🎉 Node.js ベストプラクティスは 50k スターに到達しました: このプロジェクトをこのようなものにしてくれた全てのコントリビューターに感謝申し上げます! 私たちは、増え続ける Node.js のベストプラクティスリストをさらに拡大していくために、これから先たくさんの計画を画策しています。

  • 🎧 ポッドキャスト: 私たちのチームの Yoni Goldberg が、前回の JS Party Podcast(とってもクールなポッドキャストです!)のエピソードに出演して、Node.js ベストプラクティスについて話をしました。🎧 ここから聞きましょう。

  • 🐳 Node.js + Docker ベストプラクティス: Docker を用いたより良いコーディングテクニック 15 項目を含んだ、Docker と Node.js のセクションを新たに公開しました。

  • 🎤 OdessaJS でのトーク: 今週、OdessaJS conference という素晴らしい舞台で、Node.js のテストについて話をします。



ようこそ! まず始めに知っておくべき 3 つのこと

1. 実際、あなたは何十もの Node.js の最高の記事を読んでいます - このリポジトリは、Node.js のベストプラクティスに関するトップランクのコンテンツや、コラボレーターによって書かれたコンテンツをまとめたものです。

2. 最大の集大成であり、毎週のように増え続けています - 現在、80 以上のベストプラクティスやスタイルガイド、アーキテクチャのヒントが記載されています。この「生きた本」がアップデートされた状態を保つために、新しいイシューやプルリクエストは毎日のように作成されています。私たちは、コードの修正や翻訳作業、素晴らしい新たなアイデアの提案に至るまで、あなたの貢献を心待ちにしています。詳しくはライティングガイドラインをご覧ください。

3. ほとんどのベストプラクティスには追加情報があります - ほとんどの項目に 🔗 さらに読む というリンクがあります。このリンクは、コード例や厳選されたブログからの引用、その他多くの情報など、プラクティスを発展させる内容を含んでいます。



Table of Contents

  1. プロジェクト構成のプラクティス (5)
  2. エラーハンドリングのプラクティス (11)
  3. コードスタイルのプラクティス (12)
  4. テストと総合的な品質のプラクティス (13)
  5. 本番環境移行のプラクティス (19)
  6. セキュリティのプラクティス (25)
  7. パフォーマンスのプラクティス (2) (進行中️ ✍️)
  8. Docker のプラクティス (15)



1. プロジェクト構成のプラクティス

✔ 1.1 コンポーネントによりソリューションを構築する

TL;DR: 大規模アプリケーションの最悪の落とし穴は、何百もの依存関係を持つ巨大なコードベースを維持することです。- そのようなモノリスは、新しい機能を取り入れようとする開発者の速度を低下させます。その代わりに、コードをコンポーネントに分割し、それぞれが独自のフォルダや専用のコードベースを取得し、各ユニットが小さくシンプルに保たれていることを確認してください。正しいプロジェクト構造の例を見るには、以下の「さらに読む」を参照してください。

さもないと: 新しい機能をコーディングする開発者が、自分の変更の影響を理解するのに苦労したり、他の依存するコンポーネントを壊すことを恐れたりすると、デプロイが遅くなり、リスクが高くなります。また、すべてのビジネスユニットが分離されていない場合、スケールアウトするのは難しいと考えられています。

🔗 さらに読む: コンポーネントで構成する



✔ 1.2 コンポーネントを階層化し、その境界内にウェブレイヤーを維持する

TL;DR: 各コンポーネントは、ウェブ、ロジック、データアクセスコードのための専用オブジェクトである「レイヤー」を含むべきです。これにより、懸念点がきれいに分離されるだけでなく、システムのモックやテストが大幅に楽になります。これは非常に一般的なパターンですが、API 開発者は Web レイヤーオブジェクト (例: Express req, res) をビジネスロジックとデータレイヤーに渡すことでレイヤーを混ぜる傾向があります - これにより、アプリケーションが特定の Web フレームワークに依存してしまい、特定の Web フレームワークからしかアクセスできなくなってしまいます。

さもないと: Web オブジェクトと他のレイヤーが混在するアプリには、テストコードや CRON ジョブ、メッセージキューからのトリガーなどからアクセスすることはできません。

🔗 さらに読む: アプリケーションを階層化する



✔ 1.3 一般的なユーティリティを npm パッケージとしてラップする

TL;DR: 大規模なコードベースで構成される大規模なアプリでは、logger や暗号化などの横断的に関心のあるユーティリティは、独自のコードでラップし、プライベートな npm パッケージとして公開する必要があります。これにより、複数のコードベースやプロジェクト間で共有することができます。

さもないと: デプロイと依存関係の車輪の作成をしなければいけなくなります

🔗 さらに読む: 機能で構成する



✔ 1.4 Express の「アプリ」と「サーバー」を分離する

TL;DR: Express のアプリ全体を単一の巨大なファイルで定義するという厄介な習慣を回避します。- 「Express」の定義を、API 宣言( app.js )とネットワーク関連( WWW )の少なくとも 2 つのファイルに分離してください。より良い構造にするためには、API 宣言をコンポーネント内に配置してください。

さもないと: API は HTTP 呼び出しのみでテストにアクセスできるようになります(カバレッジレポートを生成するのがより遅く、はるかに困難になります)。何百行ものコードを一つのファイルで管理するのは、おそらく大きな喜びではないでしょう。

🔗 さらに読む: Express の「アプリ」と「サーバー」を分離する



✔ 1.5 環境を意識したセキュアで階層的な設定を使用する

TL;DR: 完璧で欠陥のない設定を行うには、次のようなことが必要です。(a) キーはファイルまたは環境変数から読み込むことができる (b) 秘密情報はコミットされたコードの外側に保持されている (c) 設定が階層化されており、見つけやすくなっている rcnconfconfigconvict など、これらのボックスのほとんどを満たすのに役立つパッケージがいくつかあります。

さもないと: 設定要件のどれかを満たさないと、開発チームや DevOps チーム、おそらく両方ともの頭を悩ませてしまいます。

🔗 さらに読む: 構成のベストプラクティス




⬆ トップに戻る

2. エラーハンドリングのプラクティス

✔ 2.1 非同期エラーハンドリングに Async-Await または promises を使う

TL;DR: コールバックスタイルで非同期エラーを処理することは、おそらく地獄への最短経路でしょう(Pyramid of doom として知られています)。あなたができるコードへの最高の贈り物は、信頼できる promise ライブラリや async-await を使うことです。これらは、try-catch のような、よりコンパクトで親しみやすいコードシンタックスを可能にします。

さもなければ: Node.js のコールバックスタイル、つまり function(err, response) を利用することは、正常な処理を行うコードとエラーハンドリングの混同、過剰なネスト構造、そして厄介なコーディングパターンが原因となって、メンテナンス性の低いコードにつながります。

🔗 さらに読む: コールバック関数の利用を避ける



✔ 2.2 組み込みのエラーオブジェクトのみを使用する

TL;DR: 多くがエラーとして文字列やカスタム型を投げます - これはエラー処理ロジックとモジュール間の相互運用性を複雑にします。promise を reject したのか、例外を投げたのか、エラーを排出したのかに関わらず、組み込みのエラーオブジェクト(またはそれを拡張したオブジェクト)だけ使うことは一貫性を向上させ、情報の欠落を防ぎます。

さもないと: ある要素を呼び出したとき、どの型のエラーが返ってくるか不確かである - といった状況は、適切なエラー処理をより難しいものにします。さらに悪いことに、エラーを表現するためにカスタム型を使うことは、スタックトレースのような重大なエラー情報を失うことに繋がるかもしれません。

🔗 さらに読む: 組み込みのエラーオブジェクトのみを使用する



✔ 2.3 操作上のエラーとプログラマーのエラーを区別する

TL;DR: 操作上のエラー(例: API が無効な入力を受け取る)は、エラーの影響が十分に理解され、そして丁寧に処理される既知のエラーのことを指します。一方で、プログラマーのエラー(例: 未定義の変数を参照しようとする)は、アプリケーションをすぐさま再起動させる、未知のコードエラーのことを指します。

さもないと: エラーが発生したときに毎回アプリケーションを再起動しているかもしれませんが、さほど重要でない、予測可能な、操作上のエラーを原因としてなぜ ~5000 人規模のオンラインユーザーをダウンさせるのでしょうか? その逆もまた理想的ではありません ー 未知のエラー(プログラマーのエラー)が発生したときにアプリケーションをそのまま起動し続けることは、予想外の振る舞いに繋がるかもしれません。この2つを区別することで、機転の利いた振る舞いをさせ、与えられたコンテキストに基づいた適切なアプローチを適用させることができます。

🔗 さらに読む: 操作上のエラーとプログラマーのエラーを区別する



✔ 2.4 エラー処理を一元化し、ミドウェア内で処理をしない

TL;DR: 管理者へのメールやロギングのようなエラー処理ロジックは、エラーが発生したときに全てのエンドポイント(Express ミドルウェア、cron ジョブ、ユニットテストなど)が呼び出す、エラー処理専用の一元化されたオブジェクトにカプセル化されているべきです。

さもないと: エラーを一箇所で処理しないと、コードの重複や、不適切に処理されたエラーの発生に繋がる可能性があります。

🔗 さらに読む: エラー処理を一元化し、ミドウェア内で処理をしない



✔ 2.5 Swagger または GraphQL を利用して API のエラーをドキュメント化する

TL;DR: API の呼び出し元に、どのようなエラーが返ってくるかを示しておくことで、クラッシュすることなく丁寧にエラー処理を行うことができます。RESTful API の場合、通常 Swagger のようなドキュメントフレームワークを使用します。GraphQL を使用している場合は、スキーマやコメントも利用できます。

さもないと: API クライアントがクラッシュして再起動するのは、不明なエラーを受け取ったからかもしれません。注意: API の呼び出し元はあなた自身かもしれません(マイクロサービス構成では非常によくあることです)

🔗 さらに読む: Swagger または GraphQL を利用して API のエラーをドキュメント化する



✔ 2.6 見ず知らずの事象が起きたら潔くプロセスを終了する

TL;DR: 未知のエラーが発生した場合(プログラマーのエラー、ベストプラクティス 2.3 参照)、アプリケーションの健全性に不確実さがあります。一般的に、ForeverPM2 のようなプロセス管理ツールを利用してプロセスを慎重に再起動することが推奨されています。

さもないと: 不明な例外が発生した場合、一部のオブジェクトが不完全な状態(例えば、グローバルに使用されているイベントエミッタが内部的なエラーによりイベントを発火しなくなっている、など)になっている可能性があり、後に来るリクエストが失敗したり、予期せぬ挙動をしたりするかもしれません。

🔗 さらに読む: 見ず知らずの事象が起きたら潔くプロセスを終了する



✔ 2.7 エラーの可視性を高めるために成熟したロガーを使用する

TL;DR: PinoLog4js のような成熟したロギングツールは、エラーの発見と理解を加速します。ですから、console.log のことは忘れましょう。

さもないと: console.log によるログに目を通したり、クエリツールやまともなログビューア無しで扱いにくいテキストファイルを手動で確認したりすると、遅くまで仕事をする羽目になるかもしれません。

🔗 さらに読む: エラーの可視性を高めるために成熟したロガーを使用する



✔ 2.8 お気に入りのテストフレームワークを使用してエラーフローをテストする

TL;DR: プロ仕様の自動化された QA であろうと単純な手動の開発者によるテストであろうと、コードが正常系のシナリオを満たすだけでなく、正しくエラーを処理して返すことを保証してください。Mocha や Chai のようなテストフレームワークは、これを簡単に処理することができます(「さらに読む」の例を参照)

さもないと: 自動であっても手動であっても、テストがなければ、コードが正しいエラーを返すと信用することはできません。意味のあるエラーがなければ、エラー処理はできません。

🔗 さらに読む: お気に入りのテストフレームワークを使用してエラーフローをテストする



✔ 2.9 APM 製品を利用してエラーとダウンタイムを発見する

TL;DR: モニタリング・パフォーマンス計測を行う製品(APM として知られています)は、コードベースや API をプロアクティブに計測し、見落としていたエラーやクラッシュ、処理の遅い部分を自動的にハイライトすることができます。

さもないと: API のパフォーマンスとダウンタイムの計測に多大な労力を費やしているかもしれませんが、現実のシナリオにおいてどの部分のコードが最も遅いのか、そしてそれらがどのように UX に影響を及ぼしているのか、あなたが気づくことは恐らくないでしょう。

🔗 さらに読む: APM 製品を利用してエラーとダウンタイムを発見する



✔ 2.10 未処理の reject された promise を捕捉する

TL;DR: promise の中で投げられた全ての例外は、開発者が明示的に処理を行うことを忘れていない限り、飲み込まれて破棄されます。たとえコードが process.uncaughtException をサブスクライブしていたとしてもです!process.unhandledRejection イベントに登録することで、この問題を乗り越えることができます。

さもないと: あなたのエラーは飲み込まれて、何のトレースも残しません。心配することは、何も残りません。

🔗 さらに読む: 未処理の reject された promise を捕捉する



✔ 2.11 専用のライブラリを利用して引数の検証を高速に行う

TL;DR: API の入力をアサートすることで、後から追跡するのが非常に難しい厄介なバグを避けることができます。ajvJoi のような非常にクールなヘルパーライブラリを利用しない限り、バリデーションコードを書くことは一般的に退屈な作業です。

さもないと: 考えてみて下さい ー 関数は数値の引数「Discount」を受け取ることを期待していますが、呼び出し元が値を渡すのを忘れてしまいました。その後、コードが Discount!=0 (許容されたディスカウントの量が 0 よりも大きいことを想定) であるということをチェックし、そのチェックをクリアした場合にユーザーがディスカウントを受けられるようにしました。オーマイガー、なんて厄介なバグなんでしょう。わかりますか?(訳注:「さらに読む」に具体的なコード例が載っています)

🔗 さらに読む: 専用のライブラリを利用して引数の検証を高速に行う




⬆ トップに戻る

3. コードスタイルのプラクティス

✔ 3.1 ESLint を使う

TL;DR: ESLint は、コードエラーの可能性をチェックし、コードスタイルを修正するためのデファクトスタンダードで、細かい間隔の問題を特定するだけでなく、開発者が分類せずにエラーを投げるような深刻なコードアンチパターンを検出することもできます。ESLint はコードスタイルを自動的に修正することができますが、prettierbeautify のような他のツールは、フィックスの書式設定をより強力にし、ESLint と連携して動作します。

さもないと: 開発者は退屈な間隔や線幅の問題に集中し、プロジェクトのコードスタイルを考えすぎて時間を無駄にしてしまうかもしれません。

🔗 さらに読む: ESLint と Prettier を使う



✔ 3.2 Node.js に特化したプラグイン

TL;DR: vanilla JavaScript をカバーする ESLint の標準ルールに加えて、eslint-plugin-nodeeslint-plugin-mochaeslint-plugin-node-security のような Node.js に特化したプラグインを追加します。

さもないと: 多くの欠陥のある Node.js のコードパターンは、レーダーを逃れてしまうかもしれません。例えば、開発者は攻撃者が任意の JS スクリプトを実行できるように、パスとして与えられた変数を持つファイルを require(variableAsPath) しているかもしれません。Node.js の linters は、そのようなパターンを早期に検出して知らせてくれます。

✔ 3.3 コードブロックの中括弧を同一行上でスタートさせる

TL;DR: コードブロックの冒頭の中括弧は、冒頭の文と同じ行でなければなりません。

コード例

// Do
function someFunction() {
  // code block
}

// Avoid
function someFunction()
{
  // code block
}

さもないと: このベストプラクティスから逸脱すると、以下の StackOverflow スレッドにあるように、予期せぬ結果を招く可能性があります。:

🔗 さらに読む: "なぜ中括弧の配置によって結果が変わるのか?" (StackOverflow)



✔ 3.4 ステートメントを適切に区切る

ステートメントを区切るためにセミコロンを使うか使わないかに関わらず、不適切な改行や自動セミコロン挿入のよくある落とし穴を知っておくことで、通常の構文エラーをなくすことができます。

TL;DR: ESLint を使用して、分離の懸念について認識する。 PrettierStandardjs は、これらの問題を自動的に解決することができます。

さもないと: 前のセクションで見たように、JavaScript のインタプリタは、セミコロンがない場合は自動的に文の最後にセミコロンを追加したり、ステートメントが本来あるべき場所で終わっていないとみなしたりすることで、望まない結果になってしまう可能性があります。代入を使用し、即時に呼び出された関数式の使用を避けることで、予期せぬエラーのほとんどを防ぐことができます。

3.4 コード例

// する
function doThing() {
    // ...
}

doThing()

// する

const items = [1, 2, 3]
items.forEach(console.log)

// 避ける — 例外を投げる
const m = new Map()
const a = [1,2,3]
[...m.values()].forEach(console.log)
> [...m.values()].forEach(console.log)
>  ^^^
> SyntaxError: Unexpected token ...

// 避ける — 例外を投げる
const count = 2 // 2() を実行しようとしますが、2 は関数ではありません
(function doSomething() {
  // 凄いことをする
}())
// 直ちに呼び出された関数の前、const 定義の後にセミコロンを置く、匿名関数の戻り値を変数に保存する、あるいは IIFE を完全に回避する

🔗 さらに読む: "準 ESLint ルール" 🔗 さらに読む: "予期せぬ複数行を許さない ESLint のルール"



✔ 3.5 関数に名前を付ける

TL;DR: クロージャやコールバックを含むすべての関数に名前を付けます。匿名関数は避けてください。これは特に node アプリをプロファイリングするときに便利です。すべての関数に名前を付けることで、メモリスナップショットをチェックする際に何を見ているのかを簡単に理解することができます。

さもないと: コアダンプ(メモリスナップショット)を使用した本番環境の問題のデバッグは、匿名関数からのメモリ消費が大きいことに気づくと、困難になるかもしれません。



✔ 3.6 変数、定数、関数、クラスの命名規則を使用する

TL;DR: 定数、変数、関数の命名をするときは lowerCamelCase を使用し、クラスの命名をするときは**UpperCamelCase** (頭文字も大文字) を使用してください。これは、プレーンな変数/関数とインスタンス化を必要とするクラスを簡単に区別するのに役立ちます。記述的な名前を使用しますが、短くしてください。

さもないと: JavaScript は、最初にインスタンスを作成せずにコンストラクタ(「クラス」)を直接呼び出すことができる世界で唯一の言語です。その結果、クラスと関数構造体は UpperCamelCase から始まることで区別されます。

3.6 コード例

// クラスには、UpperCamelCase を使用します
class SomeClassExample {}

// const 名には const キーワードと lowerCamelCase を使用します
const config = {
  key: "value",
};

// 変数や関数名には lowerCamelCase を使用します
let someVariableExample = "value";
function doSomething() {}



✔ 3.7 let よりも const を優先してください。var はいりません

TL;DR: const を使うということは、一度代入された変数は再代入できないということを意味します。const を優先することで、同じ変数を異なる用途に使いたくなることを防ぎ、コードをより明確にすることができます。変数を再割り当てする必要がある場合、例えば for ループの中などでは、let を使って宣言します。もう一つの重要な点は、let を使って宣言された変数は、それが定義されたブロックスコープ内でのみ利用可能であるということです。var はブロックスコープではなく関数スコープであり、ES6 では使うべきではない ので、constlet がある以上必要ありません。

さもないと: 頻繁に変化する変数に従うと、デバッグが非常に面倒になります。

🔗 さらに読む: JavaScript ES6+: var、let、それとも const ?



✔ 3.8 関数の内部ではなく、まずモジュールを require する

TL;DR: 各ファイルの先頭かつ、全ての関数の前かつ外でモジュールを require します。このシンプルなベストプラクティスは、ファイルの依存関係を簡単かつ迅速にトップに表示するのに役立つだけでなく、いくつかの潜在的な問題を回避することができます。

さもないと: Require は Node.js によって同期的に実行されます。関数内から呼び出された場合、他のリクエストがより重要なタイミングで処理されるのをブロックすることがあります。また、require されたモジュールやそれ自身の依存関係がエラーを出してサーバをクラッシュさせてしまった場合は、できるだけ早く見つけた方が良いでしょう。関数の中からモジュールが require されている場合は早く見つけることができないかもしれません。



✔ 3.9 ファイルに直接アクセスするのではなく、フォルダごとにモジュールを require します

TL;DR: モジュール/ライブラリをフォルダ内で開発する場合は、モジュールの内部を公開する index.js ファイルを配置し、すべての使用者がそれを通過するようにします。これはモジュールへの「インタフェース」として機能し、約束事を破ることなく将来の変更を容易にします。

さもないと: ファイルの内部構造や署名を変更すると、クライアントとのインタフェースが壊れてしまう可能性があります。

3.9 コード例

// する
module.exports.SMSProvider = require("./SMSProvider");
module.exports.SMSNumberResolver = require("./SMSNumberResolver");

// 避ける
module.exports.SMSProvider = require("./SMSProvider/SMSProvider.js");
module.exports.SMSNumberResolver = require("./SMSNumberResolver/SMSNumberResolver.js");



✔ 3.10 === 演算子を使用する

TL;DR: 弱い抽象的な等号演算子 == よりも厳密な等号演算子 === を優先してください。== は 2 つの変数を共通の型に変換した後に比較します。=== には型変換はなく、両方の変数が同じ型で等しくなければいけません。

さもないと: == 演算子で比較すると、同じでない値でも真を返すかもしれません。

3.10 Code example

"" == "0"; // false
0 == ""; // true
0 == "0"; // true

false == "false"; // false
false == "0"; // true

false == undefined; // false
false == null; // false
null == undefined; // true

" \t\r\n " == 0; // true

=== を使用した場合、上のすべてのステートメントは false を返します。



✔ 3.11 コールバックを避け、Async Await を使用する

TL;DR: Node 8 LTS は Async-await を完全にサポートするようになりました。これは、コールバックやプロミスに取って代わる非同期コードの新しい扱い方です。Async-await はノンブロッキングであり、非同期コードを同期的に見せてくれます。コードに与えることができる最高の贈り物は、try-catch のようなよりコンパクトで親しみやすいコード構文を提供する async-await を使うことです。

Otherwise: コールバックスタイルで非同期エラーを処理するのは、おそらく地獄への最速の方法です。- このスタイルでは、エラーのチェックをすべて強制し、厄介なコードの入れ子を処理し、コードの流れについての推論を困難にします。

🔗さらに読む: async await 1.0 のガイド



✔ 3.12 arrow 関数式 (=>) を使う

TL;DR: プロミスやコールバックを受け入れる古い API を扱う場合は、async-await を使用して関数パラメータを避けることをお勧めしますが、arrow 関数はコード構造をよりコンパクトにし、ルート関数の語彙的なコンテキストを保持します。(すなわち this )

さもないと: コードが長いと( ES5 の関数では)バグが発生しやすく、読むのが面倒になります。

🔗 さらに読む: arrow 関数を採用する時が来た




⬆ トップに戻る

4. テストと総合的な品質のプラクティス

✔ 4.1 最低でも、API(コンポーネント)のテストを書く

TL;DR: 多くのプロジェクトでは、短いタイムスケジュールが原因で自動化テストを行っていないか、または「テストプロジェクト」がコントロール不能となり廃れてしまうことがしばしばあります。そのため、優先度を決めて、書くのが最も容易であり、ユニットテストより多くのカバレッジを提供してくれる API テストから始めましょう(Postman のようなツールを利用して、コード無しで API テストを手作りすることもできます)。その後、リソースや時間に余裕が出てきたら、ユニットテストや DB テスト、パフォーマンステストといった発展的なタイプのテストを実施してください。

さもないと: ユニットテストを書くことに長時間費やしても、システムカバレッジが 20% しかないことに気づくかもしれません。



✔ 4.2 各テスト名に 3 つの要素を含む

TL;DR: テストを要件レベルを表現することで、コード内部をよく知らない QA エンジニアや開発者に対しても説明的であるようにしてください。テスト名には、何がテストされていて(テスト対象のユニット)、どのような状況で、どのような結果が期待されているのかを記述してください。

さもないと: "Add product" という名付けられたテストが通らず、デプロイが失敗しました。これは、実際に何がうまく動作しなかったのかを示しているでしょうか?

🔗 さらに読む: 各テスト名に 3 つの要素を含む



✔ 4.3 AAA パターンを用いてテストを構成する

TL;DR: 上手に分けられた 3 つのセクションを利用してテストを構成してください: Arrange、Act、そして Assert (AAA) です。まず最初の部分でテストのセットアップを行い、次にテスト対象のユニットの実行、そして最後にアサーションフェーズに入ります。この構造に従うことで、コードを読む人がテストプランを理解するために頭脳の CPU を費やさないことが保証されます。

さもないと: メインコードを理解するのに長時間費やすだけでなく、今までシンプルな部分であったはずのもの(テスト)が、脳のリソースを奪います。

🔗 さらに読む: AAA パターンを用いてテストを構成する



✔ 4.4 Linter を用いてコードの問題を検出する

TL;DR: Linter を使用して、コードの基本的な質をチェックし、アンチパターンを早期に検出してください。テスト前に Linter を実行し、コミット前の Git-hook として追加しておけば、レビューや問題を修正するのに必要な時間を最小限に抑えることができます。セクション 3 の「コードスタイルのプラクティス」も参考にしてください。

さもないと: アンチパターンや脆弱性を含む可能性のあるコードを本番環境に渡してしまうかもしれません。



✔ 4.5 グローバルなテストフィクスチャとシードを避け、テストごとにデータを追加する

TL;DR: テスト同士が結合してしまうことを防ぎ、テストフローの理解を容易にするために、各テストは独自の DB データ行のセットを用意し、それらを利用して実行されるべきです。テストがいくつかの DB データをプルしたり、その存在を仮定する必要がある場合には、明示的にデータを追加し、他のレコードに変更を加えないようにしなければなりません。

さもないと: テストが失敗したことによってデプロイが中止されるというシナリオを考えてみましょう。チームは貴重な時間を調査に費やし、結果として悲しい結論にたどり着きます: システムは機能していますが、テスト同士が干渉しあって、ビルドを壊しているのです。

🔗 さらに読む: グローバルなテストフィクスチャとシードを避け、テストごとにデータを追加する



✔ 4.6 脆弱性のある依存関係がないか常に検査する

TL;DR: Express のような最も評判の良い依存関係にも、既知の脆弱性があります。これは、ビルド毎に CI において実行できる 🔗 npm audit や 🔗 snyk.io といったコミュニティや商用のツールを利用することで、簡単に検査することができます。

さもないと: 専用のツールを使用せずに、コードを安全に保つには、新しい脅威についての情報を、常に追う必要があります。これは非常に面倒です。



✔ 4.7 テストにタグをつける

TL;DR: 異なるテストは、異なるシナリオ下において実行しなければなりません: I/O の無いクイックスモークテストは、開発者がファイルを保存したりコミットした際に実施し、完全なエンドツーエンドテストは新しいプルリクエストが出されたときに実施する、などです。これは、テストの手綱を掴んで望み通りのテストセットを実行できるように、 #cold #api #sanity といったようにキーワードでテストをタグ付けすることで実現できます。例えば、Mocha を利用して sanity テストグループを実施する方法は次の通りです: mocha --grep 'sanity'

さもないと: 小さな変更をするたびに多くの DB クエリを実施するテストを含む全てのテストを実行することは、非常に遅く、そして開発者がテストを実行しなくなります。



✔ 4.8 間違ったテストパターンを特定するためにテストカバレッジをチェックする

TL;DR: Istanbul/NYC のようなコードカバレッジツールは 3 つの理由から素晴らしいといえます: 無料で提供されている(このレポートの恩恵を受けるために努力は必要ありません)、テストカバレッジの低下を特定するのに役立つ、そして最後に、テストのミスマッチを強調してくれることです。色付けされたコードカバレッジレポートを見ることで、例えば、キャッチ句のようなテストが全く実施されていない領域(つまり、テストがハッピーパスのみテストしていて、エラー時にどのように振る舞うかをテストしていない、ということです)に気づくかもしれません。カバレッジが特定に閾値を下回ったらビルドが失敗するように設定しましょう。

さもないと: コードの大部分がテストでカバーされていないことを教えてくれる、自動化されたメトリックが存在しないことになります。



✔ 4.9 パッケージが古くなっていないか点検する

TL;DR: お気に入りのツール(例えば、「npm outdated」や「npm-check-updates」など)を使って、インストールされたパッケージが古くなっていることを検出し、このチェックを CI パイプラインの中に組み込み、深刻な場合にはビルドを失敗させてください。深刻な場合とは例えば、インストールされているパッケージが 5 回のパッチコミット分遅れている場合(例えば、ローカルバージョンが 1.3.1 でリポジトリバージョンが 1.3.8 である、など)や、作者によって非推奨とタグ付けされている場合などがあります。ビルドをキルして、このバージョンのデプロイを禁止してください。

さもないと: 作者によって明示的に危険であるとタグ付けされたパッケージを、本番環境で実行することになります。



✔ 4.10 エンドツーエンドテストのために本番に近い環境を使用する

TL;DR: ライブデータを含むエンドツーエンド(e2e)テストは、DB のような複数の重たいサービスに依存するため、CI プロセスにおける最も弱い接続部となっていました。できる限り本番環境に近い環境を使用してください(注意: コンテンツが不足しています。「さもないと」から判断するに、docker-compose について言及されているはずです)

さもないと: docker-compose を使用しない場合、チームは開発者のマシンを含む各テスト環境のためのテスト DB を管理し、環境によって結果に差異が出ないようにそれらすべての DB が同期された状態を保たなくてはなりません。



✔ 4.11 静的解析ツールを使用して定期的にリファクタリングする

TL;DR: 静的解析ツールを利用することは、客観的な視点をもたらし、コードの品質向上や保守性の維持に役立ちます。静的解析ツールを CI に追加することで、コードの臭いを発見した際にビルドを失敗させることができます。シンプルな linting に勝るポイントとしては、複数ファイルを含むコンテキストで品質を検査できること(例:重複の検出)、高度な分析を実施できること(例:コードの複雑さ)、そしてコードの問題の履歴や進行状況を追跡できることです。使用できるツールの例としては、Sonarqube (2,600+ stars) と Code Climate (1,500+ stars) の 2 つがあります。

さもないと: コードの品質が低いと、ピカピカの新しいライブラリや最新の機能では修正できない類のバグやパフォーマンスが常に問題となります。

🔗 さらに読む: リファクタリング!



✔ 4.12 CI プラットフォームを慎重に選択する(Jenkins vs CircleCI vs Travis vs その他すべて)

TL;DR: 継続的インテグレーションプラットフォーム (CI/CD) は全ての品質に関わるツール(テストや lint など)をホストするので、プラグインのエコシステムが充実しているはずです。Jenkins は最大のコミュニティを持ち、非常に強力なプラットフォームであるため、多くのプロジェクトでデフォルトとして使われていましたが、複雑なセットアップと多大な学習コストが難点でした。最近では、CircleCI のような SaaS ツールを使用することで、CI ソリューションをセットアップすることが非常に簡単になってきました。こういったツールは、インフラ全体の管理にコストをかけることなく柔軟な CI パイプラインを構築することを可能にします。最終的には、堅牢性とスピードの間のトレードオフとなります ー 慎重にどちらを取るか選んでください。

さもないと: ニッチなベンダーを選択すると、高度なカスタマイズが必要になった際に困るかもしれません。一方で、Jenkins を選択するとインフラのセットアップに貴重な時間を費やすことになる可能性があります。

🔗 さらに読む: CI プラットフォームを選択する

✔ 4.13 ミドルウェアを分離してテストする

TL;DR: ミドルウェアが多くのリクエストにまたがる巨大なロジックを保持している場合は、ウェブフレームワーク全体を起動することなく、分離してテストする価値があります。これは、{req, res, next} オブジェクトをスタブ化してスパイすることで容易に達成することができます。

さもないと: Express ミドルウェアにおけるバグ === ほぼ全てのリクエストにおけるバグ

🔗 さらに読む: ミドルウェアを分離してテストする




⬆ トップに戻る

5. 本番環境移行のプラクティス

✔ 5.1. モニタリング

TL;DR: モニタリングとは、顧客よりも先に問題を発見するゲームです。– 明らかに、これは類を見ないほど重要なこととして割り当てられるべきです。市場には多くのオファーが溢れていますので、まずはあなたが守らなければならない基本的な指標を定義することから始めてみてください(私の提案はこの中にあります)。その後、追加の手の込んだ機能を確認し、すべてのボックスにチェックを入れるソリューションを選択します。ソリューションの概要については、以下の「要点」をクリックしてください。

さもないと: 失敗 === 失望したお客さん。シンプルです。

🔗 さらに読む: モニタリング!



✔ 5.2. スマートロギングで透明性を高める

TL;DR: ログは、デバッグ ステートメント用のゴミ倉庫にも、アプリのストーリーを伝える美しいダッシュボードのイネーブラーにもなり得ます。1 日目からロギングプラットフォームを計画しましょう:ログをどのように収集、保存、分析するかで、必要な情報(エラー率、サービスやサーバーを介したトランザクション全体の追跡など)を実際に抽出できるようにします。

さもないと: 推論するのが難しいブラックボックスになってしまい、追加情報を追加するためにすべてのロギングステートメントを書き直します。

🔗 さらに読む: スマートロギングで透明性を高める



✔ 5.3. 可能な限りのこと全て( gzip、SSL など)をリバースプロキシに委譲する

TL;DR: Node は gzip や SSL の終了などの CPU 負荷の高いタスクを行うのが非常に苦手です。代わりに nginx, HAproxy, クラウドベンダーのサービスのような「本物の」ミドルウェアサービスを使うべきです。

さもないと: 貧弱なシングルスレッドは、アプリケーションコアを処理する代わりにインフラタスクを行うことに忙しくなり、パフォーマンスはそれに応じて低下します。

🔗 さらに読む: 可能な限りのこと全て( gzip、SSL など)をリバースプロキシに委譲する



✔ 5.4. 依存関係をロックする

TL;DR: コードはすべての環境で同一でなければなりませんが、驚くべきことに npm はデフォルトで環境間で依存関係をドリフトさせることができます。– 様々な環境でパッケージをインストールすると、パッケージの最新のパッチバージョンを取得しようとします。これを克服するには、各パッケージの正確な (最新版ではない) バージョンを保存するように各環境に指示する npm 設定ファイル .npmrc を使用します。あるいは、より細かい制御を行うには npm shrinkwrap を使用してください。*更新: NPM5 では、依存関係はデフォルトでロックされています。新しいパッケージマネージャー、Yarn もデフォルトでカバーしてくれました。

さもないと: QA はコードを徹底的にテストし、本番環境では異なる挙動をするバージョンを承認します。さらに悪いことに、同じクラスタ内の異なるサーバが異なるコードを実行する可能性があります。

🔗 さらに読む: 依存関係をロックする



✔ 5.5. 適切なツールを使用してプロセスの稼働時間を守る

TL;DR: プロセスが進み、失敗した時点で再起動しなければなりません。単純なシナリオでは、PM2 のようなプロセス管理ツールで十分かもしれませんが、今日の「 docker 化」された世界では、クラスタ管理ツールも考慮する必要があります。

さもないと: 明確な戦略を持たずに何十ものインスタンスを実行し、あまりにも多くのツール(クラスタ管理、docker、PM2)を一緒に使いすぎると、DevOps のカオスにつながる可能性があります。

🔗 さらに読む: 適切なツールを使用してプロセスの稼働時間を守る



✔ 5.6. すべての CPU コアを利用する

TL;DR: 基本的な形として、Node アプリは他のすべてがアイドル状態のままで、単一の CPU コア上で動作します。 Node プロセスを複製し、すべての CPU を利用するのはあなたの義務です。 – 中小規模のアプリでは、Node クラスタや PM2 を使用することができます。大規模なアプリケーションでは、Docker クラスタ( K8S や ECS など)や Linux の init システム( systemd など)をベースにしたデプロイスクリプトを使用してプロセスを複製することを検討してください。

さもないと: あなたのアプリは、利用可能なリソースの 25%、もしくはそれ以下しか使用していない可能性が高いです(!)。一般的なサーバは 4 つ以上の CPU コアを持っていますが、 Node.js のナイーブなデプロイでは 1 つしか利用していないことに注意してください( AWS beanstalk のような PaaS サービスを利用している場合でも!)。

🔗 さらに読む: すべての CPU コアを利用する



✔ 5.7. 「メンテナンスエンドポイント」を作成する

TL;DR: メモリ使用量や REPL などのシステム関連情報をセキュアな API で公開します。標準ツールや歴戦のツールに頼ることを強くお勧めしますが、貴重な情報や操作はコードを使った方が簡単にできるものもあります。

さもないと: 多くの「診断デプロイ」を実行していることがわかります。– 診断目的のための情報を抽出するためだけにコードを本番環境に出荷するなど

🔗 さらに読む: 「メンテナンスエンドポイント」を作成する



✔ 5.8. APM 製品を使用してエラーやダウンタイムを発見する

TL;DR: アプリケーションモニタリングおよびパフォーマンス製品( APM)は、コードベースと API を積極的に測定することで、従来のモニタリングを超えて、サービスや階層間のユーザーエクスペリエンス全体を自動的に測定することができます。例えば、一部の APM 製品では、エンドユーザー側でロードが遅すぎるトランザクションを強調表示しながら、根本的な原因を示唆することができます。

さもないと: API のパフォーマンスやダウンタイムの測定に多大な労力を費やすことになるかもしれません。実世界のシナリオで最も遅いコード部分はどれか、それが UX にどのように影響するのか、おそらくあなたは意識することはないでしょう。

🔗 さらに読む: APM 製品を使用してエラーやダウンタイムを発見する



✔ 5.9. コードを本番に即したものにする

TL;DR: ゴールを意識してコードを作成し、1 日目から制作計画を立てます。ちょっと漠然としているように聞こえるので、生産保守と密接に関係する開発のヒントをいくつかまとめてみました(下の Gist をクリックしてください)。

さもないと: 世界チャンピオンの IT/DevOps の男でも、下手に書かれたシステムを救うことはできません。

🔗 さらに読む: コードを本番に即したものにする



✔ 5.10. メモリ使用量を測定してガードする

TL;DR: Node.js はメモリとの関係で物議を醸しています: v8 エンジンはメモリ使用量にソフトな制限(1.4 GB )があり、Node のコードにはメモリリークの経路が知られています。– そのため、Node のプロセスメモリを監視することは必須です。小さなアプリでは、シェルコマンドを使って定期的にメモリを測定することができますが、中規模以上のアプリでは、メモリ監視を堅牢な監視システムに組み込むことを検討してください。

さもないと: あなたのプロセスメモリは、Walmart で起こったように、1 日に 10MB もリークするかもしれません。

🔗 さらに読む: メモリ使用量を測定してガードする



✔ 5.11. フロントエンドの資産を Node から取り出す

TL;DR: 専用のミドルウェア (nginx, S3, CDN) を使用してフロントエンドのコンテンツを提供します。なぜなら、シングルスレッドモデルのため、多くの静的ファイルを扱う場合、Node のパフォーマンスは非常に痛手を受けるからです。

さもないと: あなたの Node のシングルスレッドは、何百もの html/images/angular/react ファイルのストリーミングに忙殺され、本来の目的のために生まれたタスクにすべてのリソースを確保することができません。– 動的コンテンツの提供

🔗 さらに読む: フロントエンドの資産を Node から取り出す



✔ 5.12. ステートレスのままで、ほぼ毎日サーバーを停止させる

TL;DR: 任意のタイプのデータ(ユーザーセッション、キャッシュ、アップロードされたファイルなど)を外部データストア内に保存します。定期的にサーバを「停止する」ことを検討するか、ステートレスな動作を明示的に行う「サーバレス」プラットフォーム(AWS Lambda など)を使用することを検討してください。

Otherwise: 特定のサーバーで障害が発生すると、障害のあるマシンを停止する代わりに、アプリケーションのダウンタイムが発生します。さらに、特定のサーバーに依存しているため、スケーリングアウトの弾力性はより困難になります。

🔗 さらに読む: ステートレスのままで、ほぼ毎日サーバーを停止させる



✔ 5.13. 脆弱性を自動的に検出するツールを使用する

TL;DR: Express のような最も評判の良い依存関係にも、(時折)システムを危険にさらす既知の脆弱性があります。脆弱性を常にチェックして警告するコミュニティや商用ツール(ローカルまたは GitHub)を使えば簡単に手なずけることができ、いくつかはすぐにパッチを当てることもできます。

さもないと: 専用のツールを使用せずに脆弱性からコードをクリーンに保つには、新しい脅威についてのオンライン出版物を常にフォローする必要があります。とても面倒です。

🔗 さらに読む: 脆弱性を自動的に検出するツールを使用する



✔ 5.14. 各ログステートメントにトランザクション ID を割り当てる

TL;DR: transaction-id: {任意の値} で、単一のリクエスト内の各ログエントリに同じ識別子を割り当てます。そうすることで、ログのエラーを検査する際に、前後に何が起こったかを簡単に結論付けることができます。残念ながら、非同期の性質上、これを Node で実現するのは容易ではありません。内部のコード例を参照してください。

さもないと: – 前に何が起こったのか – というコンテキストなしでプロダクションのエラーログを見ると、問題の原因を究明するのが非常に難しくなり、時間がかかります。

🔗 さらに読む: 各ログ文に 'TransactionId' を割り当てる



✔ 5.15. NODE_ENV=production を設定する

TL;DR: 環境変数 NODE_ENV を「production」または「development」に設定して、本番環境での最適化を有効にするかどうかのフラグを立てます。– 多くの npm パッケージが現在の環境を判断し、本番用にコードを最適化します。

さもないと: この単純なプロパティを省略すると、パフォーマンスが大きく低下する可能性があります。例えば、サーバサイドのレンダリングに Express を使用する場合、NODE_ENV を省略すると 3 倍も遅くなります。

🔗 さらに読む: NODE_ENV=production を設定する



✔ 5.16. 自動化された、アトミックでゼロダウンタイムのデプロイを設計する

TL;DR: 調査によると、多くのデプロイを行うチームほど、深刻なプロダクションの問題が発生する確率が低くなることがわかっています。リスクの高い手動ステップやサービスのダウンタイムを必要としない高速で自動化されたデプロイは、デプロイプロセスを大幅に改善します。Docker と CI ツールを組み合わせることが、合理化されたデプロイのための業界標準となったため、これを使って達成する必要があるでしょう。

さもないと: 長時間のデプロイ -> プロダクションのダウンタイムと人為的なミス -> デプロイに自信のないチーム -> デプロイ数と機能の減少



✔ 5.17. Node.js の LTS リリースを使用する

TL;DR: 重要なバグフィックス、セキュリティアップデート、パフォーマンスの改善を受けるために、Node.js の LTS バージョンを使用していることを確認してください。

さもないと: 新たに発見されたバグや脆弱性は、本番環境で運用中のアプリケーションを悪用するために使用される可能性があり、アプリケーションは様々なモジュールでサポートされておらず、保守が困難になる可能性があります。

🔗 さらに読む: Node.js の LTS リリースを使用する



✔ 5.18. アプリ内でログをルーティングしない

TL;DR: ログの送信先は、アプリケーションコード内で開発者によってハードコーディングされるべきではなく、代わりに、アプリケーションが実行される実行環境によって定義されるべきです。開発者はロガーユーティリティを使って stdout にログを書き、実行環境 (コンテナやサーバなど) に stdout のストリームを適切な宛先 (Splunk, Graylog, ElasticSearch など) にパイプさせるべきです。

さもないと: アプリケーションがログのルーティングをハンドリングする === スケールアップが難しい、ログの損失、懸念事項の分離が悪い

🔗 さらに読む: ログルーティング



✔ 5.19. パッケージを npm ci でインストールする

TL;DR: 本番環境のコードが、テストしたパッケージの正確なバージョンを使用していることを確認する必要があります。 npm ci を実行して、package.json と package-lock.json に一致する依存関係のクリーンインストールを厳密に行います。このコマンドは、継続的インテグレーションパイプラインのような自動化された環境で使用することをお勧めします。

さもないと: QA はコードを徹底的にテストし、本番環境では異なる動作をするバージョンを承認します。さらに悪いことに、同じプロダクションクラスタ内の異なるサーバが異なるコードを実行する可能性があります。

🔗 さらに読む: npm ci を使う




⬆ トップに戻る

6. セキュリティのプラクティス

54 items

✔ 6.1. linter のセキュリティルールを受け入れる

TL;DR: eslint-plugin-security のようなセキュリティ関連の linter プラグインを利用して、セキュリティの脆弱性や問題をできる限り早く、できればコーディング段階で捕まえてください。これにより、eval の使用や子プロセスの呼び出し、(ユーザー入力などの)文字列リテラルを持つモジュールのインポートなど、セキュリティ上の弱点をキャッチするのに役立ちます。下記の「さらに読む」をクリックして、セキュリティ linter によって捕捉されるコード例を参照してください。

さもなければ: 開発時には単純なセキュリティ上の弱点だったかもしれないものが、本番環境では大きな問題となります。同様に、プロジェクトが一貫したコードセキュリティプラクティスに従わない場合もあり、脆弱性が入り込んだり、リモートリポジトリに機密情報がコミットされたりする可能性があります。

🔗 さらに読む: Lint ルール



✔ 6.2. ミドルウェアを使用して同時リクエストを制限する

TL;DR: DOS 攻撃はとても非常に有名で、比較的簡単に実行することができます。クラウドのロードバランサーやファイアウォール、nginx、rate-limiter-flexible パッケージなどの外部サービス、または(小規模で重要度の低いアプリケーションの場合は)レートリミットミドルウェア(express-rate-limit など)などを使用して、レートリミットを実装してください。

さもないと: アプリケーションは攻撃を受ける可能性があり、結果としてユーザーに不十分なサービスを提供したり、サービス停止をしなければならない状況に陥ります。

🔗 さらに読む: レートリミットの実装



✔ 6.3 設定ファイルからシークレットを抽出する、もしくはパッケージを利用して暗号化する

TL;DR: 設定ファイルやソースコードに平文でシークレットを格納してはいけません。代わりに、Vault 製品や Kubernetes/Docker シークレット、環境変数のようなシークレット管理システムを利用してください。仕方なく、ソースコントロールにシークレットを格納する場合は、暗号化して管理(キーのローリング、有効期限の設定、監査など)をする必要があります。誤ってシークレットをコミットしないように、pre-commit/push hooks を利用してください。

さもないと: ソースコントロールは、たとえプライベートリポジトリであっても誤ってパブリックになる可能性があり、その時点で全てのシークレットが公開されてしまいます。外部サービスに与えられたソースコントロールへのアクセス権限は、関連するシステム(データベース、API、その他サービスなど)へのアクセス権限をうっかり与えてしまうことがあります。

🔗 さらに読む: シークレット管理



✔ 6.4. O/R マッパ/ODM ライブラリを使用してクエリインジェクション脆弱性を防ぐ

TL;DR: SQL/NoSQL インジェクションやその他の悪意ある攻撃を防ぐために、データをエスケープしたり、名前付きやインデックス付きのパラメータ化されたクエリをサポートしていたり、ユーザー入力が期待する型となっているか検証してくれる O/R マッパ/ODM やデータベースライブラリを活用してください。JavaScript のテンプレート文字列や文字列の結合を使用して値をクエリに挿入してはいけません。これはアプリケーションに広範囲の脆弱性を与えます。全ての評判の良い Node.js データアクセスライブラリ(Sequelize, Knex, mongoose など)はインジェクション攻撃に対してあらかじめ対策されています。

さもないと: 未検証またはサニタイズされていないユーザー入力は、MongoDB のような NoSQL データベースで作業している際にオペレーターインジェクションを招きますし、適切なサニタイズシステムまたは O/R マッパ を利用しないことは容易に SQL インジェクション攻撃を招き、多大な脆弱性を生みます。

🔗 さらに読む: ORM/ODM ライブラリを使用してクエリインジェクション脆弱性を防ぐ



✔ 6.5. 一般的なセキュリティベストプラクティス集

TL;DR: Node.js とは直接関係のないセキュリティに関するアドバイス集です ー Node における実装は他の言語とあまり違いはありません。さらに読むをクリックして、読み進めてください。

🔗 さらに読む: 一般的なセキュリティベストプラクティス



✔ 6.6. セキュリティ強化するために HTTP レスポンスヘッダを調整する

TL;DR: クロスサイトスクリプティング(XSS)やクリックジャッキング、その他の悪意のある攻撃などの一般的な攻撃を攻撃者が行うことを防ぐために、アプリケーションは安全なヘッダを使用するべきです。これらは helmet のようなモジュールを使って簡単に設定することができます。

さもないと: 攻撃者がアプリケーションユーザーに対して直接攻撃を行い、甚大なセキュリティ脆弱性につながる可能性があります。

🔗 さらに読む: アプリケーションでセキュアなヘッダーを利用する



✔ 6.7. 定期的に、そして自動的に脆弱性のある依存関係を検査する

TL;DR: npm のエコシステムでは、プロジェクトにおいて多くの依存関係があることが一般的です。新たな脆弱性が発見された場合には、依存関係を常にチェックしておくべきです。npm auditsnyk のようなツールを利用して、脆弱性のある依存関係を追跡、監視し、パッチを適用しましょう。これらのツールを CI セットアップと統合することで、本番環境にデプロイされる前に脆弱性のある依存関係を発見することができるでしょう。

さもないと: 攻撃者がウェブフレームワークを特定して、全ての既知の脆弱性を突いてくる可能性があります。

🔗 さらに読む: 依存性のセキュリティ



✔ 6.8. パスワードの処理に Node.js の crypto ライブラリではなく Bcrypt を利用する

TL;DR: パスワードやシークレット(API キー)は、JavaScript の実装においてパフォーマンス面・セキュリティ面で優れた選択肢である bcrypt のようなセキュアなハッシュ+ソルト関数を利用して保存するべきです。

さもないと: セキュアな関数を使わずに永続化されたパスワードやシークレット情報は、ブルートフォース攻撃や辞書攻撃に弱く、結果として情報漏えいに繋がります。

🔗 さらに読む: Bcrypt を使用する



✔ 6.9. HTML や JS、CSS の出力をエスケープする

TL;DR: ブラウザに送信された信頼されていないデータは、ただ表示される代わりに実行される可能性があり、これは一般的にクロスサイトスクリプティング(XSS)攻撃と呼ばれています。これを軽減するには、データを、実行されるべきではない純粋なコンテンツとして明示的にマークする専用のライブラリを使用します(エンコーディング、エスケープなど)。

さもないと: 攻撃者は悪意のある JavaScript のコードを DB に保存し、それをそのまま脆弱なクライアントに送信する可能性があります。

🔗 さらに読む: 出力をエスケープする



✔ 6.10. 受信した JSON スキーマを検証する

TL;DR: 受信したリクエストの body ペイロードを検証し、期待する条件を満たすことを確認し、期待通りでない場合はすぐに失敗するようにしてください。各ルート内での面倒な検証コードの実装を避けるために、jsonschemajoi のような、軽量の JSON ベースの検証ライブラリを利用するかもしれません。

さもないと: あなたの寛大で寛容なアプローチは攻撃対象を大幅に拡大させ、攻撃者がアプリケーションをクラッシュさせるための組み合わせを見つけるまで、多くの入力を試してみるように促すことに繋がります。

🔗 さらに読む: 受信した JSON スキーマを検証する



✔ 6.11. JWT のブラックリスト化をサポートする

TL;DR: JSON Web Token を(例えば Passport.js などを用いて)利用する場合、デフォルトでは、発行されたトークンからのアクセスを無効にする仕組みはありません。悪意のあるユーザーのアクティビティを発見したとしても、そのユーザーが有効なトークンを持っている限り、システムへのアクセスを止めることはできません。各リクエストで検証される、信頼されていないトークンのブラックリストを実装することで、この問題を緩和することができます。

さもないと: 期限切れや、誤って配置されたトークンは、アプリケーションにアクセスしたり、トークンの所有者になりすますために、サードパーティによって悪意を持って利用される可能性があります。

🔗 さらに読む: JSON Web Token のブラックリスト



✔ 6.12. 認証に対するブルートフォース攻撃を阻止する

TL;DR: シンプルで強力なテクニックは、次の 2 つのメトリクスを用いて認証の試行回数を制限することです:

  1. 同じユーザー固有の ID/名前、そして IP アドレスからの連続失敗回数
  2. ある IP アドレスからの長い期間の失敗回数。例えば、1 日で 100 回失敗した IP アドレスをブロックする

さもないと: 攻撃者が、アプリケーションの特権アカウントへのアクセス権を得るために、無制限の自動化されたパスワード試行を行うことができます。

🔗 さらに読む: ログインレートリミット



✔ 6.13. 非 root ユーザとして Node.js を実行する

TL;DR: Node.js が無制限の権限を持った root ユーザとして実行されるという一般的なシナリオがあります。例えば、Docker コンテナにおけるデフォルトの挙動です。root ではないユーザを作成して Docker イメージに組み込む(例は以下にあります)か、"-u username" フラグを利用してコンテナを起動することで、非 root ユーザでプロセスを実行することが推奨されます。

さもないと: サーバ上でスクリプトを実行することに成功した攻撃者が、ローカルマシンにおける無制限の権限を獲得してしまいます(例:iptable を変更して、攻撃者のサーバに再ルーティングする)。

🔗 さらに読む: 非 root ユーザとして Node.js を実行する



✔ 6.14. リバースプロキシまたはミドルウェアを使用してペイロードのサイズを制限する

TL;DR: Body ペイロードが大きければ大きいほど、シングルスレッドでの処理が重くなります。これは、攻撃者にとっては、膨大なリクエストを送信(DoS/DDoS 攻撃)せずともサーバーをダウンさせることができる機会となります。エッジ(例:firewall、ELB)で受信するリクエストのボデサイズを制限する、もしくは express body parser を用いて小さいサイズのペイロードのみを受け付けることで、緩和してください。

さもないと: アプリケーションは大きなリクエストを処理しなければなくなり、他の重要な仕事を完遂させることができず、パフォーマンスへの影響や DDoS 攻撃に対する脆弱性につながります。

🔗 さらに読む: ペイロードサイズを制限する



✔ 6.15. JavaScript の eval 構文を避ける

TL;DR: eval は、ランタイムにおいてカスタム JavaScript コードの実行を許可しているため、有害です。これは単にパフォーマンス的な懸念だけではなく、ユーザーの入力を元にした悪意のある JavaScript コードのために、重大なセキュリティ的な懸念でもあります。その他の避けるべき言語仕様は、new Function コンストラクタです。setTimeoutsetInterval も動的な JavaScript コードに渡されるべきではありません。

さもないと: 悪意のある JavaScript コードが eval やその他のリアルタイムに評価する JavaScript の関数に渡されるテキストへたどり着き、そのページにおける JavaScript の完全な権限を獲得してしまいます。この脆弱性はしばしば XSS 攻撃として顕在化します。

🔗 さらに読む: JavaScript の eval 構文を避ける



✔ 6.16. 悪意のある RegEx がシングルスレッド実行をオーバーロードすることを防止する

TL;DR: 正規表現(RegEx)は便利ですが、JavaScript アプリケーション全体、特に Node.js プラットフォームに対して真の脅威となります。テキストのユーザー入力をマッチさせることは、処理に大量の CPU サイクルを必要とするかもしれません。RegEx の処理は、10 Word を検証する単一のリクエストが 6 秒間イベントループ全体をブロックし、CPU に 🔥 を点けるほどには非効率であるかもしれません。そのため、独自の RegExp パターンを記述する代わりに validator.js のようなサードパーティ検証パッケージを利用するか、脆弱な正規表現パターンを検出するために safe-regex を利用するようにしましょう。

さもないと: 下手な正規表現の記述は、イベントループを完全にブロックしてしまう正規表現 DoS 攻撃の影響を受ける可能性があります。例えば、人気のある moment パッケージでは、2017 年 11 月に悪意のある RegEx の使用による脆弱性が発見されています。

🔗 さらに読む: 悪質な RegEx を防止する



✔ 6.17. 変数を利用してモジュールを読み込むことを避ける

TL;DR: ユーザー入力が起因となって問題が生じる恐れがあるため、パラメータとして与えられたパスを用いて他のファイルを require/import しないようにしてください。この原則は、ユーザー入力に基づいた動的な変数を用いた、一般的なファイルアクセス(fs.readFile() など)やその他のセンシティブなリソースアクセスにも拡張することができます。Eslint-plugin-security linter はそのようなパターンを検知して、早期に警告を出すことができます。

さもないと: 悪意のあるユーザー入力は、既存のシステムファイルに、前にファイルシステムにアップロードされたファイルのような改変されたファイルを要求したり、既存のシステムファイルにアクセスするために利用されるパラメータを操作する可能性があります。

🔗 さらに読む: 安全なモジュール読み込み



✔ 6.18. サンドボックス内で安全でないコードを実行する

TL;DR: ランタイムにおいて与えられた外部のコード(プラグインなど)を実行するようなタスクを行うとき、独立していて、メインコードをプラグインから保護する任意の「サンドボックス」実行環境を使用してください。これは、専用のプロセス(cluster.fork() など)やサーバーレス環境、またはサンドボックスとして動作する専用の npm パッケージを使用することで実現できます。

さもないと: プラグインは、無限ループやメモリーオーバーロード、センシティブなプロセスの環境変数へのアクセスなど、あらゆる手段を通じて攻撃可能となります。

🔗 さらに読む: サンドボックス内で安全でないコードを実行する



✔ 6.19. 子プロセスで処理を行う場合は特別な注意を払う

TL;DR: 可能なら子プロセスの仕様を避け、それでも使用する必要がある場合には、入力を検証しサニタイズすることで、シェルインジェクション攻撃を軽減してください。属性の集合を持つ単一コマンドのみを実行し、シェルパラメータ拡張を許可しない child_process.execFile の使用を優先してください。

さもないと: 子プロセスを愚直に利用することは、サニタイズされていないシステムコマンドに渡される悪意のあるユーザー入力が原因となって、結果としてリモートからのコマンド実行、またはシェルインジェクション攻撃を受けることにつながります。

🔗 さらに読む: 子プロセスで処理を行う場合は注意する



✔ 6.20. エラーの詳細をクライアントから隠す

TL;DR: 統合されている express のエラーハンドラはデフォルトでエラーの詳細を隠します。しかし、(多くの人がベストプラクティスだと考えている)カスタムエラーオブジェクトを使用して独自のエラー処理ロジックを実装する可能性は大いにあります。その場合、クライアントに、機密なアプリケーション詳細を含む恐れのあるエラーオブジェクト全体を返さないようにしてください。

さもないと: 攻撃者によって悪用される可能性のある、サーバファイルのパス、使用中のサードパーティモジュール、アプリケーションのその他内部ワークフローなど、機密性の高いアプリケーションの詳細情報が、スタックトレース内に残された情報から漏洩する可能性があります。

🔗 さらに読む: エラーの詳細をクライアントから隠す



✔ 6.21. npm や Yarn に 2 要素認証(2FA)を設定する

TL;DR: 開発におけるどのステップも、MFA(多要素認証)で保護されるべきです。npm/Yarn は開発者のパスワードに触れることができる攻撃者にとって絶好の機会となります。開発者の認証情報を利用して、プロジェクトやサービスにおいて広くインストールされている攻撃者は悪意のあるコードを注入することができます。もしパブリックに公開されている場合は、ウェブ全体にまで及ぶかもしれません。npm において 2 要素認証を設定することで、攻撃者がパッケージコードを改ざんする可能性はほぼゼロになります。

さもないと: Have you heard about the eslint developer whose password was hijacked?(パスワードがハイジャックされた eslint 開発者の話を聞いたことがありますか?)



✔ 6.22. セッションミドルウェアの設定を変更する

TL;DR: それぞれの Web フレームワークや技術には、既知の弱点があります - 攻撃者に対してどの Web フレームワークを利用しているかを伝えることは、攻撃者にとって大きな助けになることです。セッションミドルウェアのデフォルトの設定を利用することは、X-Powered-By ヘッダー同様に、アプリケーションをモジュールやフレームワーク固有のハイジャック攻撃にさらす可能性があります。技術スタック(例:Node.js、express など)を識別したり、明らかにするものは極力隠すようにしてください。

さもないと: Cookie は安全でないコネクションを通じて送信される恐れがあり、攻撃者はセッション識別子を利用して背後にある Web アプリケーションフレームワークや、モジュール固有の脆弱性を特定する可能性があります。

🔗 さらに読む: クッキー(Cookie)とセッションの安全性



✔ 6.23. 明示的にプロセスがいつクラッシュすべきか設定することで、DoS 攻撃を回避する

TL;DR: エラーが処理されなかった場合、Node プロセスはクラッシュします。多くのベストプラクティスは、たとえエラーがキャッチされ処理されたとしても、exit することを推奨しています。例えば、Express は、非同期エラーが発生すると、ルートをキャッチ節でラップしない限りクラッシュします。これは、どんな入力がプロセスをクラッシュさせるのかを認識し、繰り返し同様のリクエストを送信する攻撃者にとって絶好の攻撃スポットを提供します。この問題に対する即席の対策はありませんが、いくつかのテクニックはペインを軽減することができます: 処理されていないエラーによってプロセスがクラッシュした場合は常に重大な警告を出す、入力を検証して無効なユーザー入力によるプロセスのクラッシュを回避する、すべてのルートをキャッチで囲み、(グローバルに発生した場合とは対象的に)リクエスト内でエラーが発生した際にクラッシュしないように考慮する、などです。

さもないと: これはあくまで経験に基づいた推測ですが、多くの Node.js アプリケーションを見たときに、すべての POST リクエストにおいて空の JSON ボディを渡そうとすると、一部のアプリケーションはクラッシュしてしまいます。その時点で、同じリクエストを繰り返し送信することによって簡単にアプリケーションを停止させることができます。



✔ 6.24. 安全でないリダイレクトを防ぐ

TL;DR: ユーザー入力を検証しないリダイレクトは、攻撃者がフィッシング詐欺をしたり、ユーザーの認証情報を盗んだり、その他の悪質なアクションを実行することを可能にします。

さもないと: もし攻撃者が、外部のユーザーから与えられた入力を検証していないことを発見した場合、特別に作成されたリンクをフォーラムやソーシャルメディア、その他のパブリックな場所に投稿してユーザーにクリックさせることで、この脆弱性を悪用する恐れがあります。

🔗 さらに読む: 安全でないリダイレクトを防ぐ



✔ 6.25. npm レジストリへのシークレットの公開を避ける

TL;DR: 誤ってシークレットをパブリック npm レジストリに公開してしまうリスクを回避するように、注意を払ってください。.npmignore ファイルを利用して、特定のファイルやフォルダをブラックリスト化したり、package.json 内の files 配列をホワイトリストとして利用することができます。

さもないと: プロジェクトの API キーやパスワード、その他のシークレットが公開され、その情報を目にしたすべての人に悪用されることで、結果として金銭的な損失、なりすまし、その他リスクに繋がってしまいます。

🔗 さらに読む: シークレットの公開を避ける


⬆ トップに戻る

7. Draft: パフォーマンスのプラクティス

Our contributors are working on this section. Would you like to join?



✔ 7.1. イベントループをブロックしない

TL;DR: CPU 集約的なタスクは、主にシングルスレッドのイベントループをブロックし、専用のスレッド、プロセス、またはコンテキストに基づいて別の技術にそれらをオフロードするため、避けてください。

さもないと: イベントループがブロックされると、Node.js は他のリクエストを処理することができなくなり、同時接続ユーザーの遅延を引き起こします。3000 人のユーザーがレスポンスを待っていて、コンテンツを提供する準備ができていたとしても、1 つのリクエストがサーバからの結果のディスパッチをブロックしていしまいます

🔗 さらに読む: イベントループをブロックしない




✔ 7.2. Lodash のようなユーザーランドのユーティリティよりも、ネイティブの JS メソッドを選ぶ

TL;DR: ネイティブメソッドよりも lodashunderscore のようなユーティリティライブラリを使う方が、不要な依存関係やパフォーマンスの低下につながるため、よりペナルティが大きいことがよくあります。 新しい ES 標準と一緒に新しい V8 エンジンが導入されたことで、ネイティブメソッドが改善され、ユーティリティライブラリよりも約 50% のパフォーマンスが向上したことを覚えておいてください。

さもないと: すでに利用可能なものを単純に使用できたり、いくつかのファイルと引き換えに、数行で処理することができるような、パフォーマンスの低いプロジェクトを維持しなければならないでしょう。

🔗 さらに読む: ユーザーランドなユーティリティよりもネイティブを使用する




⬆ トップに戻る

8. Docker のプラクティス

🏅 Bret Fisher氏からは、次のような実践を多く学ぶことができました。感謝します。



✔ 8.1 マルチステージビルドを使用して、より無駄のない、より安全な Docker イメージを構築する

TL;DR: マルチステージビルドを使用して、必要な本番環境のアーティファクトだけをコピーします。多くのビルド時の依存関係やファイルは、アプリケーションの実行には必要ありません。マルチステージビルドでは、これらのリソースをビルド中に使用することができ、ランタイム環境には必要なものだけが含まれています。マルチステージビルドは、過剰な負荷とセキュリティの脅威を取り除く簡単な方法です。

さもないと: イメージが大きくなるとビルドとリリースに時間がかかり、ビルド専用ツールには脆弱性が含まれている可能性があり、ビルド段階でのみ使用されるシークレット情報が漏洩する可能性があります。

マルチステージビルド用の Dockerfile の例

FROM node:14.4.0 AS build

COPY . .
RUN npm ci && npm run build


FROM node:slim-14.4.0

USER node
EXPOSE 8080

COPY --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/package-lock.json ./
RUN npm ci --production

CMD [ "node", "dist/app.js" ]

🔗 さらに読む: マルチステージビルドを使用する




✔ 8.2. 'node' コマンドを使用して、npm start 避けた Bootstrap

TL;DR: アプリの起動には CMD ['node','server.js'] を使用し、OS のシグナルをコードに渡さない npm スクリプトの使用は避けてください。これにより、子プロセス、シグナル処理、グレースフルシャットダウン、ゾンビプロセスの問題を防ぐことができます。

さもないと: シグナルが渡されない場合、あなたのコードはシャットダウンについて通知されることはありません。これがなければ、適切に閉じる機会を失い、現在のリクエストやデータを失う可能性があります。

さらに読む: node コマンドを使用して 、npm の起動を避けた Bootstrap コンテナ




✔ 8.3. レプリケーションとアップタイムの扱いを Docker ランタイムに任せる

TL;DR: Docker ランタイムオーケストレーター(Kubernetes など)を使用する場合は、中間プロセスマネージャやプロセスを複製するカスタムコード(PM2、Cluster モジュールなど)を使用せずに、Node.js プロセスを直接呼び出します。ランタイムプラットフォームは、配置の決定を行うためのデータ量と可視性が最も高く、必要とされるプロセスの数、それらをどのように分散させ、クラッシュが発生した場合にどうすればよいかを最もよく知っています。

さもないと: リソース不足でクラッシュし続けるコンテナは、プロセスマネージャによって無期限に再起動されてしまいます。Kubernetes がそれを認識していれば、別の余裕のあるインスタンスに移すことができます。

🔗 さらに読む: プロセスを再起動と複製を Docker オーケストレーターに任せる




✔ 8.4. .dockerignore を使用してシークレット情報の漏洩を防ぐ

TL;DR: 一般的な秘密ファイルや開発成果物をフィルタリングする .dockerignore ファイルを含めてください。そうすることで、秘密がイメージに漏れるのを防ぐことができるかもしれません。ボーナスとして、ビルド時間が大幅に短縮されます。また、すべてのファイルを再帰的にコピーするのではなく、何を Docker にコピーするかを明示的に選択するようにしてください。

さもないと: .env, .aws, .npmrc のような共通の個人秘密ファイルは、イメージにアクセスできる人全員に共有されます (例: Docker リポジトリ)。

🔗 さらに読む: .dockerignore を使用する




✔ 8.5. 本番前に依存関係をクリーンアップする

TL;DR: ビルドやテストのライフサイクルの中で Dev-Dependencies が必要になることもありますが、最終的には本番に出荷されるイメージは、開発の依存関係を最小限に抑え、クリーンなものにしなければなりません。このようにすることで、必要なコードのみが出荷され、潜在的な攻撃の量 (すなわち攻撃の表面) が最小限に抑えられることが保証されます。マルチステージビルドを使用する場合 (専用の箇条書きを参照)、最初にすべての依存関係をインストールし、最後に npm ci --production を実行することでこれを実現できます。

さもないと: 悪名高い npm のセキュリティ侵害の多くは開発パッケージ内で発見されました (例: eslint-scope

🔗 さらに読む: 開発依存性の除去




✔ 8.6. シャットダウンをスマートに、そしてグレースフルに

TL;DR: プロセス SIGTERM イベントを処理し、既存のすべての接続とリソースをクリーンアップします。これは進行中のリクエストに応答している間に行う必要があります。Docker 化されたランタイムでは、コンテナのシャットダウンは珍しいイベントではなく、むしろルーチン作業の一部として頻繁に発生します。これを実現するためには、いくつかの可動部分をオーケストレーションするための思慮深いコードが必要です: ロードバランサ、キープアライブ接続、HTTP サーバ、その他のリソースです。

さもないと: 即座に kill してしまうことは、何千人もの失望したユーザーに対応しないことを意味します。

🔗 さらに読む: グレースフルシャットダウン




✔ 8.7. Docker と v8 の両方を使ってメモリ制限を設定する

TL;DR: Docker と JavaScript のランタイムフラグの両方を使用して、常にメモリ制限を設定してください。Docker の制限は、コンテナ配置の思慮深い判断をするために必要であり、--v8 のフラグ max-old-space は、GC を時間通りにキックオフし、メモリ使用率の低下を防ぐために必要です。実質的には、v8 の古いスペースメモリをコンテナの制限値より少しだけ小さく設定します。

さもないと: docker の定義は、思慮深いスケーリングの決定を行い、他の市民を飢えさせないようにするために必要です。v8 の制限を定義しないと、コンテナリソースを十分に利用できません。 - 明示的な指示がないと、ホストリソースの ~50-60% を利用するときにクラッシュします。

🔗 さらに読む: Docker のみを使用してメモリ制限を設定する




✔ 8.8. 効率的なキャッシュを計画する

TL;DR: 正しく行えば、ほぼ瞬時にキャッシュから Docker イメージ全体をリビルドすることができます。あまり更新されない処理は Dockerfile の上の方に記述し、更新が多い処理(アプリケーションのコードなど)は下の方に記述するべきです。

さもないと: Docker のビルドが非常に長くなり、小さな変更をした場合でも多くのリソースを消費することになります。

🔗 さらに読む: キャッシュを活用してビルド時間を短縮する




✔ 8.9. latest タグは避け、明示的なイメージのリファレンスを使用する

TL;DR: 明示的なイメージダイジェスト、またはバージョンラベルを指定し、latest を参照しないようにしてください。開発者はしばしば、latest タグを指定することでリポジトリ内の最新のイメージが提供されると思い込みがちですが、そうではありません。ダイジェスト(digest)を利用することで、サービスのすべてのインスタンスが全く同じコードを実行していることが保証されます。

さらに、イメージタグを参照することは、決定論的インストールにおいてイメージタグを頼りにすることができないために、ベースイメージが変更される可能性があることを意味します。代わりに、決定論的なインストールが想定される場合には、SHA256 ダイジェストを使用して正確なイメージを参照することができます。

さもないと: 破壊的変更を含むベースイメージの新しいバージョンが本番環境にデプロイされ、意図しないアプリケーションの挙動を引き起こす可能性があります。

🔗 さらに読む: イメージタグを理解して「latest」タグを注意して使う




✔ 8.10. 小さな Docker ベースイメージを優先する

TL;DR: 大きなイメージは、脆弱性にさらされる可能性を高め、リソースの消費量を増加させます。Slim や Alpine Linux のような、スリムな Docker イメージを使うことで、この問題を軽減することができます。

さもないと: イメージのビルド、プッシュ、プルに時間を要し、未知の攻撃の因子が悪意のあるアクターによって使用され、より多くのリソースが消費されます。

🔗 さらに読む: 小さなイメージを優先する




✔ 8.11. ビルド時のシークレットをクリーンアウトし、引数にシークレットを含めることを避ける

TL;DR: Docker のビルド環境からシークレットが漏洩することを避けてください。Docker イメージは一般的に、 CI や本番環境ほどサニタイズされていないレジストリといった複数の環境で共有されます。典型例としては、通常 dockerfile に引数として渡される npm トークンがあります。このトークンは必要となったタイミング以降も残り続け、攻撃者がプライベート npm レジストリにアクセスすることを無期限に許可することになります。これは、シークレットを .npmrc のようなファイルにコピーしてマルチステージビルドを用いてそれを削除する(ビルド履歴も削除するべきであることに注意してください)か、トレースを残さない Docker build-kit のシークレット機能を利用することで回避することができます。

さもないと: CI と docker レジストリへのアクセス権限を持っている人は誰でも、おまけとして貴重な組織の情報にアクセスできてしまいます。

🔗 さらに読む: ビルド時のシークレットをクリーンアウトする




✔ 8.12. イメージをスキャンして多層な脆弱性をチェックする

TL;DR: コード依存関係の脆弱性をチェックすることに加えて、プロダクションで利用される最終的なイメージもスキャンするようにしてください。Docker イメージスキャナはコード依存関係だけでなく、OS のバイナリもチェックします。この E2E セキュリティスキャンはより多くの領域をカバーし、ビルド中に悪者が悪い因子を注入していないことを確認します。そのため、デプロイ前の最後のステップとしてこれを実行することをおすすめします。CI/CD プラグインも提供している、無料または商用のスキャナがいくつか存在します。

さもないと: コードは脆弱性から完全に脆弱性から解放されているかもしれませんが、アプリケーションで一般的に使用されている OS レベルのバイナリ(例:OpenSSL、TarBall)の脆弱性が原因となって、ハッキングされる可能性が依然としてあります。

🔗 さらに読む: プロダクションの前にイメージ全体をスキャンする




✔ 8.13 NODE_MODULE キャッシュをクリーンアップする

TL;DR: コンテナに依存関係をインストールした後は、ローカルのキャッシュを削除してください。今後のインストールを高速化することを目的として依存関係を複製しても、意味はありません - Docker イメージは不変です。一行のコードで、数十 MB(通常は画像サイズの 10~50%)を削ることができます。

さもないと: 使用されないファイルが原因で、サイズが 3 割増のイメージがプロダクションにデプロイされることになります。

🔗 さらに読む: NODE_MODULE キャッシュをクリーンアップする




✔ 8.14. 一般的な Docker のプラクティス

TL;DR: Node.js とは直接関係の無い、Docker に関するアドバイス集です - Node における実装は他の言語とあまり変わりません。「さらに読む」から読み進めてください。

🔗 さらに読む: 一般的な Docker のプラクティス




✔ 8.15. Dockerfile を lint する

TL;DR: Dockerfile を linting することは、ベストプラクティスとは異なってしまっている Dockerfile の問題点を特定するための重要なステップです。Docker 専用の linter を使って潜在的な欠落をチェックすることで、パフォーマンスとセキュリティの改善可能箇所を容易に特定することができ、無駄な時間を削り、またプロダクションコードにおけるセキュリティの問題から解放してくれます。

さもないと: Dockerfile の作者が誤って root を本番ユーザーにしてしまい、不明なソースリポジトリからの Docker イメージを使用してしまう、といったことが起こり得ます。これは、シンプルな litner を利用することで回避することができます。

🔗 さらに読む: Dockerfile を lint する




⬆ Return to top

マイルストーン

このガイドを維持し最新に保つために、私たちはコミュニティの助けを借りながらガイドラインとベストプラクティスを常に更新、改良しています。このプロジェクトに貢献したい場合は、私たちのマイルストーンをチェックしたり、ワーキンググループに参加することができます。


翻訳

すべての翻訳はコミュニティによって支えられています。完成済みの翻訳、進行中の翻訳、または新たな翻訳のいずれにおいても、サポートを受けられたら幸いです!

翻訳(完了済み)

翻訳(進行中)



ステアリングコミッティー

ステアリングコミッティーのメンバーをご紹介します。このプロジェクトのガイダンスと将来の方向性を提供するために協力してくださっている方々です。さらに、コミッティーの各メンバーは、GitHub プロジェクトで管理されているプロジェクトをリードしています。

Yoni Goldberg

アメリカ、ヨーロッパ、イスラエルにおいて、大規模な Node.js アプリケーション開発をサポートするコンサルタント。上記の多くのベストプラクティスは当初 goldbergyoni.com で公開されました。Yoni への連絡は @goldbergyoni または me@goldbergyoni.com までお願いします。


Bruno Scheufler

💻 フルスタック web エンジニア、Node.js & GraphQL の熱狂的なファン


Kyle Martin

ニュージーランドを拠点とするフルスタックデベロッパー & サイトリライアビリティエンジニア。Web アプリケーションセキュリティや、グローバルスケールで稼働する Node.js アプリケーションの構築に関心があります。


Kevyn Bruyere

Ops や自動化に関心のあるフルスタックデベロッパー。


ステアリングコミッティー・エメリティ

Sagir Khan

JavaScript とそのエコシステム(React、Node.js、TypeScript、GraphQL、MongoDB など、システムのあらゆるレイヤーで JS/JSON が関係するものであるなら何でも)の専門家。世界で最も認知されているブランドのために Web プラットフォームを利用してプロダクトを構築しています。Node.js ファウンデーションの個人メンバー。


コラボレーター

すべてのコラボレーターの方々に感謝いたします! 🙏

私たちのコラボレーターは、新たなベストプラクティスの提案やイシューの優先順位付け、プルリクエストのレビューなどその他多くのことを通じて、定期的にこのリポジトリに貢献してくださっているメンバーの方々です。多くの人々がより良い Node.js アプリケーションを構築できるように導く私たちをサポートすることにもし興味があるのであれば、貢献ガイドラインをお読み下さい 🎉

Ido Richter (Founder) Keith Holliday

コラボレーター・エメリティ

Refael Ackermann

貢献

もしオープンソースに貢献したいと思ったことがあるのなら、いまがチャンスです! 詳細は貢献ドキュメントを参照してください。

貢献メンバー ✨

このリポジトリに貢献してくれた素晴らしい方々に感謝いたします。

Kevin Rambaud
Kevin Rambaud

🖋
Michael Fine
Michael Fine

🖋
Shreya Dahal
Shreya Dahal

🖋
Matheus Cruz Rocha
Matheus Cruz Rocha

🖋
Yog Mehta
Yog Mehta

🖋
Kudakwashe Paradzayi
Kudakwashe Paradzayi

🖋
t1st3
t1st3

🖋
mulijordan1976
mulijordan1976

🖋
Matan Kushner
Matan Kushner

🖋
Fabio Hiroki
Fabio Hiroki

🖋
James Sumners
James Sumners

🖋
Dan Gamble
Dan Gamble

🖋
PJ Trainor
PJ Trainor

🖋
Remek Ambroziak
Remek Ambroziak

🖋
Yoni Jah
Yoni Jah

🖋
Misha Khokhlov
Misha Khokhlov

🖋
Evgeny Orekhov
Evgeny Orekhov

🖋
-
-

🖋
Isaac Halvorson
Isaac Halvorson

🖋
Vedran Karačić
Vedran Karačić

🖋
lallenlowe
lallenlowe

🖋
Nathan Wells
Nathan Wells

🖋
Paulo Reis
Paulo Reis

🖋
syzer
syzer

🖋
David Sancho
David Sancho

🖋
Robert Manolea
Robert Manolea

🖋
Xavier Ho
Xavier Ho

🖋
Aaron
Aaron

🖋
Jan Charles Maghirang Adona
Jan Charles Maghirang Adona

🖋
Allen
Allen

🖋
Leonardo Villela
Leonardo Villela

🖋
Michał Załęcki
Michał Załęcki

🖋
Chris Nicola
Chris Nicola

🖋
Alejandro Corredor
Alejandro Corredor

🖋
cwar
cwar

🖋
Yuwei
Yuwei

🖋
Utkarsh Bhatt
Utkarsh Bhatt

🖋
Duarte Mendes
Duarte Mendes

🖋
Jason Kim
Jason Kim

🖋
Mitja O.
Mitja O.

🖋
Sandro Miguel Marques
Sandro Miguel Marques

🖋
Gabe
Gabe

🖋
Ron Gross
Ron Gross

🖋
Valeri Karpov
Valeri Karpov

🖋
Sergio Bernal
Sergio Bernal

🖋
Nikola Telkedzhiev
Nikola Telkedzhiev

🖋
Vitor Godoy
Vitor Godoy

🖋
Manish Saraan
Manish Saraan

🖋
Sangbeom Han
Sangbeom Han

🖋
blackmatch
blackmatch

🖋
Joe Reeve
Joe Reeve

🖋
Ryan Busby
Ryan Busby

🖋
Iman Mohamadi
Iman Mohamadi

🖋
Sergii Paryzhskyi
Sergii Paryzhskyi

🖋
Kapil Patel
Kapil Patel

🖋
迷渡
迷渡

🖋
Hozefa
Hozefa

🖋
Ethan
Ethan

🖋
Sam
Sam

🖋
Arlind
Arlind

🖋
Teddy Toussaint
Teddy Toussaint

🖋
Lewis
Lewis

🖋
Gabriel Lidenor
Gabriel Lidenor

🖋
Roman
Roman

🖋
Francozeira
Francozeira

🖋
Invvard
Invvard

🖋
Rômulo Garofalo
Rômulo Garofalo

🖋
Tho Q Luong
Tho Q Luong

🖋
Burak Shen
Burak Shen

🖋
Martin Muzatko
Martin Muzatko

🖋
Jared Collier
Jared Collier

🖋
Hilton Meyer
Hilton Meyer

🖋
ChangJoo Park(박창주)
ChangJoo Park(박창주)

🖋
Masahiro Sakaguchi
Masahiro Sakaguchi

🖋
Keith Holliday
Keith Holliday

🖋
coreyc
coreyc

🖋
Maximilian Berkmann
Maximilian Berkmann

🖋
Douglas Mariano Valero
Douglas Mariano Valero

🖋
Marcelo Melo
Marcelo Melo

🖋
Mehmet Perk
Mehmet Perk

🖋
ryan ouyang
ryan ouyang

🖋
Shabeer
Shabeer

🖋
Eduard Kyvenko
Eduard Kyvenko

🖋
Deyvison Rocha
Deyvison Rocha

🖋
George Mamer
George Mamer

🖋
Konstantinos Leimonis
Konstantinos Leimonis

🖋
Oliver Lluberes
Oliver Lluberes

🌍
Tien Do
Tien Do

🖋
Ranvir Singh
Ranvir Singh

🖋
Vadim Nicolaev
Vadim Nicolaev

🖋 🌍
German Gamboa Gonzalez
German Gamboa Gonzalez

🖋
Hafez
Hafez

🖋
Chandiran
Chandiran

🖋
VinayaSathyanarayana
VinayaSathyanarayana

🖋
Kim Kern
Kim Kern

🖋
Kenneth Freitas
Kenneth Freitas

🖋
songe
songe

🖋
Kirill Shekhovtsov
Kirill Shekhovtsov

🖋
Serge
Serge

🖋
keyrwinz
keyrwinz

🖋
Dmitry Nikitenko
Dmitry Nikitenko

🖋
bushuai
bushuai

👀 🖋
Benjamin Gruenbaum
Benjamin Gruenbaum

🖋
Ezequiel
Ezequiel

🌍
Juan José Rodríguez
Juan José Rodríguez

🌍
Or Bin
Or Bin

🖋
Andreo Vieira
Andreo Vieira

🖋
Michael Solomon
Michael Solomon

🖋
Jimmy Callin
Jimmy Callin

🖋
Siddharth
Siddharth

🖋
Ryan Smith
Ryan Smith

🖋
Tom Boettger
Tom Boettger

🖋
Joaquín Ormaechea
Joaquín Ormaechea

🌍
dfrzuz
dfrzuz

🌍
Victor Homyakov
Victor Homyakov

🖋
Josh
Josh

🖋 🛡️
Alec Francis
Alec Francis

🖋
arjun6610
arjun6610

🖋
Jan Osch
Jan Osch

🖋
Thiago Rotondo Sampaio
Thiago Rotondo Sampaio

🌍
Alexsey
Alexsey

🖋
Luis A. Acurero
Luis A. Acurero

🌍
Lucas Romano
Lucas Romano

🌍
Denise Case
Denise Case

🖋
Nick Ribal
Nick Ribal

🖋 👀
0xflotus
0xflotus

🖋
Jonathan Chen
Jonathan Chen

🖋
Dilan Srilal
Dilan Srilal

🖋
vladthelittleone
vladthelittleone

🌍
Nik Osvalds
Nik Osvalds

🖋
Daniel Kiss
Daniel Kiss

📖
Forresst
Forresst

🖋
Jonathan Svenheden
Jonathan Svenheden

🖋
AustrisC
AustrisC

🖋
kyeongtae kim
kyeongtae kim

🌍
007
007

🖋
Ane Diaz de Tuesta
Ane Diaz de Tuesta

🌍 🖋
YukiOta
YukiOta

🌍
Frazer Smith
Frazer Smith

🖋
Raz Luvaton
Raz Luvaton

🖋
Yuta Azumi
Yuta Azumi

🖋
andrewjbarbour
andrewjbarbour

🖋
mr
mr

🖋
Aleksandar
Aleksandar

🖋
Owl
Owl

🖋
Yedidya Schwartz
Yedidya Schwartz

🖋 💡
ari
ari

🖋
Thomas König
Thomas König

🖋
Kalle Lämsä
Kalle Lämsä

🖋
Wyatt
Wyatt

🖋
KHADIR Tayeb
KHADIR Tayeb

🖋
Shankar Regmi
Shankar Regmi

🖋
Shubham
Shubham

🖋
Lucas Alves
Lucas Alves

🖋
Benjamin
Benjamin

🖋
Yeoh Joer
Yeoh Joer

🖋
Miigon
Miigon

🖋
Rostislav Bogorad
Rostislav Bogorad

🖋
Flouse
Flouse

🖋
Tarantini Pereira
Tarantini Pereira

🖋
Kazuki Matsuo
Kazuki Matsuo

🖋
Adam Smith
Adam Smith

🖋
Dohyeon Ko
Dohyeon Ko

🖋
Vladislav Legkov
Vladislav Legkov

🖋
Kerollos Magdy
Kerollos Magdy

🖋
Erez Lieberman
Erez Lieberman

🖋
Breno Macedo
Breno Macedo

🖋
Fernando Flores
Fernando Flores

🌍
Rafael Brito
Rafael Brito

🌍
Emiliano Peralta
Emiliano Peralta

🌍
Shin, SJ
Shin, SJ

🖋
Benjamin Forster
Benjamin Forster

🖋
Daniele Fedeli
Daniele Fedeli

🖋
djob195
djob195

🖋
antspk
antspk

🖋
정진영
정진영

🖋
kkk-cashwalk
kkk-cashwalk

🖋
apainintheneck
apainintheneck

🖋
Fajar Budhi Iswanda
Fajar Budhi Iswanda

🖋
이주호
이주호

🖋
Singh
Singh

🖋
Alex Dumitru
Alex Dumitru

🖋
Anton Lykhatskyi
Anton Lykhatskyi

🖋
sangwonlee
sangwonlee

🖋
Eugenio Berretta
Eugenio Berretta

🖋
soranakk
soranakk

🖋
고준영
고준영

🖋 💻
Guilherme Portella
Guilherme Portella

🖋
André Esser
André Esser

🖋
Scc
Scc

🌍
Mauro Accornero
Mauro Accornero

🖋