# Router
XpresserRouter (opens new window) provides an easy way to route your urls to functions or controller actions.
In your application an instance of XpresserRouter can be accessed globally using $.router
.
# Using global DollarSign
# Without Global DollarSign
if you don't want to use the global dollarSign, or you disabled exposeDollarSign
then you have to import the router
like this:
# Supported Verbs
- checkout
- copy
- delete
- get
- head
- lock
- merge
- mkactivity
- mkcol
- move
- m-search
- notify
- options
- patch
- post
- purge
- put
- report
- search
- subscribe
- trace
- unlock
- unsubscribe
# Basic
router.METHOD(PATH, ACTION)
where
router
is an instance of XpresserRouter.METHOD
is an HTTP request method, in lowercase.PATH
is a path on the server.ACTION
is the function executed when the route is matched.
ACTION
can either be a function, or a string holding a controller action.
router.get('/', http => http.view('about'));
Using a Function works same way a controller action would but using the later would make your routes file look clean for your own readability.
# Routing to Files.
The router provides a shorthand helper for routing static files.
router.sendFile('/*', 'public/404.html');
The above is a shorthand for the code below.
router.get('/*', http => {
const file = http.$('path').base('public/404.html');
return http.res.sendFile(file);
})
# Routing to Controller actions.
Assuming we have a controller named PagesController with 2 methods: about & faq
router.get('/about', 'PagesController@about')
router.get('/faq', 'PagesController@faq')
Looks neat now right? It can look better with smart routing.
Note: XpresserRouter does not require the word Controller
in your controller name when routing e.g
router.get('/about', 'Pages@about')
router.get('/faq', 'Pages@faq')
The above works exactly as the first.
# Named Routes
Named routing is another amazing feature of XpresserRouter. Named routes allows referring to routes when generating redirects or Urls easily.
You can specify named routes by chaining the name
method onto the route definition
router.post('/auth/login', 'Auth@login').name('login')
Once you have assigned a name to your routes, you may use the route's name when generating URLs or redirects.
In your views you can refer to that route using the ctx.route($name)
helper.
<form action="<%= ctx.route('login') %>">
...
</form>
will be rendered as
<form action="/auth/login">
...
</form>
If your name is the same with your controller action name just like the example above you can use the.actionAsName()
helper.
router.post('/auth/login', 'Auth@login').name('login')
// is same as
router.post('/auth/login', 'Auth@login').actionAsName();
# Paths
router.path()
is not part of the http verbs functions, it is a special method for creating deep routes.
router.path(path, () => {/* Child Routes */});
It accepts the path for 1st argument and a function containing child routes for the 2nd argument.
Assuming you have a controller with so many methods for a related route. e.g
const {ControllerClass} = require('xpresser');
class AccountController extends ControllerClass {
static view() {/* Some codes here... */}
static update() {/* Some codes here... */}
static change_password() {/* Some codes here... */}
static send_code() {/* Some codes here... */}
static verify() {/* Some codes here... */}
}
Now we want all the requests to this controller to be on example.com/account/{whatever}, this is
where $.router.path
comes handy.
# Without Path
router.get('/account', 'Account@view');
router.post('/account', 'Account@update');
router.post('/account/change_password', 'Account@change_password');
router.post('/account/send_code', 'Account@send_code');
router.get('/account/verify', 'Account@verify');
# Using Path
router.path('/account', () => {
router.get('', 'view');
router.post('', 'update');
router.post('change_password', 'change_password');
router.post('send_code', 'send_code');
router.get('verify', 'verify');
}).controller('Account');
The above code will produce the same routes as the one without path. But it doesn't end here.
The path function also provides amazing ways to make you write less when declaring routes.
# @/= Path Helpers
Notice the url and actions of change_password
, send_code
& verify
in the code above, They are the same. i.e
router.post('change_password', 'change_password'); // url/path and action is the same.
router.post('send_code', 'send_code'); // url/path and action is the same.
router.get('verify', 'verify'); // url/path and action is the same.
it kind of feels like there is a little redundancy there, well XpresserRouter provides a clean solution for situations like this.
# @
// Instead of
router.post('change_password', 'change_password');
// it can be written as
router.post('@change_password');
The above simply means that XpresserRouter should use same url as the actions name.
# =
// Instead of
router.get('', 'view');
// it can be written as
router.get('=view');
The above simply means that XpresserRouter should set the action to the path's index url.
if we want to rewrite the above example it will look like below
router.path('/account', () => {
router.get('=view');
router.post('=update');
router.post('@change_password');
router.post('@send_code');
router.get('@verify');
}).controller('Account');
# Generated Routes #1
Method | Url | Controller@action |
---|---|---|
GET | /account | AccountController@view |
POST | /account | AccountController@update |
POST | /account/change_password | AccountController@change_password |
POST | /account/send_code | AccountController@send_code |
GET | /account/verify | AccountController@verify |
Note: This only applies if your controller actions matches your url which most times is the case and can be used
only by routes declared in router.path()
children function.
# Controller Actions Case
Note: Require xpresser >=0.25.4
When using @
or =
in the path, XpresserRouter will automatically convert the action name to snake
case.
This behavior can be controlled by the {server.router.pathCase} configuration.
$.router.path("/user", (r) => {
r.post('@sendResetEmail');
r.post('@accountSettings');
})
// if {server.router.pathCase} is `snake` will be converted to:
"/user/send_reset_email"
"/user/account_settings"
// if {server.router.pathCase} is `kebab` will be converted to:
"/user/send-reset-email"
"/user/account-settings"
# Named Routes
In real life situations when using the path function you may like to have a name prefix for all names registered in that path e.g
router.path('/account', () => {
router.get('@view').name('account.view');
router.post('@update').name('account.update');
router.post('@change_password').name('account.change_password');
router.post('@send_code').name('account.send_code');
router.get('@verify').name('account.verify');
}).controller('Account');
# As
Instead of using account.
repeatedly you can use the as
helper like this
router.path('/account', () => {
router.get('=view').name('view');
router.post('=update').name('update');
router.post('@change_password').name('change_password');
router.post('@send_code').name('send_code');
router.get('@verify').name('verify');
}).controller('Account').as('account');
The as function tells XpresserRouter to prefix any name in the given path with account.
# Actions as Name
Using the routes declared above as an example, you will notice the controller actions are the same with names of all the routes.
Path also offers a function similar to the actionAsName function but this time it is plural.
router.path('/account', () => {
router.get('=view');
router.post('=update');
router.post('@change_password');
router.post('@send_code');
router.get('@verify');
}).controller('Account').as('account').actionsAsName();
With these simple looking codes, your end result will be:
Method | Url | Controller@action | Name |
---|---|---|---|
GET | /account | AccountController@view | account.view |
POST | /account | AccountController@update | account.update |
POST | /account/change_password | AccountController@change_password | account.change_password |
POST | /account/send_code | AccountController@send_code | account.send_code |
GET | /account/verify | AccountController@verify | account.verify |
# External Controller
Routes declared in .path
also can make reference to external controllers but will be prefixed by the current path
url.
router.path('/user/:id', () => {
router.get('posts', 'Posts@user')
// -> /user/:id/posts
}).controller('Account')
# Use Controller
The router
instance includes a useController
method. This method groups routes to a controller using path
router.useController("Post", () => {
router.get("/posts", "all");
router.post("/posts", "create");
router.path("/post/:postId", () => {
router.get("=view");
route.post("=update");
})
});
# Generated Routes #2
Method | Url | Controller@action |
---|---|---|
GET | /posts | PostController@all |
POST | /posts | PostController@create |
GET | /post/:postId | PostController@view |
POST | /post/:postId | PostController@update |
# View All Routes
Xpresser provides a cli command that lists all routes for you and also includes mapped controllers.
See xjs routes
command
# Advanced Routing
Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expressions.
The characters ?, +, *, and () are subsets of their regular expression counterparts. The hyphen (-) and the dot (.) are interpreted literally by string-based paths.
If you need to use the dollar character ($) in a path string, enclose it escaped within ([ and ]). For example, the path string for requests at “/data/$book”, would be “/data/([$])book”.
# Route Path patterns
Here are some examples of route paths based on string patterns.
This route path below will match acd and abcd.
router.get('/ab?cd', 'YourController@index');
This route path will match abcd, abbcd, abbbcd, and so on.
router.get('/ab+cd', 'YourController@index');
This route path will match abcd, abxcd, abRANDOMcd, ab123cd, and so on.
router.get('/ab*cd', 'YourController@index');
This route path will match /abe and /abcde.
router.get('/ab(cd)?e', 'YourController@index');
# Paths with regular expressions.
Examples of route paths based on regular expressions:
This route path will match anything with an “a” in it.
router.get(/a/, 'YourController@index')
This route path will match butterfly and dragonfly, but not butterflyman, dragonflyman, and so on.
router.get(/.*fly$/, 'YourController@index')
# Route parameters.
Route parameters are named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the req.params object, with the name of the route parameter specified in the path as their respective keys.
Route path: /users/:userId/books/:bookId
Request URL: http://localhost:3000/users/34/books/8989
http.params: { "userId": "34", "bookId": "8989" }
To define routes with route parameters, simply specify the route parameters in the path of the route as shown below.
router.get('/users/:userId/books/:bookId', function(http) {
return http.send(http.params); // {"userId": "34", "bookId": "8989"}
});
# Routes after plugins
By default, xpresser registers your defined routes first before plugin routes. This makes it possible to overwrite any plugin route if need be.
To define routes after plugins routes you need to declare the routes after plugin function. for example
const {getInstanceRouter} = require("xpresser");
const router = getInstanceRouter()
router.get("/", () => "Index Page");
// Declare routes after plugins function
router.routesAfterPlugins = function () {
// Define routes after plugins
router.get("/*", (http) => {
return http.status(404).send("404 error");
});
};