URL Generation
This guide covers the API around generating plain and signed URLs for the registered routes. By the end of this guide, you will know:
- How to generate plain URLs for registered routes.
- Using signed URLs for tasks like email verification.
- How to assign unique names to your routes.
What is URL Generation?
Even though the routes are defined inside the start/routes.ts
file, you need to reference them at several other places. For example:
- Redirecting the request to a URL
- An anchor tag in your HTML document
- Or, the form action
A simple approach is to hardcode the URLs in all places. However, if you decide to change the routes later, you will have to find all the harcoded URLs and update them. A better approach is to generate the URLs dynamically.
Generating URLs
AdonisJS provides helpers for making URLs for the registered routes. You can reference routes either by their Controller.action
name or by using the route unique name. For example:
Begin by defining a route
Route.get('users/:id', 'UsersController.show')
Then, inside your controller or the view template, you can generate a URL by referencing the Controller.action
name.
import Route from '@ioc:Adonis/Core/Route'
export default class UsersController {
public async index ({ response }) {
response.redirect(
Route.makeUrl('UsersController.show', { params: { id: 1 } }) )
}
}
Defining Params
The routes with parameters expects you to define the params
values in order to make the URL, otherwise an exception will be raised.
Defining Query String
You can also define the query string values when making the URL. For example:
Route.makeUrl('UsersController.show', {
params: { id: 1 },
qs: {
status: 'active',
},
})
// Output: /users/1?status=active
Query string with arrays
Route.makeUrl('UsersController.show', {
params: { id: 1 },
qs: {
fields: ['username', 'email'],
},
})
// Output: /users/1?fields%5B0%5D=username&fields%5B1%5D=email
Using Route Names
In majority of cases, the Controller.action
will point to a single route. However, there can be cases, in which a controller action is shared by multiple routes. For example:
Showing a list of all the blog posts on the homepage as well as the /posts
URL and hence using the same controller action on both the routes.
Route.get('/', 'PostsController.index')
Route.get('posts', 'PostsController.index')
In situations like these, it becomes mandatory to give unique names to your routes, so that you can generate correct URLs.
Using the as
method, you can assign a unique name to a route. The router will complain, if you attempt to assign the same name to the multiple routes and hence this is the best way to ensure that you are always referencing the correct route when generating URLs.
Route.get('/', 'PostsController.index')
.as('homepage') 👈
Route.get('posts', 'PostsController.index')
.as('listPosts') 👈
The makeUrl
API remains the same, all you need to do is use the route name, instead of the controller action name.
Route.makeUrl('listPosts')
// or
Route.makeUrl('homepage')
Generating Signed URLs
Signed URLs provides a neat way to generate URLs with hash signature appended to them. The hash ensures that the generated URL is not modified or tampered.
A great use case of signed URLs is email verification. Instead of generating and storing email verification tokens inside the database, you can opt-in for signed URLs.
For demonstration, let's create a dummy app to verify the user email address using a signed URL.
Create a route that will handle email verification
Route.get('/verify/:email', async ({ request }) => { if (request.hasValidSignature()) { return 'Marking email as verified' } return 'Url is not valid' }).as('verifyEmail')
Create another route to generate a signed URL to verify the email. In a real world app, you may send the signed URL to the user email address.
Route.get('/get_verification_link', async () => { const signedUrl = Route.makeSignedUrl('verifyEmail', { params: { email: '[email protected]', } }) return `Click <a href="${signedUrl}">here</a> to verify email address` })
Visit
/get_verification_link
URL and click on the verification link. If you attempt to modify the URL, the signature verification will fail.Demo
What just happened?
- As you can notice, we directly pass the user email address to the URL, without worrying about someone changing it.
- The
request.hasValidSignature()
tests the route signature and ensures that the URL is not tampered. - Just like
params
, you can also addquery string
to the signed URL's and it will just work fine.
Expiring Signed URLs
By default, the signed URLs lives forever. However, you can add expiry to them at the time of generating one.
Route.get('/get_verification_link', async () => {
const signedUrl = Route.makeSignedUrl('verifyEmail', {
params: {
email: '[email protected]',
},
expiresIn: '30m', })
return `Click <a href="${signedUrl}">here</a> to verify email address`
})