本文へスキップ
Typle
ログイン状態を確認中

プログラミング · 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を分けると検証しやすい)

ほかの公式パックを見る