プログラミング · Express · Node.js
Expressメソッドのタイピング練習
Express 5 と TypeScript のメソッド35個を、初級・中級・上級の3つのリストで打って覚えます。app.get でのルート定義、app.listen でのサーバー起動、req.query の扱いなどを練習できます。
3リスト・35語
無料・ログインなしで練習できます。押すとこのパックをフォローし、すぐに練習が始まります。
収録している言葉
Expressメソッド(初級)
| 打つ言葉 | 意味・補足 |
|---|---|
| import express from 'express'; const app = express(); | Express 5 / TypeScript:対象環境(Node.jsとExpress 5、@types/express等の型・TS実行設定が必要。各例は独立した断片) |
| app.get('/health', (_req, res) => { res.json({ ok: true }) }); | app.get:GETルート |
| app.post('/users', (_req, res) => { res.status(201).json({ id: 1 }) }); | app.post / put / patch / delete:HTTPメソッド別ルート(例はDB保存しない。更新・削除時は入力検証・認証認可) |
| app.use(express.json({ limit: '100kb' })); | express.json:JSON解析ミドルウェア(ルートより前に登録。未検証入力) |
| app.use(express.urlencoded({ extended: false, limit: '100kb' })); | express.urlencoded:フォーム解析(multipartファイル送信は別ライブラリ等) |
| app.delete('/sample', (_req, res) => { res.status(204).end() }); | res.sendStatus / end:定型・本文なし応答(実削除するなら認可・DB処理を追加) |
| app.post('/done', (_req, res) => { res.redirect(303, '/success') }); | res.redirect:転送(外部入力URLをそのまま使わない。自動returnではない) |
| app.use('/static', express.static('public')); | express.static:静的配信(公開ディレクトリに秘密ファイルを置かない。相対パスはcwd依存) |
| import helmet from 'helmet'; app.use(helmet()); | helmet:セキュリティヘッダー(別パッケージ。CSP等はアプリに合わせて調整) |
| npm install express | 実行:パッケージ・起動(ターミナル。型検査はtsc等で別途) |
| npm install -D typescript tsx @types/express @types/node | 実行:パッケージ・起動(ターミナル。型検査はtsc等で別途) |
| npx tsx src/index.ts | 実行:パッケージ・起動(ターミナル。型検査はtsc等で別途) |
Expressメソッド(中級)
| 打つ言葉 | 意味・補足 |
|---|---|
| const server = app.listen(3000, () => console.log('http://localhost:3000')); | app.listen:HTTPサーバー起動(環境のPORTや終了処理は用途に合わせる) |
| app.get('/users/:id', (req, res) => { res.json({ id: req.params.id }) }); | :id / req.params:パス引数(文字列として取得。数値等は検証・変換) |
| app.route('/items').get((_req, res) => { res.json([]) }).post((_req, res) => { res.sendStatus(201) }); | app.route:同じパスの処理を連結 |
| app.get('/files/*path', (req, res) => { res.json({ path: req.params.path }) }); | 名前付きワイルドカード:Express 5のパス構文(Express 4の無名*とは異なる。ルート自体を含める場合/{*path}等) |
| app.get('/sample', (_req, res) => { res.status(200).json({ name: 'Pomo' }) }); | res.status / json:ステータスとJSON |
| app.get('/text', (_req, res) => { res.type('text/plain').send('Hello') }); | res.send / type:本文とContent-Type(HTMLへ未エスケープ入力を埋め込まない) |
| app.use((req, res, next) => { console.log(req.method, req.path); next() }); | next:次の処理へ(応答するかnextする。両方を行わない) |
| app.get('/async', async (_req, res) => { const value = await Promise.resolve(42); res.json({ value }); }); | asyncハンドラー:非同期処理と例外伝播(Express 5は返されたPromiseのrejectをエラー処理へ渡す。別コールバック等は別途扱う) |
| app.get('/fail', (_req, _res, next) => { next(new Error('failure')) }); | next(err):エラーを伝える(エラー処理へ移行) |
| app.disable('x-powered-by'); /* 構成を確認してから: */ /* app.set('trust proxy',1); */ | app.disable / trust proxy:ヘッダー・Proxy設定(trust proxyの誤設定はIP・HTTPS判定等を偽装可能にする) |
| import cors from 'cors'; app.use(cors({ origin: 'https://app.example.com' })); | cors:CORSミドルウェア(corsと型を追加導入。CORSは認証やサーバー間通信の制限ではない) |
| const server = app.listen(3000); process.on('SIGTERM', () => { server.close(() => process.exit(0)) }); | 終了時のserver.close:接続を閉じる(タイムアウト・DB切断等を追加。例は独立) |
Expressメソッド(上級)
| 打つ言葉 | 意味・補足 |
|---|---|
| app.get('/search', (req, res) => { const q = req.query.q; if (typeof q !== 'string') { res.status(400).json({ error: 'q required' }); return; } res.json({ q }); }); | req.query:クエリ文字列(値の型・長さを検証。文字列とは限らない) |
| const router = express.Router(); router.get('/', (_req, res) => { res.json([]) }); app.use('/users', router); | Router / app.use:サブルートを分離(登録順が重要) |
| app.post('/users', (req, res) => { const name = req.body?.name; if (typeof name !== 'string' || !name.trim()) { res.status(400).json({ error: 'name required' }); return; } res.status(201).json({ name }); }); | req.body:本文を検証して利用(先に適切なbody parser。型注釈は実行時検証ではない) |
| app.get('/header', (req, res) => { res.set('X-App', 'sample'); res.json({ accept: req.get('Accept') }) }); | res.set / req.get:ヘッダー設定・取得(リクエストヘッダーを無条件で信頼しない) |
| app.post('/theme', (_req, res) => { res.cookie('theme', 'dark', { httpOnly: true, sameSite: 'lax', secure: process.env.NODE_ENV === 'production', path: '/' }); res.json({ ok: true }); }); | res.cookie / clearCookie:Cookie設定(認証用Cookieでは期限・CSRF対策等も設計。req.cookiesは追加parser等が必要) |
| import path from 'node:path'; app.get('/manual', (_req, res) => { res.sendFile(path.resolve('public/manual.pdf')) }); | res.sendFile:ファイル送信(ファイルが必要。未検証パス入力を連結しない) |
| app.use(express.json()); app.get('/health', (_req, res) => { res.json({ ok: true }) }); app.use((_req, res) => { res.status(404).json({ error: 'not found' }) }); | 処理の順序:共通前処理を先に登録(404ハンドラーはルートの後) |
| import { randomUUID } from 'node:crypto'; app.use((_req, res, next) => { res.locals.requestId = randomUUID(); next() }); | res.locals:同一リクエスト内の値(ログへ秘密を出さない) |
| import type { RequestHandler } from 'express'; const validateId: RequestHandler = (req, res, next) => { if (!/^\d+$/.test(req.params.id)) { res.status(400).json({ error: 'invalid id' }); return; } next(); }; app.get('/users/:id', validateId, (req, res) => { res.json({ id: req.params.id }) }); | 個別ルートの事前処理:ルート単位で検証(認証認可は別のミドルウェアやサービスで実施) |
| import type { ErrorRequestHandler } from 'express'; const onError: ErrorRequestHandler = (err, req, res, next) => { if (res.headersSent) { next(err); return; } console.error(err); res.status(500).json({ error: 'internal error' }); }; app.use(onError); | 4引数エラーハンドラー:失敗応答(ルート等の後に登録。スタック・機密情報を返さない) |
| import request from 'supertest'; const response = await request(app).get('/health'); if (response.status !== 200) throw new Error('unexpected status'); | Supertest:HTTPテスト(別パッケージ・テスト環境。appとlistenを分けると検証しやすい) |