2025.03.12追記
mergeParamsというオプションを使って直接アクセスできるみたいです。
import { Router } from "express";
const router=Router({mergeParams:true});
はじめに
Expressでは、
import express from 'express';
const app=express();
app.use('/users/:id',(req,res,next)=>{
//...処理
})
のようにしてルートパラメータを設定することができ、req.params.id
として取得できます。
そして、ルートパラメータは複数設定することもできます。
app.use('/users/:id/todos/:todoid',(req,res,next)=>{
//...処理
})
ルーティングがそこまで複雑でなければ同じルータに書いてしまえばよいですが、複雑になってくると下位のルータを使うなどしてファイルを分割したくなります。
しかし、app.use('/users/:id',router)
だとrouter
で呼ばれるミドルウェアはidにアクセスできません。
import express,{ Router } from 'express';
const app=express();
const router=Router();
const testHandler=(req,res)=>{
const {id}=req.params;
if(!id){
res.send("idにアクセスできません");
return;
}
res.send("id:"+id);
}
router.use('/',testHandler);
//ここのtestHandlerはreq.params.idにアクセスできる
app.use('/test/:id',testHandler);
//routerで呼ばれるtestHandlerはreq.params.idにアクセスできない
app.use('/test2/:id',router);
app.listen(8000,()=>{
console.log("App is Running at http://localhost:8000.");
})
app.param(name,callback)
そこで、app.param
やrouter.param
を使うことで下位のルータからもアクセスできるようにすることが可能です。
app.param(name,callback)
は名前がname
に一致するルートパラメータに対してコールバック関数を設定するメソッドです。
下の例では、名前がidであるルートパラメータをもつパスに対してリクエストがあったときにreq.id=id;
が実行される、ということになります。
...
router.param("id",(req,res,next,id)=>{
//reqオブジェクトにルートパラメータを格納
req.id=id;
next();
})
...
TypeScriptの場合
TypeScriptだと、プロパティ 'id' は型 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' に存在しません。
のようなエラーが出ると思います。
この場合は、index.d.ts
ファイルに
declare module "express-serve-static-core" {
interface Request {
id:string;
}
}
を追加するとエラーが出なくなります。(ファイル名は拡張子がd.ts
であればなんでもいいです)
続き
testHandlerを下のように書き換えると、app.use('/test2/:id',router)
のミドルウェアからもidにアクセスできます。(/users/:id/hoge
など同じパラメータ名をもつパスがあるとややこしくなりそうですが...)
const testHandler=(req,res)=>{
const {id}=req.params;
if(!id){
- res.send("idにアクセスできません");
+ //route.paramで格納したidを取り出す
+ _id=req.id;
+ res.send("id:"+_id);
return;
}
res.send("id:"+id);
}
終わりに
Google検索してもGitHub Copilotに聞いても出てこなかったので苦労しました。
公式ドキュメントはやはり偉大。
あと英語読めると便利。
参考