Response
An instance of the response class is used to respond to HTTP requests. AdonisJS supports sending HTML fragments, JSON objects, streams, and much more. The response instance can be accessed using the ctx.response
property.
Sending response
The simplest way to send a response is to return a value from the route handler.
import router from '@adonisjs/core/services/router'
router.get('/', async () => {
/** 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 from the route handler, you can use the response.send
method to explicitly set the response body. However, calling the response.send
method multiple times will overwrite the old body and only keep the latest one.
import router from '@adonisjs/core/services/router'
router.get('/', async ({ response }) => {
/** Plain string */
response.send('This is the homepage')
/** Html fragment */
response.send('<p> This is the homepage </p>')
/** JSON response */
response.send({ page: 'home' })
/** Converted to ISO string */
response.send(new Date())
})
A custom status code for the response can be set using the response.status
method.
response.status(200).send({ page: 'home' })
// Send empty 201 response
response.status(201).send('')
Streaming content
The response.stream
method allows piping a stream to the response. The method internally destroys the stream after it finishes.
The response.stream
method does not set the content-type
and the content-length
headers; you must set them explicitly before streaming the content.
import router from '@adonisjs/core/services/router'
router.get('/', async ({ response }) => {
const image = fs.createReadStream('./some-file.jpg')
response.stream(image)
})
A 500 status code is sent to the client in case of an error. However, you can customize the error code and message by defining a callback as the second parameter.
const image = fs.createReadStream('./some-file.jpg')
response.stream(image, () => {
const message = 'Unable to serve file. Try again'
const status = 400
return [message, status]
})
Downloading files
We recommend using the response.download
method over the response.stream
method when you want to stream files from the disk. This is because the download
method automatically sets the content-type
and the content-length
headers.
import app from '@adonisjs/core/services/app'
import router from '@adonisjs/core/services/router'
router.get('/uploads/:file', async ({ response, params }) => {
const filePath = app.makePath(`uploads/${params.file}`)
response.download(filePath)
})
Optionally, you can generate an Etag for the file contents. Using Etags will help the browser re-use the cached response from the previous request (if any).
const filePath = app.makePath(`uploads/${params.file}`)
const generateEtag = true
response.download(filePath, generateEtag)
Similar to the response.stream
method, you can send a custom error message and status code by defining a callback as the last parameter.
const filePath = app.makePath(`uploads/${params.file}`)
const generateEtag = true
response.download(filePath, generateEtag, (error) => {
if (error.code === 'ENOENT') {
return ['File does not exists', 404]
}
return ['Cannot download file', 400]
})
Force downloading files
The response.attachment
method is similar to the response.download
method, but it forces the browsers to save the file on the user's computer by setting the Content-Disposition header.
import app from '@adonisjs/core/services/app'
import router from '@adonisjs/core/services/router'
router.get('/uploads/:file', async ({ response, params }) => {
const filePath = app.makePath(`uploads/${params.file}`)
response.attachment(filePath, 'custom-filename.jpg')
})
Setting response status and headers
Setting status
You may set the response status using the response.status
method. Calling this method will override the existing response status (if any). However, you may use the response.safeStatus
method to set the status only when it is undefined
.
import router from '@adonisjs/core/services/router'
router.get('/', async ({ response }) => {
/**
* Sets the status to 200
*/
response.safeStatus(200)
/**
* Does not set the status since it
* is already set
*/
response.safeStatus(201)
})
Setting headers
You may set the response headers using the response.header
method. This method overrides the existing header value (if it already exists). However, you may use the response.safeHeader
method to set the header only when it is undefined
.
import router from '@adonisjs/core/services/router'
router.get('/', async ({ response }) => {
/**
* Defines the content-type header
*/
response.safeHeader('Content-type', 'text/html')
/**
* Does not set the content-type header since it
* is already set
*/
response.safeHeader('Content-type', 'text/html')
})
You can use the response.append
method to append values to existing header values.
response.append('Set-cookie', 'cookie-value')
The response.removeHeader
method removes the existing header.
response.removeHeader('Set-cookie')
Redirects
The response.redirect
method returns an instance of the Redirect class. The redirect class uses fluent API to construct the redirect URL.
The simplest way to perform a redirect is to call the redirect.toPath
method with the redirection path.
import app from '@adonisjs/core/services/app'
import router from '@adonisjs/core/services/router'
router.get('/posts', async ({ response }) => {
response.redirect().toPath('/articles')
})
The redirect class also allows constructing a URL from a pre-registered route. The redirect.toRoute
method accepts the route identifier as the first parameter and the route params as the second parameter.
import app from '@adonisjs/core/services/app'
import router from '@adonisjs/core/services/router'
router.get('/articles/:id', async () => {}).as('articles.show')
router.get('/posts/:id', async ({ response, params }) => {
response.redirect().toRoute('articles.show', { id: params.id })
})
Redirect back to the previous page
You might want to redirect the user to the previous page during form submissions in case of validation errors. You can do that using the redirect.back
method.
response.redirect().back()
Redirection status code
The default status for redirect responses is 302
; you can change it by calling the redirect.status
method.
response.redirect().status(301).toRoute('articles.show', { id: params.id })
Redirect with query string
You can use the withQs
method to append a query string to the redirect URL. The method accepts an object of a key-value pair and converts it to a string.
response.redirect().withQs({ page: 1, limit: 20 }).toRoute('articles.index')
To forward the query string from the current request URL, call the withQs
method without any parameters.
// Forward current URL query string
response.redirect().withQs().toRoute('articles.index')
When redirecting back to the previous page, the withQs
method will forward the query string of the previous page.
// Forward current URL query string
response.redirect().withQs().back()
Aborting request with an error
You may use the response.abort
method to end the request by raising an exception. The method will throw an E_HTTP_REQUEST_ABORTED
exception and trigger the exception handling flow.
router.get('posts/:id/edit', ({ response, auth, params }) => {
const post = await Post.findByOrFail(params.id)
if (!auth.user.can('editPost', post)) {
response.abort({ message: 'Cannot edit post' })
}
// continue with the rest of the logic
})
By default, the exception will create an HTTP response with a 400
status code. However, you can specify a custom status code as the second parameter.
response.abort({ message: 'Cannot edit post' }, 403)
Running actions after response finishes
You may listen for the event when Node.js finishes writing the response to the TCP socket using the response.onFinish
method. Under the hood, we use the on-finished package, so feel free to consult the package README file for an in-depth technical explanation.
router.get('posts', ({ response }) => {
response.onFinish(() => {
// cleanup logic
})
})
Accessing Node.js res
object
You can access the Node.js res object using the response.response
property.
router.get('posts', ({ response }) => {
console.log(response.response)
})
Response body serialization
The response body set using the response.send
method gets serialized to a string before it is written as response to the outgoing message stream.
Following is the list of supported data types and their serialization rules.
- Arrays and Objects are stringified using the safe stringify function. The method is similar to
JSON.stringify
but removes the circular references and serializesBigInt(s)
. - The number and boolean values are converted to a string.
- The instance of the Date class is converted to a string by calling the
toISOString
method. - Regular expressions and error objects are converted to a string by calling the
toString
method. - Any other data type results in an exception.
Content type inference
After serializing the response, the response class automatically infers and sets the content-type
and the content-length
headers.
Following is the list of rules we follow to set the content-type
header.
- Content type is set to
application/json
for arrays and objects. - It is set to
text/html
for HTML fragments. - JSONP responses are sent with the
text/javascript
content type. - The content type is set to
text/plain
for everything else.
Extending Response class
You can add custom properties to the Response class using macros or getters. Make sure to read the extending AdonisJS guide first if you are new to the concept of macros.
import { Response } from '@adonisjs/core/http'
Response.macro('property', function (this: Response) {
return value
})
Response.getter('property', function (this: Response) {
return value
})
Since the macros and getters are added at runtime, you must inform TypeScript about their types.
declare module '@adonisjs/core/http' {
export interface Response {
property: valueType
}
}