Express.jsネストされたルーターで休む


136

次のようなRESTエンドポイントが必要だとします。

/user/
/user/user_id 

/user/user_id/items/
/user/user_id/items/item_id

それぞれのCRUDは理にかなっています。たとえば、/ user POSTは新しいユーザーを作成し、GETはすべてのユーザーをフェッチします。/ user / user_id GETは、その1人のユーザーのみをフェッチします。

アイテムはユーザー固有なので、特定のユーザーであるuser_idの下に配置します。

次に、エクスプレスルーティングをモジュール化するために、いくつかのルーターインスタンスを作成しました。ユーザー用のルーターとアイテム用のルーターがあります。

var userRouter = require('express').Router();
userRouter.route('/')
  .get(function() {})
  .post(function() {})
userRouter.route('/:user_id')
  .get(function() {})

var itemRouter = require('express').Router();
itemRouter.route('/')
  .get(function() {})
  .post(function() {})
itemRouter.route('/:item_id')
  .get(function() {})

app.use('/users', userRouter);

// Now how to add the next router?
// app.use('/users/', itemRouter);

URL to itemは、のURL階層の子孫ですuser/usersでは、userRouterへのURLを取得するにはどうすれば/user/*user_id*/items/よいですか、itemRouterへのより具体的なルートを取得するにはどうすればよいですか。また、可能であれば、user_idにもitemRouterからアクセスできるようにしたいと考えています。


これを解決するためにExpressを使用することについてはすでに素晴らしい答えです。ただし、ループバック(Expressで構築)を使用してSwaggerベースのAPIを実装し、モデル間の関係を追加して、要求されたようにCRUDを実行することはできます。良いことは、最初の学習曲線の後で、組み立て方がはるかに速いことです。loopback.io
マイクS.

回答:


278

ルーターを他のルーターのミドルウェアとして接続することにより、ルーターをネストできます。params

親ルーターから{mergeParams: true}にアクセスする場合は、子ルーターに渡す必要がありparamsます。

mergeParamsExpress4.5.0で導入されました(2014年7月5日)

この例では、ルートににitemRouter接続さuserRouter/:userId/itemsます

これにより、次のルートが可能になります。

GET /user-> hello user
GET /user/5-> hello user 5
GET /user/5/items-> hello items from user 5
GET /user/5/items/6->hello item 6 from user 5

var express = require('express');
var app = express();

var userRouter = express.Router();
// you need to set mergeParams: true on the router,
// if you want to access params from the parent router
var itemRouter = express.Router({mergeParams: true});

// you can nest routers by attaching them as middleware:
userRouter.use('/:userId/items', itemRouter);

userRouter.route('/')
    .get(function (req, res) {
        res.status(200)
            .send('hello users');
    });

userRouter.route('/:userId')
    .get(function (req, res) {
        res.status(200)
            .send('hello user ' + req.params.userId);
    });

itemRouter.route('/')
    .get(function (req, res) {
        res.status(200)
            .send('hello items from user ' + req.params.userId);
    });

itemRouter.route('/:itemId')
    .get(function (req, res) {
        res.status(200)
            .send('hello item ' + req.params.itemId + ' from user ' + req.params.userId);
    });

app.use('/user', userRouter);

app.listen(3003);

3
答えてくれてありがとう。ここで使用するルーターは、Jordoniasが共有するルーターよりも明示的にネストされています。しかし、フードの下でも同じように機能しますか?包括性に対する報奨金を付与したいと思いますが、数時間後までそれを行うことはできません。
ハギー、2014

答えてくれてありがとう。子ルートから親ルートのクエリパラメータを取得する同様の方法はありますか?
くつろげる'09

1
クエリパラメータが特定のルートに関連付けられていないため、どのルートでも利用できない場合は驚きます...
Willem D'Haeseleer

非常に徹底した答え!1つの質問:ユーザールーターとアイテムルーター間のカプセル化と知識の分離のために、サブルーターにパラメーターが必要であることを指定する宣言的な方法はありますか?言い換えると、アイテムルーターがユーザーIDが渡されることを期待していることをアイテムルーターが通知するように、登録またはアクセス呼び出しを明示的に書き込む方法はありますか?例の状況では、アイテムルーターが別のファイルに完全に含まれています。構造的には、呼び出しに入るまでユーザーを必要とすることは明らかではなく、ユーザールーターでユーザーIDを渡すことだけが明らかです
yo.ian.g

これは、ルーターの「標準的な」使用法よりも読みやすくありません。コードを表示するときにネストを視覚化する方法を探しています。
DrewInTheMountains

127

管理可能なネストされたルート...

Express 4で非常に扱いやすい方法でネストされたルートを実行する具体的な例が必要でした。これが「エクスプレスでネストされたルート」の上位の検索結果でした。たとえば、分割する必要がある多くのルートを持つAPIがあります。

./index.js:

var app = require('express')();

// anything beginning with "/api" will go into this
app.use('/api', require('./routes/api'));

app.listen(3000);

./routes/api/index.js:

var router = require('express').Router();

// split up route handling
router.use('/products', require('./products'));
router.use('/categories', require('./categories'));
// etc.

module.exports = router;

./routes/api/products.js:

var router = require('express').Router();

// api/products
router.get('/', function(req, res) {
  res.json({ products: [] });
});

// api/products/:id
router.get('/:id', function(req, res) {
  res.json({ id: req.params.id });
});

module.exports = router;

フォルダー構造でのネストの例

「フォルダ構造のネスト」に関するコメントに気付きました。これは暗示されていますが明らかではないため、以下のセクションを追加しました。ルートのネストされたフォルダ構造の具体例を次に示します

index.js
/api
  index.js
  /admin
    index.js
    /users
      index.js
      list.js
    /permissions
      index.js
      list.js

これは、ノードの動作のより一般的な例です。「index.js」をディレクトリのデフォルトのWebページでの「index.html」の動作と同様に使用すると、エントリポイントをコードに変更せずに、再帰に基づいて組織を簡単に拡張できます。「index.js」はrequireを使用するときにアクセスされるデフォルトのドキュメントですディレクトリでを。

index.jsの内容

const express = require('express');
const router = express.Router();
router.use('/api', require('./api'));
module.exports = router;

/api/index.jsの内容

const express = require('express');
const router = express.Router();
router.use('/admin', require('./admin'));
module.exports = router;

/api/admin/index.jsの内容

const express = require('express');
const router = express.Router();
router.use('/users', require('./users'));
router.use('/permissions', require('./permissions'));
module.exports = router;

/api/admin/users/index.jsの内容

const express = require('express');
const router = express.Router();
router.get('/', require('./list'));
module.exports = router;

ここにはいくつかのDRY問題がある可能性がありますが、懸念のカプセル化には適しています。

ちなみに、最近私はactionheroに入り、ソケットとタスクを備えたフル機能を備えていることがわかりました。それは、真のフレームワークオールインワンがRESTパラダイムを一変させるようなものです。エクスプレスでネイキッドになることをチェックするべきでしょう。


11
これがルートをどのように分割するかはわかりますが、ネストに対処するにはどうすればよいですか?
1252748

完璧...そして理にかなっています。これはスケーラブルなオプションです。opがバージョン管理を実装する方法(v1、v2など)を知りたいのですが
Kermit_ice_tea

8
var userRouter = require('express').Router();
var itemRouter = require('express').Router({ mergeParams: true }); 

userRouter.route('/')
  .get(function(req, res) {})
  .post(function(req, res) {})
userRouter.route('/:user_id')
  .get(function() {})

itemRouter.route('/')
  .get(function(req, res) {})
  .post(function(req, res) {})
itemRouter.route('/:item_id')
  .get(function(req, res) {
    return res.send(req.params);
  });

app.use('/user/', userRouter);
app.use('/user/:user_id/item', itemRouter);

質問の2番目の部分の鍵は、mergeParamsオプションの使用です

var itemRouter = require('express').Router({ mergeParams: true }); 

/user/jordan/item/catI REPONSEを取得します:

{"user_id":"jordan","item_id":"cat"}

涼しい。あなたの方法とウィレムの方法の両方が、私が欲しかったものに対して機能します。私は彼の包括性をチェックしますが、同様にあなたをマークアップします。どうもありがとう。あなたのメソッドはネストされていないように見えますが、私が望んでいたことをほとんど実行します。ありがとう。
ハギー、2014

ここでは、mergeParamsオプションが重要です。
MrE、2016

2

@Jason Sebringソリューションを使用し、Typescriptに適応します。

server.ts

import Routes from './api/routes';
app.use('/api/', Routes);

/api/routes/index.ts

import { Router } from 'express';
import HomeRoutes from './home';

const router = Router();

router.use('/', HomeRoutes);
// add other routes...

export default router;

/api/routes/home.ts

import { Request, Response, Router } from 'express';

const router = Router();

router.get('/', (req: Request, res: Response) => {
  res.json({
    message: 'Welcome to API',
  });
});

export default router;

提供してもらえます./api/routesか?
ジュリアン

1
@ジュリアン:ファイルの場所を修正しました。./api/routesには2つのファイルindex.tsとがありhome.tsます。最初のものはによって使用されserver.tsます。それがあなたを助けることを願っています。
ピエールRA

0
try to add  { mergeParams: true } look to simple example  which it middlware use it in controller file getUser at the same for  postUser
    const userRouter = require("express").Router({ mergeParams: true });
    export default ()=>{
    userRouter
      .route("/")
      .get(getUser)
      .post(postUser);
    userRouter.route("/:user_id").get(function () {});
    
    
    }

-9

必要なルーターは1つだけで、次のように使用します。

router.get('/users');
router.get('/users/:user_id');

router.get('/users/:user_id/items');
router.get('/users/:user_id/items/:item_id');

app.use('api/v1', router);

はい。ただし、アイテムとユーザーの間のロジックを分離したいので、それらを分離することを好みます。それが可能かどうかわかりません。
ハギー、2014

@huggie itemsusers正しいものです、なぜそれを分離する必要があるのですか?必要に応じて、同じルーターを使用して別のファイルでそれらを定義できます。
eguneys 2014

ユーザーに属していますが、ユーザーに影響を与えずに簡単にプラグインまたはプラグアウトできるようにしたいと考えています。そして現在、私はそれぞれのルーターに異なるURLエンドポイントがあります。このスタイルは、エクスプレスジェネレーターによって奨励されているようです。それが不可能な場合は、ルーターインスタンスを別のファイルに送信する必要がありますか?しかし、それは元の構造と一致していません。
ハギー、2014

あるルーターを別のルーターの下に追加することはできますか?Expressミドルウェアアーキテクチャは下のルーターで処理されるようです(そうであるかどうかは完全にはわかりません)。それは可能だと思います。
ハギー、2014

2
-1これは、ネストされたルーターに関する質問に答えていません
Willem D'Haeseleer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.