I received a great question on the last lesson covering route groups and I wanted to answer it for everyone before moving forward. It's in regard to having routes split among several different files.
The question was if I have a route group I want to apply to more than one file, do I need to redefine the group within each file? In other words, for the below file structure, in order to have a route group for /api/v1
do you need to define the group in both the posts.ts
file and the series.ts
file?
start/
├─ routes/
│ ├─ api/
│ │ ├─ v1/
│ │ │ ├─ posts.ts
│ │ │ ├─ series.ts
├─ routes.ts
That would be a pain! Thankfully, the answer is no, you don't. So long as the code defining a route definition executes within a route group, that route definition will be created as a member of the group.
For example, we can wrap any set of routes within a function then call the function inside two different groups. The result will be the routes defined in the function will be defined twice, once for each group.
function postRoutes() {
Route.get('/posts', () => 'get all posts')
}
Route.group(() => {
// this will define a route for GET: /accounts/:accountId/posts
postRoutes()
}).prefix('/accounts/:accountId')
Route.group(() => {
// this will define a route for GET: /hub/posts
postRoutes()
}).prefix('/hub')
We can then use this same methodology to our advantage when it comes to defining routes to a single group from multiple files.
Using A Function
First, let's take the above example and apply it to our multi-file scenario. We can wrap the routes we have defined within our posts.ts
file and series.ts
file in a function that we export. Then, we can import those functions and execute them within our group, like the below.
// start/routes/api/v1/posts.ts
import Route from '@ioc:Adonis/Core/Route'
export default function postRoutes() {
Route.group(() => {
Route.get('/', () => 'get all posts').as('index')
}).prefix('/posts').as('posts')
}
// start/routes/api/v1/series.ts
import Route from '@ioc:Adonis/Core/Route'
export default function postRoutes() {
Route.group(() => {
Route.get('/', () => 'get all series').as('index')
}).prefix('/series').as('series')
}
// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'
import postRoutes from './routes/api/v1/posts'
import seriesRoutes from './routes/api/v1/series'
Route.group(() => {
postRoutes()
seriesRoutes()
}).prefix('/api/v1').as('api.v1')
Since the code registering the post and series routes is executed within our /api/v1
route group, the group is applied both. So, our routes end up looking like this:
/api/v1/posts AS api.v1.posts.index
/api/v1/series AS api.v1.series.index
Using this knowledge you can also extract the /api/v1
group into its own file within /start/routes/api
as well if you'd wish, then you can import it within routes.ts
the same way we are our post and series routes.
Importing Using Require
The second option we'll be looking at is importing using require. By using require we can import our code directly where we need it to execute, eliminating the need to wrap everything in an additional function the way we are with the first approach we looked at.
So, with the same structure we used with our function approach, let's see how it'd look using require.
// start/routes/api/v1/posts.ts
import Route from '@ioc:Adonis/Core/Route'
Route.group(() => {
Route.get('/', () => 'get all posts').as('index')
}).prefix('/posts').as('posts')
// start/routes/api/v1/series.ts
import Route from '@ioc:Adonis/Core/Route'
Route.group(() => {
Route.get('/', () => 'get all series').as('index')
}).prefix('/series').as('series')
// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'
Route.group(() => {
require('./routes/api/v1/posts')
require('./routes/api/v1/series')
}).prefix('/api/v1').as('api.v1')
The first thing you'll notice is our post and series files no longer are wrapped with an exported function. Since we're importing using require, we no longer need a way to put off the execution of this code.
The next thing you'll notice is that we're directly using require inside of our group.
Apart from that, the file structure and the files themselves look very similar. The end-result routes defined are the exact same as well.
/api/v1/posts AS api.v1.posts.index
/api/v1/series AS api.v1.series.index
Moving The API Group To Routes/API
If you'd like to move the /api/v1
group into a file within the /start/routes/api
directory you can absolutely do that using this approach as well. There's one thing to keep in mind, though. If your routes.ts
file isn't just import statements, then you'll need to take into consideration how you import from ./routes/api
. If the precedence of your routes allows you to import using import ‘./pathhere’
you can do that. Otherwise, you may want to use require directly where you need the routes imported.
So, let's say you created a file at /start/routes/api/index.ts
and move your /api/v1
route group definition into that file.
// start/routes/api/index.ts
import Route from '@ioc:Adonis/Core/Route'
Route.group(() => {
require('./v1/posts')
require('./v1/series')
}).prefix('/api/v1').as('api.v1')
You can import it using import
inside your routes.ts
if this works with the precedence order of your other route definitions. Remember, a request will stop and use the first route definition matching the requested url.
// start/routes.ts
// ... other imports
import './routes/api'
// ... other imports / routes
If using import
will cause problems with your route precedence, then you can use an IIFE.
// ... other imports / routes
require('./routes/api')
// ... other routes
Next Up
So, we've covered a couple of ways you can share a group definition with multiple files without redefining a group. This cuts back on redundancies and also gives you more control over the other of your routes as opposed to defining your groups over again in each file. In the next lesson, we'll get back to our regularly scheduled lessons by covering how to generate route URLs.
Corrections
Thanks to Arthur Emanuel Rezende for correcting me about require being synchronous instead of asynchronous. I had a mental lapse there haha :)