Skip to main content

Routing

Understand how you can set up routes for your REST API application.

Basic routing​

The most basic Athenna routes accept an url and a closure, providing a basic and expressive method of defining routes and behavior without complicated routing configuration files:

import { Route } from '@athenna/http' // Route Facade

Route.get('/welcome', ({ response }) => {
return response.status(200).send({ hello: 'world' })
})

Available Router Methods​

The router allows you to register routes that respond to any HTTP verb:

Route.get(url, callback)
Route.post(url, callback)
Route.put(url, callback)
Route.patch(url, callback)
Route.delete(url, callback)
Route.options(url, callback)

Sometimes you may need to register a route that responds to multiple HTTP verbs. You may do so using the any method to respond to all HTTP verbs:

Route.any('/', () => {
//
})
tip

When defining multiple routes that share the same url, routes using the get, post, put, patch, delete, and options methods should be defined before routes using the any and redirect methods. This ensures the incoming request is matched with the correct route.

View routes​

You may use the Route.view() method if you have a route handler that only renders a view. It is a convenient shortcut to render a view without defining an explicit handler:

Route.view('/welcome', 'welcome')

The example above is just a shortcut for the following:

Route.get('/welcome', ({ response }) => {
return response.view('welcome')
})

Optionally, you can pass the template data as the third argument, just like in response.view() method:

Route.view('/welcome', 'welcome', { name: 'lenon' })

Redirect routes​

If you are defining a route that redirects to another url, you may use the Route.redirect() method. This method provides a convenient shortcut so that you do not have to define a full route or controller for performing a simple redirect:

Route.redirect('/here', '/there')

By default, Route.redirect() method returns a 302 status code. You may customize the status code using the optional third parameter:

Route.redirect('/here', '/there', 301)

The route list​

The route:list command can easily provide an overview of all the routes that are defined by your application:

node artisan route:list

By default, the route middleware that are assigned to each route will not be displayed in the route:list output; however, you can instruct Athenna to display the route middleware by adding the -m, --middleware option to the command:

node artisan route:list --middleware

Route parameters​

Sometimes you will need to capture segments of the URL within your route. For example, you may need to capture a user's ID from the URL. You may do so by defining route parameters:

Route.get('/user/:id', ({ request, response }) => {
return response.send({ user: `User ${request.param('id')}` })
})

You may define as many route parameters as required by your route:

Route.get('/posts/:postId/comments/:commentId', ({ request, response }) => {
return response.send({ params: request.params })
})

Named routes​

Named routes allow you to give a name to a specific route. You may specify a name for a route by chaining the name() method onto the route definition:

Route.get('/users/:id', 'UserController.show').name('users.show')

The route name is displayed by the route:list command and it's available in the request.routeName getter, which is very useful for logs and metrics:

Route.get('/users/:id', ({ request }) => {
console.log(request.routeName) // users.show
}).name('users.show')
note

Routes created by resource controllers are automatically named using the controller name and the action, like UserController.index.

Route data​

Sometimes you want to attach some static values to a route that will be used by your middlewares, like the permission needed to access it. You can do so using the data() method:

Route.post('/posts', 'PostController.store')
.data({ permission: 'post:create' })
.middleware('authorize')

These values will be available in the data object of the request context. This means that a single middleware can be reused by all your routes, each one with its own permission:

Path.middlewares('AuthorizeMiddleware.ts')
import { Middleware, type Context, ForbiddenException } from '@athenna/http'

@Middleware({ name: 'authorize' })
export class AuthorizeMiddleware {
public async handle({ data }: Context) {
if (!data.user.permissions.includes(data.permission)) {
throw new ForbiddenException('You are not allowed to do this.')
}
}
}

You can also set one value per call by giving a key and a value:

Route.post('/posts', 'PostController.store')
.data('permission', 'post:create')
.data('audit', true)
warning

Calling data() with an object will replace all the values that were already set in the route.

tip

Each request receives its own copy of the route data, so you can safely change the data object inside your middlewares without affecting other requests. When a global middleware sets a value with the same key of your route data, the value of the global middleware is used.

Route groups​

Route groups allow you to share route attributes, such as middleware, across a large number of routes without needing to define those attributes on each route.

Middleware​

To assign middlewares to all routes within a group, you may use the middleware method before defining the group. Middleware is executed in the order they are listed in the array:

Route.group(() => {
Route.get('/', () => {
// Uses first & second middleware...
})

Route.get('/user/profile', () => {
// Uses first & second middleware...
})
}).middleware(['first', 'second'])

Route prefixes​

The prefix method may be used to prefix each route in the group with a given URL. For example, you may want to prefix all route URLs within the group with admin:

Route.group(() => {
Route.get('/users', () => {
// Matches only the "/admin/users" URL
})
}).prefix('admin')

Cross-origin resource sharing (CORS)​

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.

Athenna uses the @fastify/cors plugin inside HttpKernel. All the configurations that @fastify/cors supports can be set inside

Path.config('http.ts')

./src/config/http.ts

file in the cors object.

Cors plugin is registered in your http application by default, but you can remove it uninstalling @fastify/cors from your application, or removing the cors object key from your

Path.config('http.ts')

./src/config/http.ts

file.

tip

For more information on CORS and CORS headers, please consult the MDN web documentation on CORS.

Custom route file path​

You can change the name and the path of your

Path.routes('http.ts')

./src/routes/http.ts

file. To do that you need to set the new path to Ignite.httpServer() method:

Path.bin('dev.ts')
import { Ignite } from '@athenna/core'

const ignite = await new Ignite().load(import.meta.url)

await ignite.httpServer({
routePath: './bootstrap/routes/http-dev.js' 👈
})
tip

Always remember that when using relative paths to set something in Athenna, you need to use your project root path as reference, just like in the example above.