Appearance
Tutorial: Advanced Routing
See the basics
This tutorial continues the project started in Hello World-example.
Trying Changes
This tutorial is going to show-case several ways of defining routes and policies. Feel free to restart the project intermittently by running
sh
hitchy start
and try URLs defined in either case.
Using Controllers
Instead of putting handlers for routes into the router configuration you should start working with controllers.
Note
Controllers are components of a Hitchy-based application.
Create the following file with the given content:
javascript
exports.world = ( req, res ) => res.send( "Hello World!" );
Open file config/routes.js created as part of previous tutorial and replace its content with the following one:
javascript
exports.routes = {
"/": ( req, res ) => res.send( "Hello World!" ),
"/colon": "hello::world",
"/period": "hello.world",
"/object": { controller: "hello", method: "world" },
"/colon/decorated": "HelloController::world",
"/period/decorated": "HelloController.world",
"/object/decorated": { controller: "HelloController", method: "world" },
};
This defines different routes resulting in the same output on request. However, all but the first one are using the controller created before. They use different supported ways for addressing a method exposed by a controller.
Neither way of addressing a controller's method should be mistaken as actual code.
Try it!
Restart project and open http://127.0.0.1:3000/object/decorated in your browser now.
Defining Routes per Method
In HTTP every request selects a method, like GET, POST or PUT. This method information can be used for matching routes. The default is GET. You might select any different method by prepending it to the path name of your routing definition:
javascript
exports.routes = {
"/": ( req, res ) => res.send( "Hello World!" ),
"GET /colon": "hello::world",
"POST /period": "hello.world",
"DELETE /object": { controller: "hello", method: "world" },
"PATCH /colon/decorated": "HelloController::world",
"ALL /period/decorated": "HelloController.world",
"* /object/decorated": { controller: "HelloController", method: "world" },
};
The last two cases show special keywords ALL
and *
supported as special method names for defining routes applying to every request without regard to its HTTP method.
Try it!
In opposition to the previous case, some routes can not be tested in browser that simply, anymore. Testing those routes bound to methods POST, DELETE and PATCH result in showing Page not found error.
What about Policies?
Policies are very similar to controllers as described before.
Create a file api/policies/filter.js with the following content:
javascript
module.exports = {
failOnDemand( req, res, next ) {
if ( req.query.fail ) {
this.api.log( "hitchy:request" )( "responding on failure" );
res.status( 400 ).send( "Failed!" );
} else {
next();
}
}
};
Remarks
A special Hitchy API is exposed as this.api
in every request handler. This API includes a logging service which is used here.
Next create a file config/policies.js with the following content:
javascript
exports.policies = {
"/period": "filter.failOnDemand",
"/object/decorated": "FilterPolicy::failOnDemand",
};
This is associating incoming requests with your policies. It is very similar to defining routes to controllers and thus it is called policy routing. Of course, you could also use those other styles of addressing a target demonstrated on controller routes before.
Major differences between policies and controllers are:
- The fully decorated version of a policy component uses suffix Policy instead of Controller.
- The routing configuration is exposed via
exports.policies
instead ofexports.routes
. - As described in routing basics, policies apply to all requests matching prefix of request path name.
Try It!
Restart project and open http://127.0.0.1:3000/object/decorated?fail=1 in your browser now. It will show Failed!
instead of Hello World!
. By removing the ?fail=1
the greeting returns. In addition, there is an output in console used to run Hitchy.
About Routing Slots
In routing basics you've learned about routing slots. In your application's configuration you can split up your definition of routes and policies to apply to separate slots of routing:
In file config/routes.js or config/policies.js you can replace configurations like these
javascript
exports.policies = {
"/period": "filter.failOnDemand",
"/object/decorated": "FilterPolicy::failOnDemand",
};
with grouped configurations like
javascript
exports.policies = {
before: {
"/period": "filter.failOnDemand",
},
late: {
"/object/decorated": "FilterPolicy::failOnDemand",
},
};
This would apply the previously declared policy routings into separate slots of separate routing stages.
Try it!
By applying the latter policy to the late slot the URL http://127.0.0.1:3000/object/decorated?fail=1 would not result in displaying Failed!
this time. However, the policy is used nonetheless, as you can see by the log message in Hitchy's output.
Routing Parameters
Hitchy depends on popular path-to-regexp to support highly flexible definition of request paths. This includes definition of named parameters to be exposed in context of request handlers.
Append the following route definition in config/routes.js. This time you should know how to achieve that.
javascript
"/greet/:clientName": "Hello::world"
In api/controllers/hello.js replace existing method world with this one:
javascript
world( req, res ) {
res.send( `Hello ${req.params.clientName || "world"}!` );
}
Try it!
After restarting the response gets personal when requesting URL like http://127.0.0.1:3000/greet/John in your browser.