
Let's start dipping our toes in the code now by creating some routes and controllers.
For anyone unaware of the term routing. In terms of web development, it is a mapping of URLs and their handlers that you want your app to handle. URLs outside of this mapping will result in a 404.
Defining routes
Routes in AdonisJS are defined inside thestart/routes.ts
file. Using this file is a convention and not a technical limitation. Let's open the file and replace its contents with the following code snippet.
importRoutefrom'@ioc:Adonis/Core/Route'Route.get('/',async()=>{return'This is the home page'})Route.get('/about',async()=>{return'This is the about page'})Route.get('/projects',async()=>{return'This is the page to list projects'})
- We begin by importing the
Route
module. - Using the
Route.get
method, we define a total of 3 routes. - A typical route accepts aroute pattern and ahandler to respond to the requests.
- In the above example, the handler is an inline function. Later we will look into using controllers as well.
- Finally, the return value of the function is sent back to the client making the request.
Let's give this code a try by visiting the URLs for the registered routes.
Supported data types
You can return most of the Javascript data types from the route handler and AdonisJS will properly serialize them for you.
Route.get('/',async()=>{// return 28// return new Date()// return { hello: 'world' }// return [1, 2, 3]// return false// return '<h1> Hello world </h1>'})
HTTP Context
Every route handler receives an instance of the HTTP context as the first parameter. The context holds all the information related to the current request, along with theresponse
object to customize the HTTP response.
Route.get('/',async(ctx)=>{console.log(ctx.inspect())return'handled'})
Following is the output of thectx.inspect()
.
If you are coming from a framework like express, then there is noreq
andres
objects in AdonisJS. Instead you have access toctx.request
andctx.response
.
Also note that the API ofrequest
andresponse
is not compatible with express and neither it is a goal for us.
The HTTP context has an extendible API and many AdonisJS packages add their properties to the context. For example: If you install the@adonisjs/auth module, it will add thectx.auth
property.
Using controllers
Controllers in AdonisJS are vanilla ES6 classes stored inside theapp/Controllers/Http
directory. You can create a new controller by running the following ace command.
node ace make:controller TodoController# CREATE: app/Controllers/Http/TodosController.ts
Let's open the newly created file and replace its contents with the following code snippet.
import{HttpContextContract}from'@ioc:Adonis/Core/HttpContext'exportdefaultclassTodosController{publicasyncindex(ctx:HttpContextContract){return'Hello world from the todos controller'}}
How should we now go about using this controller inside our routes file?
Let's begin with zero magic and simply import the controller inside the routes file. Open thestart/routes.ts
file and add another route using the controller.
importRoutefrom'@ioc:Adonis/Core/Route'importTodosControllerfrom'App/Controllers/Http/TodosController'Route.get('/todos',(ctx)=>newTodosController().index(ctx))
Visit thehttp://localhost:3333/todos URL and you will surely see the return value from the controller method.
Lazy loading controllers
Now, imagine an app with 40-50 controllers. Every controller will also have its own set of imports, making the routes file a choke point.
Lazy loading is the perfect solution to the above problem. Instead of importing all the controllers at the top level, we can lazily import them within the route's handler.
importRoutefrom'@ioc:Adonis/Core/Route'Route.get('/todos',async(ctx)=>{constTodosController=(awaitimport('App/Controllers/Http/TodosController')).defaultreturnnewTodosController().index(ctx)})
Now, theTodosController
is only loaded when the request for the/todos
route comes in. Since the import/require statements are cached in Node.js, you don't have to worry about reading the same file multiple times from the disk.
Are are you happy with the above code?
I am personally not. There is too much boilerplate and you would expect a framework to do a better job here and cleanup things for you and AdonisJS does that.
Replace the previously written route with the following code snippet.
Route.get('/todos','TodosController.index')
This is the recommended way of referencing controllers within your routes file.
- We already know that your controllers are inside the
app/Controllers/Http
directory and hence there is no need to define the complete path. - You only need to define the file name and the method to be called on the exported class.
- Behind the scenes, AdonisJS will lazily import the controller. Creates an instance of it and executes the referenced method.
What about the type safety?
The verbose implementation has the extra benefit of being type safe. This is something missing when using the string based expression. Or I will say, it is missing for now.
We need two things to achieve type safety when referencingcontroller.method
as a string expression.
- The ability to tokenize the expression and create a full path to the controller and its method. This is achievable withTypescript 4.1 beta release. Here is aproof of concept for the same.
- Next is the ability to have an
Import
type with support for generics. There is anopen issue for it and I am positive that it will make its way to the Typescript in the future, as it adheres to the Typescript design goals.
To summarize, we bet in the future of Typescript and decided to remove all the extra boilerplate required to reference controllers within the routes file and expose a simple to use API.
Wrap up
Alright, let's wrap up this post. In the next post, we will begin designing the web page for our todo app.
Meanwhile, lemme share some code examples for commonly required tasks that you may perform when creating a web app.
Render views
Render views using the AdonisJStemplate engine
Route.get('todos',async({view})=>{returnview.render('todos/index',{todos:[{id:1,title:'Writing an article',isCompleted:false}],})})
Modify outgoing response
Route.get('/',async({response})=>{response.redirect('/to/a/url')response.status(301)response.header('x-powered-by','my-app-name')response.cookie('foo','bar')})
Stream files from the disk
Route.get('/',async({response})=>{response.download('/path/to/some/file.txt')})
Read request data
Route.get('/',async({request})=>{console.log(request.url())console.log(request.method())console.log(request.cookie('cookie-name'))// request body + query stringconsole.log(request.all())// get a single file & validate it tooconstavatar=request.file('avatar',{size:'2mb',extnames:['jpg','png','jpeg'],})// All uploaded files as an objectconsole.log(request.allFiles())})
Top comments(2)

- LocationCocos Islands
- WorkVue Developer at Daily Syntax
- Joined
how to use imported routes.
user-routes.ts
admin-routes.ts
routes.ts -- default
import user, admin ................... routes to routes.ts
but how to use them in routes group in routes.ts file
For further actions, you may consider blocking this person and/orreporting abuse