Response

The instance of the response class allows you to respond to the HTTP requests. AdonisJS out of the box supports sending HTML fragments, JSON objects, streams and much more.

You can access the response object from the HTTP context instance passed to the route handler, middleware, and exception handler.

Route.get('/', (ctx) => {
ctx.response.send('hello world')
})

Sending the response

The simplest way to send a response is to return a value from the route handler.

Route.get('/', () => {
/** Plain string */
return 'This is the homepage'
/** Html fragment */
return '<p> This is the homepage </p>'
/** JSON response */
return { page: 'home' }
/** Converted to ISO string */
return new Date()
})

Along with returning a value, you can also use the response.send method to send the response. The first argument is the response body (same as the return value). Optionally you can pass a second argument to generate and set the etag .

You can also enable ETag generation for all responses by enabling the http.etag property inside the config/app.ts file.

response.send({ hello: 'world' })
// With etag
response.send({ hello: 'world' }, true)

Serializing response body

Following is the list of data types serialized by the response class.

Content type inference

The response class automatically sets the content-type and content-length headers by inspecting the response body.

The automatic content type header is only defined when you don't set it explicitly during the request lifecycle.

Lazy Response

Many Node.js frameworks write the response to the outgoing stream as soon as you call the response.send method. AdonisJS does not do the same. Instead, we wait for the route handler and all the middleware calls to finish before writing the final response.

This approach ensures that the last call to response.send always wins. In most cases, this behavior doesn't impact you or your applications at all. However, it allows you to post-process the response inside a middleware.

Following is an example of converting the camelCase object keys to snake_case.

The following example is not the best way to transform response. It is just a demonstration of how post-processing a response looks like

import snakeCaseKeys from 'snakecase-keys'
Route
.get('/', async ({ response }) => {
response.send({ fullName: 'Harminder Virk' })
})
.middleware(async ({ response }, next) => {
await next()
/**
* Following code is executed after the route handler.
* Read the middleware guide to learn how it works
*/
const existingBody = response.lazyBody[0]
if (!existingBody || existingBody.constructor !== Object) {
return
}
response.send(snakeCaseKeys(existingBody))
})

In the above example, the route handler writes the response body using the response.send method. However, the downstream middleware mutates the body and re-writes it using the response.send again.

Since the response body is lazily evaluated, We will always set the content-length and the content-type headers by inspecting the most recent response body.

Response status and headers

Following are the methods to work with the response headers and the response status.

The response.header method defines the HTTP response header. Using this method overwrites the existing header (if any).

response.header('Content-type', 'text/html')

append

The response.append method is similar to the header method. However, it appends to the existing header value (if any).

response.append('Set-cookie', 'cookie-value')

removeHeader

The response.removeHeader allows removing an existing response header.

response.removeHeader('Content-type')

getHeader

The response.getHeader method returns the value of an existing header.

const cookie = response.getHeader('Set-cookie')

safeHeader

The response.safeHeader method is similar to the header method. However, it only defines the header if it is not defined already.

response.safeHeader('Content-type', 'application/json')

status

The response.status defines the status for the HTTP response.

response.status(401)

safeStatus

Like the status method, the response.status only defines the status if it is not defined already.

response.safeStatus(401)

Streams & file downloads

AdonisJS has first-class support for piping streams and file downloads. Also, we make sure to clean up streams in case of errors properly.

stream

The response.stream method allows piping the stream to the response. This method does not set the content-type and the content-length headers, and you will have to set them manually.

const image = fs.createReadStream('./some-file.jpg')
response.stream(image)

In the case of errors, A 500 response is sent to the client. However, you can send a custom status code and message by defining a callback as the second parameter.

response.stream(image, (error) => {
return ['Unable to send file', 400]
})

download

The download method streams the file to the client by reading it from the disk. Unlike the stream method, the download method does set the content-type and the content-length headers.

const filePath = Application.tmpPath('uploads/some-file.jpg')
response.download(filePath)

Optionally, you can also define the ETag for the file.

const filePath = Application.tmpPath('uploads/some-file.jpg')
response.download(filePath, true)

A custom status code and a message can be defined by passing a callback as the third parameter.

const filePath = Application.tmpPath('uploads/some-file.jpg')
response.download(filePath, true, (error) => {
if (error.code === 'ENOENT') {
return ['File does not exists', 404]
}
return ['Cannot download file', 400]
})

attachment

The response.attachment is similar to the download method. However, it allows customizing the downloaded file name and also defines the Content-Disposition header.

const filePath = Application.tmpPath('uploads/some-file.jpg')
response.attachment(filePath)
// define custom name
response.attachment(filePath, 'foo.jpg')
// define custom disposition
response.attachment(filePath, 'foo.jpg', 'inline')

Redirects

The response class exposes a rich API to work with redirects, including redirecting users to a route, redirecting back to the previous page, and forwarding the existing query string.

You can get an instance of the Redirect class using the response.redirect() method.

// Redirect back
response.redirect().back()
// Redirect to a url
response.redirect().toPath('/some/url')

Custom status code

By default, a 302 status code is used. You can override it using the .status method.

response.redirect().status(301).toPath('/some/url')

Redirect to a route

You can also redirect the request to a named route using the .toRoute method.

response.redirect().toRoute('PostsController.show', { id: 1 })

Define/forward query string

The .withQs allows you to forward the existing query string or define a custom query string during redirect.

response
.redirect()
.withQs() // 👈 forwardes the existing qs
.back()
response
.redirect()
.withQs({ sort: 'id' }) // 👈 custom
.back()

withQs with params

Calling the .withQs method with custom object multiple times merges the objects together. However, you can combine it with .clearQs method to clear the existing objects. For example:

response
.redirect()
.withQs({ sort: 'id' })
.clearQs()
.withQs({ filters: { name: 'virk' } })
.toPath('/users')
// URL: /users?filters[name]=virk

withQs without params

Calling the withQs method without any parameters will forward the existing query string to the redirected URL. If you redirect the user back to the old page, then the query string from the referrer header URL will be used.

response.redirect().withQs().back() // 👈 referrer header qs is used
response.redirect().withQs().toPath('/users') // 👈 current URL qs is used

Abort and respond

The response class allows you to abort the current HTTP request using the response.abort or response.abortIf methods.

abort

The response.abort method aborts the current request by raising an AbortException

The method accepts a total of two arguments: ie, the response body and an optional status.

if (!auth.user) {
response.abort('Not authenticated')
// with custom status
response.abort('Not authenticated', 401)
}

abortIf

The response.abortIf method accepts a condition and aborts the request when the condition is true.

response.abortIf(!auth.user, 'Not authenticated', 401)

abortUnless

The response.abortUnless method is the opposite of the abortIf method.

response.abortUnless(auth.user, 'Not authenticated', 401)

Other methods and properties

Following is the list of other methods and properties available in the response class.

finished

Find if the response has been written to the outgoing stream.

if (!response.finished) {
response.send()
}

headersSent

An alias for the Node.js res.headersSent property.

isPending

The property is the opposite of the response.finished property.

if (response.isPending) {
response.send()
}

vary

A shortcut to define the HTTP vary header . Calling the vary method multiple times will append to the list of existing headers.

response.vary('Origin')
// Set multiple headers
response.vary('Accept, User-Agent')

location

A shortcut to set the HTTP location header .

response.location('/dashboard')

type

A shortcut to set the HTTP content-type header .

response.type('application/json')

You can also make use of the keywords for defining the content type. For example:

response.type('json') // defines content-type=application/json
response.type('html') // defines content-type=text/html

Descriptive response methods

The response class has a bunch of descriptive methods (one of each HTTP status) to send the response body and set the status at the same time. For example:

response.badRequest({ error: 'Invalid login credentials' })
response.forbidden({ error: 'Unauthorized' })

Here's the list of all the available methods.

Extending Response class

You can extend the Response class using macros or getters. The best place to extend the response is inside a custom service provider.

Open the pre-existing providers/AppProvider.ts file and write the following code inside the boot method.

import { ApplicationContract } from '@ioc:Adonis/Core/Application'
export default class AppProvider {
public static needsApplication = true
constructor(protected app: ApplicationContract) {}
public async boot() {
const Response = this.app.container.use('Adonis/Core/Response')
Response.macro('flash', function (messages) {
this.ctx!.session.flash(messages)
return this
})
}
}

In the above example, we have added the flash method to the response class. It sets the flash messages by internally calling the session.flash method.

You can use the newly added method as follows.

Route.post('users', ({ response }) => {
response.flash({ success: 'User created' })
})

Informing typescript about the method

The flash property is added at the runtime, and hence TypeScript does not know about it. To inform the TypeScript, we will use declaration merging and add the property to the ResponseContract interface.

Create a new file at path contracts/response.ts (the filename is not important) and paste the following contents inside it.

contracts/response.ts
declare module '@ioc:Adonis/Core/Response' {
interface ResponseContract {
flash(messages: any): this
}
}

Additional reading

Following are some of the additional guides to learn more about the topics not covered in this document.