You are viewing the legacy version of AdonisJS. Visit for newer docs. This version will receive security patches until the end of 2021.


Routes enable the outside world to interact with your app via URLs.

Routes are registered inside the start/routes.js file.

Basic Routing

The most basic route binding requires a URL and a closure:

Route.get('/', () => 'Hello Adonis')

The return value of the closure will be sent back to the client as a response.

You can also bind a route to a controller using a controller.method signature:

Route.get('posts', 'PostController.index')

The above signature PostController.index refers to the App/Controllers/Http/PostController.js file’s index method.

Available Router Methods

Resourceful routes use different HTTP verbs to indicate the type of request:

Route.get(url, closure), closure)
Route.put(url, closure)
Route.patch(url, closure)
Route.delete(url, closure)

To register a route that responds to multiple verbs, use Route.route:

Route.route('/', () => {
}, ['GET', 'POST', 'PUT'])

To render a view directly (e.g. static pages), use Route.on.render:


In the example above, when the root / route is loaded, the resources/view/welcome.edge file is rendered directly.

Route Parameters

Required Parameters

For dynamic routes, you can define route parameters like so:

Route.get('posts/:id', ({ params }) => {
  return `Post ${}`

In the example above, :id is a route parameter.

Its value is then retrieved via the params object.

Optional Parameters

To define an optional route parameter, append a ? symbol to its definition:

Route.get('make/:drink?', ({ params }) => {
  // use Coffee as fallback when drink is not defined
  const drink = params.drink || 'Coffee'

  return `One ${drink}, coming right up!`

In the example above, :drink? is an optional route parameter.

Wildcard Route

You may want to render a single view from the server and handle routing using your preferred front-end framework:

Route.any('*', ({ view }) => view.render('...'))

Any specific routes need to be defined before your wildcard route:

Route.get('api/v1/users', closure)

Route.any('*', ({ view }) => view.render('...'))

Named Route

Though routes are defined inside the start/routes.js file, they are referenced everywhere else in the application (e.g. using the views route helper to make a URL for a given route).

By using the as() method, you can assign your route a unique name:

Route.get('users', closure).as('users.index')

This will enable you to use route helpers in your templates and code, like so:

<!-- before -->
<a href="/users">List of users</a>

<!-- after -->
<a href="{{ route('users.index') }}">List of users</a>
foo ({ response }) {
  return response.route('users.index')

Both route helpers share the same signature and accept an optional parameters object as their second argument:

Route.get('posts/:id', closure).as('')

route('', { id: 1 })

route helpers also accept an optional parameters object as a third argument to handle protocol, domain and query options:

route('', { id: 1 }, {
  query: { foo: 'bar' }

// resulting in /post/1?foo=bar

// Without parameters:
route('auth.login', null, {
  domain: '',
  protocol: 'https',
  query: { redirect: '/dashboard' }

// resulting in

The same rules apply for the view.

<a href="{{ route('', { id: 1 }, {query: { foo: 'bar' }}) }}">Show post</a>
// href="/post/1?foo=bar"

Route Formats

Route formats open up a new way for content negotiation, where you can accept the response format as part of the URL.

A route format is a contract between the client and server for what type of response to return:

Route.get('users', async ({ request, view }) => {
  const users = await User.all()

  if (request.format() === 'json') {
    return users

  return view.render('users.index', { users })

For the example above, the /users endpoint will be able to respond in multiple formats based on the URL:

GET /users.json     # Returns an array of users in JSON
GET /users          # Returns the view in HTML

You can also disable the default URL and force the client to define the format:

Route.get('users', closure).formats(['json', 'html'], true)

Passing true as the second argument ensures the client specifies one of the expected formats. Otherwise, a 404 error is thrown.

Route Resources

You will often create resourceful routes to do CRUD operations on a resource.

Route.resource assigns CRUD routes to a controller using a single line of code:

// This...
Route.resource('users', 'UserController')

// ...equates to this:
Route.get('users', 'UserController.index').as('users.index')'users', '').as('')
Route.get('users/create', 'UserController.create').as('users.create')
Route.get('users/:id', '').as('')
Route.put('users/:id', 'UserController.update').as('users.update')
Route.patch('users/:id', 'UserController.update')
Route.get('users/:id/edit', 'UserController.edit').as('users.edit')
Route.delete('users/:id', 'UserController.destroy').as('users.destroy')
This feature is only available when binding routes to a Controller.

You can also define nested resources:

Route.resource('posts.comments', 'PostCommentController')

Filtering Resources

You can limit the routes assigned by the Route.resource method by chaining one of the filter methods below.


Removes GET resource/create and GET resource/:id/edit routes:

Route.resource('users', 'UserController')


Keeps only the passed routes:

Route.resource('users', 'UserController')
  .only(['index', 'show'])


Keeps all routes except the passed routes:

Route.resource('users', 'UserController')
  .except(['index', 'show'])

Resource Middleware

You can attach middleware to any resource as you would with a single route:

Route.resource('users', 'UserController')

If you don’t want to attach middleware to all routes generated via Route.resource, you can customize this behavior by passing a Map to the middleware method:

Route.resource('users', 'UserController')
  .middleware(new Map([
    [['store', 'update', 'destroy'], ['auth']]

In the example above, the auth middleware is only applied to the store, update and destroy routes.

Resource Formats

You can define response formats for resourceful routes via the formats method:

Route.resource('users', 'UserController')

Routing Domains

Your application may use multiple domains.

AdonisJs make it super easy to deal with this use-case.

Domains can be a static endpoint like, or a dynamic endpoint like

You can define the domain on a single route as well.
start/routes.js => {
  Route.get('/', ({ subdomains }) => {
    return `The username is ${subdomains.user}`

In the example above, if you visited, you would see The username is virk.

Route Groups

If your application routes share common logic/configuration, instead of redefining the configuration for each route, you can group them like so:

// Ungrouped
Route.get('api/v1/users', closure)'api/v1/users', closure)

// Grouped => {
  Route.get('users', closure)'users', closure)


Prefix all route URLs defined in the group:

start/routes.js => {
  Route.get('users', closure)   // GET /api/v1/users'users', closure)  // POST /api/v1/users


Assign one or many middleware to the route group:

start/routes.js => {
Group middleware executes before route middleware.


Prefix the namespace of the bound controller:

start/routes.js => {
  // Binds '/users' to 'App/Controllers/Http/Admin/UserController'
  Route.resource('/users', 'UserController')


Defines formats for all routes in the group:

start/routes.js => {
}).formats(['json', 'html'], true)


Specify which domain group routes belong to:

start/routes.js => {