基本セットアップと起動
Expressアプリケーションの基本的な初期化とサーバー起動方法です。
// expressモジュールをインポート
const express = require('express');
// Expressアプリケーションを作成
const app = express();
// ポート番号を設定 (環境変数 PORT があればそれを使用、なければ3000)
const port = process.env.PORT || 3000;
// ルートパス ('/') へのGETリクエストに対するハンドラ
app.get('/', (req, res) => {
res.send('Hello World! 👋');
});
// 指定したポートでリクエストを待ち受ける
app.listen(port, () => {
console.log(`サーバーがポート ${port} で起動しました 🚀`);
console.log(`http://localhost:${port}`);
});
ルーティング (Routing)
特定のエンドポイント(URI)とHTTPメソッドに対するリクエストを処理する方法を定義します。
基本的なメソッド
主要なHTTPメソッドに対応する基本的なルーティング設定です。
// GETリクエスト
app.get('/users', (req, res) => {
// ユーザー一覧を取得して送信
res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
});
// POSTリクエスト
app.post('/users', (req, res) => {
// 新しいユーザーを作成 (リクエストボディが必要 - 後述のミドルウェア参照)
const newUser = req.body; // req.body を使うには body-parser 等のミドルウェアが必要
console.log('Creating user:', newUser);
res.status(201).send('User created successfully! ✨');
});
// PUTリクエスト (特定リソースの更新)
app.put('/users/:id', (req, res) => {
const userId = req.params.id;
const updatedData = req.body;
console.log(`Updating user ${userId}:`, updatedData);
res.send(`User ${userId} updated.`);
});
// DELETEリクエスト (特定リソースの削除)
app.delete('/users/:id', (req, res) => {
const userId = req.params.id;
console.log(`Deleting user ${userId}`);
res.send(`User ${userId} deleted.`);
});
// すべてのHTTPメソッドに対応
app.all('/secret', (req, res, next) => {
console.log('Accessing the secret section ...');
next(); // 次のハンドラへ処理を渡す
});
ルートパラメータ
URLパスの一部をパラメータとして抽出し、req.params
オブジェクトで利用します。
// 例: /users/123/books/456
app.get('/users/:userId/books/:bookId', (req, res) => {
const userId = req.params.userId;
const bookId = req.params.bookId;
res.send(`User ID: ${userId}, Book ID: ${bookId}`);
});
ヒント ルートパラメータには正規表現を使って制約をかけることも可能です。
// userId は数値のみ受け付ける
app.get('/users/:userId(\\d+)', (req, res) => {
res.send(`User ID (digits only): ${req.params.userId}`);
});
ルートハンドラ
単一のルートに対して複数のハンドラ関数を配列として指定できます。next()
を呼び出すことで次のハンドラに処理が移ります。
const handler1 = (req, res, next) => {
console.log('First handler executed.');
req.customData = 'Passed from handler1'; // リクエストオブジェクトにデータを追加
next();
};
const handler2 = (req, res) => {
console.log('Second handler executed.');
res.send(`Data from previous handler: ${req.customData}`);
};
app.get('/multi-handler', [handler1, handler2]);
app.route()
特定のルートパスに対するチェーナブルなルートハンドラを作成できます。同じパスに対する異なるHTTPメソッドの処理をまとめるのに便利です。
app.route('/items')
.get((req, res) => {
res.send('Get a list of items');
})
.post((req, res) => {
res.send('Add a new item');
});
Express Router
モジュール式のマウント可能なルートハンドラを作成します。アプリケーションが大きくなった場合にルート定義を分割・整理するのに役立ちます。
routes/users.js
(例):
const express = require('express');
const router = express.Router();
// このルーターのミドルウェア (このルーター内の全ルートに適用)
router.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// ルートパス ('/') は /users にマウントされる
router.get('/', (req, res) => {
res.send('Users home page');
});
// /users/about
router.get('/about', (req, res) => {
res.send('About users');
});
module.exports = router;
app.js
(メインファイル):
const express = require('express');
const app = express();
const usersRouter = require('./routes/users'); // ルーターをインポート
// '/users' パス以下を usersRouter で処理するようマウント
app.use('/users', usersRouter);
// ... (他の設定やルート)
app.listen(3000);
ミドルウェア (Middleware)
リクエストオブジェクト(req
)、レスポンスオブジェクト(res
)、およびアプリケーションのリクエスト/レスポンスサイクルにおける次のミドルウェア関数へのアクセス権を持つ関数です。ミドルウェアは主に以下の目的で使用されます。
- 任意のコードを実行する
- リクエストオブジェクトとレスポンスオブジェクトに変更を加える
- リクエスト/レスポンスサイクルを終了させる
- スタック内の次のミドルウェア関数を呼び出す (
next()
)
アプリケーションレベル・ミドルウェア
app.use()
または app.METHOD()
を使用して、アプリケーション全体または特定のHTTPメソッドにバインドします。
// 全てのリクエストに対して実行されるミドルウェア (パス指定なし)
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next(); // 次のミドルウェアまたはルートハンドラへ
});
// '/admin' で始まるパスへのリクエストにのみ適用されるミドルウェア
app.use('/admin', (req, res, next) => {
console.log('Admin area access attempt.');
// ここで認証チェックなどを行う
if (req.headers['x-admin-key'] === 'secret') {
next();
} else {
res.status(403).send('Forbidden 🚫');
}
});
// 特定のルート (GET /example) の前に実行されるミドルウェア
app.get('/example', (req, res, next) => {
console.log('Middleware for /example GET route.');
next();
}, (req, res) => {
res.send('Example route reached.');
});
ルーターレベル・ミドルウェア
express.Router()
インスタンスにバインドされます。特定のルーターが処理するリクエストにのみ適用されます。(前述のExpress Routerの例を参照)
エラー処理ミドルウェア
他のミドルウェア関数と異なり、4つの引数 (err
, req
, res
, next
) を持ちます。通常、他の app.use()
やルート呼び出しの最後に定義します。
// ルートハンドラ内でエラーが発生した場合
app.get('/error-prone', (req, res, next) => {
try {
// 何らかのエラーを引き起こす可能性のある処理
throw new Error('Something went wrong! 🔥');
} catch (err) {
next(err); // エラーをエラー処理ミドルウェアに渡す
}
});
// エラー処理ミドルウェア (4つの引数を取る)
app.use((err, req, res, next) => {
console.error('Error caught!:', err.stack); // エラーログを出力
res.status(500).send('Internal Server Error 😥');
});
⚠️ エラー処理ミドルウェアは、他のすべての app.use()
およびルート定義の後に定義する必要があります。
組み込みミドルウェア
Express にはいくつかの便利な組み込みミドルウェアがあります。
ミドルウェア | 説明 | 使用例 |
---|---|---|
express.json() |
受信リクエストのペイロードをJSONとして解析し、req.body で利用可能にします。body-parser に基づいています。 |
app.use(express.json()); |
express.urlencoded() |
受信リクエストのペイロードをURLエンコード形式 (key=value&key2=value2 ) として解析し、req.body で利用可能にします。extended オプションで解析ライブラリを選択できます (true : qs , false : querystring )。 |
app.use(express.urlencoded({ extended: true })); |
express.static() |
静的ファイル(HTML, CSS, JavaScript, 画像など)を提供します。指定されたディレクトリ内のファイルを直接配信します。 | app.use(express.static('public')); ( public ディレクトリ内のファイルにルートパスからアクセス可能) app.use('/static', express.static('assets')); ( assets ディレクトリ内のファイルに /static パスからアクセス可能) |
express.Router() |
モジュール式のルートハンドラを作成します。(前述のExpress Routerを参照) | const router = express.Router(); |
サードパーティ・ミドルウェア
npm でインストールして使用できる多くのサードパーティ製ミドルウェアがあります。
ミドルウェア例 | 説明 | インストール & 使用例 |
---|---|---|
cors |
Cross-Origin Resource Sharing (CORS) を有効にします。異なるオリジンからのリクエストを許可します。 | npm install cors const cors = require('cors'); app.use(cors()); // 全てのオリジンを許可 // 特定のオリジンのみ許可する場合 app.use(cors({ origin: 'https://example.com' })); |
helmet |
様々なHTTPヘッダーを設定することで、既知のWeb脆弱性からアプリケーションを保護します。 | npm install helmet const helmet = require('helmet'); app.use(helmet()); |
morgan |
HTTPリクエストロガーミドルウェア。リクエストの詳細をコンソールやファイルに出力します。 | npm install morgan const morgan = require('morgan'); app.use(morgan('dev')); // 'dev', 'combined', 'short' などのフォーマットを指定 |
compression |
レスポンスを圧縮 (gzipなど) して転送量を削減します。 | npm install compression const compression = require('compression'); app.use(compression()); |
cookie-parser |
リクエストヘッダーのCookieを解析し、req.cookies オブジェクトに格納します。 |
npm install cookie-parser const cookieParser = require('cookie-parser'); app.use(cookieParser()); |
express-session |
サーバーサイドセッション管理を提供します。 | npm install express-session const session = require('express-session'); app.use(session({ secret: 'your secret key', resave: false, saveUninitialized: true })); |
リクエストオブジェクト (Request Object – req)
HTTPリクエストを表し、リクエストクエリ文字列、パラメータ、ボディ、HTTPヘッダーなどのプロパティを持ちます。
プロパティ/メソッド | 説明 | 例 |
---|---|---|
req.params |
ルートパラメータを含むオブジェクト。例: /users/:id で /users/5 にアクセスした場合、req.params は { "id": "5" } 。 |
const userId = req.params.id; |
req.query |
URLクエリ文字列のパラメータを含むオブジェクト。例: /search?q=node&page=2 の場合、req.query は { "q": "node", "page": "2" } 。 |
const searchTerm = req.query.q; |
req.body |
リクエストボディに含まれるデータを含むオブジェクト。express.json() や express.urlencoded() などのボディ解析ミドルウェアが必要です。 |
const userData = req.body; |
req.headers |
受信したリクエストヘッダーを含むオブジェクト。ヘッダー名は大文字小文字を区別しません (例: req.headers['content-type'] )。 |
const contentType = req.headers['content-type']; const userAgent = req.get('User-Agent'); // ヘッダー取得の推奨メソッド |
req.method |
リクエストのHTTPメソッド (GET , POST など) を文字列で返します。 |
console.log(req.method); |
req.path |
リクエストパスのパス部分を文字列で返します。クエリ文字列は含まれません。 | console.log(req.path); // '/users/1' |
req.url |
リクエストURLのパスとクエリ文字列を含む文字列。 (例: /search?q=node ) |
console.log(req.url); |
req.originalUrl |
req.url に似ていますが、app.use() などでマウントされた元のURLを保持します。 |
// app.use('/admin', adminRouter); の中のルートでアクセスした場合 console.log(req.originalUrl); // '/admin/users' console.log(req.url); // '/users' |
req.ip |
リクエスト元のIPアドレスを文字列で返します。プロキシ経由の場合は設定が必要です (app.set('trust proxy', true) )。 |
console.log(req.ip); |
req.cookies |
cookie-parser ミドルウェアが使用されている場合に、送信されたクッキーを含むオブジェクト。 |
const sessionId = req.cookies.session_id; |
req.get(headerName) |
指定されたHTTPリクエストヘッダーフィールドの値を取得します。大文字小文字を区別しません。 | const cacheControl = req.get('Cache-Control'); |
req.accepts(types) |
リクエストの Accept ヘッダーに基づき、指定されたタイプが受け入れ可能かどうかをチェックします。 |
if (req.accepts('json')) { ... } if (req.accepts(['html', 'json'])) { ... } |
レスポンスオブジェクト (Response Object – res)
ExpressアプリがHTTPリクエストを受信したときにクライアントに送り返すHTTPレスポンスを表します。
メソッド | 説明 | 例 |
---|---|---|
res.send([body]) |
HTTPレスポンスを送信します。引数は Buffer オブジェクト、String 、オブジェクト、Boolean 、または Array が可能です。Content-Typeヘッダーを自動的に設定しようとします。 |
res.send('Hello!'); res.send({ message: 'Success' }); res.status(404).send('Not Found'); |
res.json([body]) |
JSONレスポンスを送信します。引数を JSON.stringify() で変換し、Content-Type ヘッダーを application/json に設定します。 |
res.json({ user: { id: 1, name: 'Alice' } }); res.status(201).json({ status: 'created' }); |
res.status(code) |
レスポンスのHTTPステータスコードを設定します。チェイン可能です。 | res.status(404).send('Resource not found.'); res.status(200).json({ data: 'ok' }); |
res.sendStatus(statusCode) |
ステータスコードを設定し、その文字列表現 (例: “Not Found”) をレスポンスボディとして送信します。 | res.sendStatus(403); // ボディに "Forbidden" を送信 |
res.render(view [, locals] [, callback]) |
テンプレートエンジンを使用してビューをレンダリングし、HTML文字列をレスポンスとして送信します。app.set('view engine', 'ejs') や app.set('views', './views') の設定が必要です。 |
// ejsテンプレートエンジンの場合 res.render('index', { title: 'Home Page', user: req.user }); |
res.redirect([status,] path) |
指定されたパスにリダイレクトします。デフォルトのステータスコードは 302 (Found) です。 | res.redirect('/login'); res.redirect(301, '/new-location'); // 永続的リダイレクト |
res.sendFile(path [, options] [, fn]) |
指定されたパスのファイルを転送します。ファイルタイプに基づいて Content-Type を設定します。パスは絶対パスか、root オプションで指定されたディレクトリからの相対パスである必要があります。 |
res.sendFile(path.join(__dirname, 'public/index.html')); res.sendFile('image.jpg', { root: './uploads' }); |
res.download(path [, filename] [, options] [, fn]) |
ファイルをダウンロードするようクライアントに促します。Content-Disposition ヘッダーを attachment に設定します。 |
res.download('/path/to/report.pdf'); res.download('/path/to/data.csv', 'user_data.csv'); // ダウンロード時のファイル名を指定 |
res.set(field [, value]) or res.header(field [, value]) |
レスポンスヘッダーを設定します。オブジェクトを渡して複数のヘッダーを一度に設定することもできます。 | res.set('Content-Type', 'text/plain'); res.set({ 'X-Custom-Header': 'value1', 'Cache-Control': 'no-cache' }); |
res.cookie(name, value [, options]) |
クッキーを設定します。value は文字列またはオブジェクト (JSONに変換されます) です。options でドメイン、パス、有効期限 (maxAge , expires )、httpOnly , secure などを設定できます。cookie-parser は不要です。 |
res.cookie('username', 'john_doe', { maxAge: 900000, httpOnly: true }); |
res.clearCookie(name [, options]) |
指定されたクッキーをクリアします。res.cookie と同じ options (特に path と domain ) を指定する必要があります。 |
res.clearCookie('username', { path: '/' }); |
res.append(field [, value]) |
指定された値を追加のヘッダーフィールドとして追加します。既存のヘッダーがあれば、値が追加されます。 | res.append('Link', [' |
res.location(path) |
レスポンスの Location HTTPヘッダーを設定します。リダイレクトと組み合わせて使うことが多いです。 |
res.location('/new-resource/123').sendStatus(201); |
res.end() |
レスポンスプロセスを終了します。主にデータを送信せずにレスポンスを終了する場合に使用します。res.send() や res.json() は内部でこれを呼び出します。 |
res.status(204).end(); // No Content レスポンス |
静的ファイルの提供
express.static
組み込みミドルウェアを使用して、HTML、CSS、クライアントサイドJavaScript、画像などの静的アセットを提供します。
// 'public' ディレクトリ内のファイルをルートパスから提供
// 例: public/css/style.css は http://localhost:3000/css/style.css でアクセス可能
app.use(express.static('public'));
// 'assets' ディレクトリ内のファイルを '/static' 仮想パスプレフィックスから提供
// 例: assets/js/main.js は http://localhost:3000/static/js/main.js でアクセス可能
app.use('/static', express.static('assets'));
// 複数の静的ディレクトリを提供 (最初に見つかったものが使用される)
app.use(express.static('public'));
app.use(express.static('files'));
// express.static のオプション
// const path = require('path');
// app.use('/static', express.static(path.join(__dirname, 'public'), {
// dotfiles: 'ignore', // ドットファイル (.env など) の扱い ('allow', 'deny', 'ignore')
// etag: true, // ETag生成を有効にするか (デフォルト: true)
// extensions: ['html', 'htm'], // ファイル拡張子がない場合に試す拡張子
// index: false, // ディレクトリインデックスファイル (index.html) の提供 (デフォルト: 'index.html')
// maxAge: '1d', // Cache-Control の max-age (ミリ秒または ms 形式の文字列)
// redirect: true, // パスがディレクトリの場合に末尾スラッシュへリダイレクトするか
// setHeaders: function (res, path, stat) { // カスタムヘッダーを設定する関数
// res.set('x-timestamp', Date.now());
// }
// }));
💡 静的ファイルを提供するディレクトリ (例: ‘public’) は、Nodeプロセスが実行されるディレクトリからの相対パスです。
テンプレートエンジン
サーバーサイドで動的にHTMLを生成するためにテンプレートエンジンを使用します。Expressは多くのテンプレートエンジンと連携できます (EJS, Pug/Jade, Handlebarsなど)。
セットアップ (EJSの例)
// 1. テンプレートエンジンをインストール
// npm install ejs
// 2. Expressにテンプレートエンジンを設定
app.set('view engine', 'ejs');
// 3. ビューファイルが格納されているディレクトリを設定 (デフォルトは 'views')
app.set('views', './views'); // プロジェクトルートに 'views' ディレクトリがあると仮定
ビューのレンダリング
ルートハンドラ内で res.render()
を使用してビューをレンダリングし、データを渡します。
app.get('/profile', (req, res) => {
const user = {
name: '田中 太郎',
email: 'taro.tanaka@example.com',
isAdmin: false,
hobbies: ['読書', '旅行', 'プログラミング']
};
// 'views/profile.ejs' をレンダリングし、user オブジェクトを渡す
res.render('profile', { user: user, title: 'プロフィール' });
});
ビューファイル (例: views/profile.ejs)
渡されたデータを使用してHTMLを動的に生成します。
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<!-- ここに Bulma CSS などのスタイルシートをリンク -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title"><%= user.name %> のプロフィール</h1>
<p class="subtitle">Email: <%= user.email %></p>
<% if (user.isAdmin) { %>
<span class="tag is-danger">管理者</span>
<% } else { %>
<span class="tag is-info">一般ユーザー</span>
<% } %>
<h2 class="title is-4 mt-4">趣味:</h2>
<ul>
<% user.hobbies.forEach(function(hobby){ %>
<li><%= hobby %></li>
<% }); %>
</ul>
</div>
</section>
</body>
</html>
EJS Documentation Pug Documentation Handlebars Documentation
セキュリティに関する考慮事項 (基本)
Express アプリケーションを保護するための基本的なベストプラクティスです。
- Helmetの使用:
helmet
ミドルウェアを使用して、一般的なセキュリティヘッダー (X-Frame-Options, Strict-Transport-Security など) を設定します。 - 依存関係の管理:
npm audit
を定期的に実行し、依存関係の脆弱性をチェック・修正します。 - HTTPSの使用: 本番環境では必ずHTTPSを使用し、通信を暗号化します。リバースプロキシ (Nginx, Caddy など) を使用してSSL/TLS終端を行うのが一般的です。
- 入力値の検証とサニタイズ: ユーザーからの入力 (
req.body
,req.query
,req.params
) は常に検証し、不正なデータや悪意のあるスクリプト (XSS攻撃) を防ぐためにサニタイズします。express-validator
などのライブラリが役立ちます。 - レート制限: ブルートフォース攻撃やDoS攻撃を防ぐために、APIエンドポイントへのリクエスト数を制限します。
express-rate-limit
などのミドルウェアを使用します。 - エラー処理の強化: 詳細なエラー情報 (スタックトレースなど) を本番環境のクライアントに送信しないようにします。エラー処理ミドルウェアで適切に処理し、ログに記録します。
- セッション管理の保護:
express-session
を使用する場合、セキュアなクッキー属性 (secure: true
– HTTPS接続時のみ送信,httpOnly: true
– クライアントサイドJSからのアクセス禁止) を設定し、強力なsecret
を使用します。 - CORSの適切な設定:
cors
ミドルウェアを使用する場合、許可するオリジンを可能な限り具体的に指定します (*
の使用は避ける)。 - SQLインジェクション対策: データベースとやり取りする際は、ORM (Sequelize, Mongooseなど) やパラメータ化されたクエリを使用し、SQLインジェクションを防ぎます。
// 基本的なセキュリティミドルウェアの適用例
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
app.use(helmet()); // セキュリティヘッダーを設定
// CORS設定 (例: 特定のオリジンのみ許可)
const corsOptions = {
origin: 'https://your-frontend-app.com',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
};
app.use(cors(corsOptions));
// レート制限 (例: 15分間に100リクエストまで)
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
});
app.use(limiter); // 全てのリクエストにレート制限を適用
// ... 他のミドルウェアやルート設定
// 本番環境向けのエラーハンドラ (スタックトレースを漏洩させない)
app.use((err, req, res, next) => {
console.error(err.stack); // サーバーログには記録
res.status(err.status || 500).json({
error: {
message: process.env.NODE_ENV === 'production' ? 'Internal Server Error' : err.message,
// 本番環境では詳細なエラー情報を返さない
}
});
});
コメント