Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Route parameters

Each blog post has its own address: /posts/1, /posts/2 and so on. You cannot write a route for each one, so the variable part of the address is declared as a parameter — it starts with a colon.

A parameter in the address

// start/routes.ts
import router from '@adonisjs/core/services/router'
 
router.get('/posts/:id', async ({ params }) => {
  return `Post number ${params.id}`
})

The handler receives the request context object, and destructuring takes its params field. At /posts/5 the page shows "Post number 5". Parameter values always arrive as strings.

Several parameters

// start/routes.ts
router.get('/posts/:postId/comments/:commentId', async ({ params }) => {
  return `Comment ${params.commentId} on post ${params.postId}`
})

The address /posts/3/comments/12 returns "Comment 12 on post 3".

An optional parameter

A question mark after the name makes the parameter optional:

// start/routes.ts
router.get('/archive/:year?', async ({ params }) => {
  const year = params.year ?? 'all years'
  return `Archive: ${year}`
})

Both addresses work: /archive/2025 shows "Archive: 2025", and /archive shows "Archive: all years".

Checking the format

To make a route match only numbers, give the parameter a check with the where method:

// start/routes.ts
router
  .get('/posts/:id', async ({ params }) => {
    return `Post number ${params.id}`
  })
  .where('id', router.matchers.number())

Now /posts/15 works and /posts/hello returns 404. The ready-made number() matcher also turns the value into a number, so params.id is a number, not a string. There are others: router.matchers.uuid() and router.matchers.slug().

You can set your own check with a regular expression:

// start/routes.ts
router
  .get('/tags/:name', async ({ params }) => `Tag ${params.name}`)
  .where('name', /^[a-z]+$/)

Linking to a route with a parameter

In a template or in HTML the address is built from the parameter:

{{-- resources/views/pages/home.edge --}}
<a href="/posts/{{ post.id }}">{{ post.title }}</a>

The next lesson shows a better way — links by route name.

Common mistake

AdonisJS checks routes in the order they are declared. A route with a parameter declared above will also catch a specific address:

// start/routes.ts
// Wrong: /posts/create goes to the first route with id = "create".
router.get('/posts/:id', async ({ params }) => `Post ${params.id}`)
router.get('/posts/create', async () => 'New post')

Put specific addresses above routes with parameters, or restrict the parameter with the number() matcher.

Thanks for reading!

Donate to the site — we work hard for you.