Skip to main content
version 1.0.0

Request Context

Introduction

Athenna provides an object inside all Http handlers called ctx. This property is implemented by the ContextContract interface.

The context object

In Athenna as you can see in the previous documentation page of Middlewares and Controllers we are always destructuring the ctx property and using like this:

Route.get('/welcome', ({ response }) => {
response.status(200).send({ hello: 'world' })
})

But is the same of doing this:

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

The ctx object is little different for each type of handlers, and we will see all the differences previous in this documentation page.

The request object

Athenna Request class provides an object-oriented way to interact with the current HTTP request being handled by your application as well as retrieve the ip, headers, body, and files that were submitted with the request.

The ip getter

With this getter you will be able to get the ip from where the requests was executed:

Route.get('/welcome', ({ request }) => {
console.log(request.ip) // 192.168.0.1

/*....*/
})

The method getter

With this getter you will be able to get the REST method of your request:

Route.get('/welcome', ({ request }) => {
console.log(request.method) // GET

/*....*/
})

The hostUrl getter

With this getter you will be able to get the host url of the request concatenating the host:port of your application and the originalUrl of the request:

Route.get('/welcome', ({ request }) => {
console.log(request.hostUrl) // http://localhost:1335/welcome

/*....*/
})

The baseUrl getter

With this getter you will be able to get the url of the route without the query params:

Route.get('/welcome', ({ request }) => {
console.log(request.baseUrl) // /welcome

/*....*/
})

The originalUrl getter

With this getter you will be able to get the original url with the query params:

Route.get('/welcome', ({ request }) => {
console.log(request.originalUrl) // /welcome?hello=world

/*....*/
})

The body, params, queries and headers getters

With these getters you will be able to retrieve all the data inside each one of then:

Route.post('/welcome/:id', ({ request }) => {
console.log(request.body) // { hello: 'world' }
console.log(request.params) // { id: '1' }
console.log(request.queries) // { world: 'hello' }
console.log(request.headers) // { 'content-type': 'application/json' }

/*....*/
})

The input and payload methods

With these methods you will be able to retrieve only one value per call from the request body:

Route.post('/welcome/:id', ({ request }) => {
const defaultValue = 'defaultValue'

console.log(request.input('hello'), 'found') // 'world'
console.log(request.input('not-found'), defaultValue) // 'defaultValue'

console.log(request.payload('hello'), defaultValue) // 'world'
console.log(request.payload('not-found'), defaultValue) // 'defaultValue'

/*....*/
})
tip

As you can see, you can use the second argument of this type of methods to set the default value if the key has not been found in your request.

You may even use "dot" syntax to retrieve values that are nested within JSON arrays / objects:

const name = request.input('user.name')

The only and except methods

If you need to retrieve a subset of the input data, you may use the only and except methods. Both of these methods accept a single array or a dynamic list of arguments:

const input = request.only('username', 'password')
const input = request.only(['username', 'password'])

const input = request.except('credit_card')
const input = request.except(['credit_card'])
warning

The only method returns all the key / value pairs that you request; however, it will not return key / value pairs that are not present on the request body.

The param, query and header methods

With these methods you will be able to retrieve only one value per call from the above methods. You can also set a second parameter that will set the default value if the first argument key doesn't exist:

Route.post('/welcome/:id', ({ request }) => {
const defaultValue = 'defaultValue'

console.log(request.param('id'), defaultValue) // '1'
console.log(request.param('not-found'), defaultValue) // 'defaultValue'

console.log(request.query('world'), defaultValue) // 'hello'
console.log(request.query('not-found'), defaultValue) // 'defaultValue'

console.log(request.header('content-type'), defaultValue) // 'application/json'
console.log(request.header('not-found'), defaultValue) // 'defaultValue'

/*....*/
})

The getFastifyRequest method

With this method you will be able to retrieve the vanilla Fastify request object to use more advanced getters and methods from Fastify:

Route.get('/welcome', ({ request }) => {
const fastifyRequest = request.getFastifyRequest()

/*....*/
})

The response object

Athenna Response class provides an object-oriented way to interact with the current HTTP response being handled by your application as well set a status code and return the response to the client.

The send and json methods

With these methods you are going to terminate the request sending a response body to the client. The send and json methods have the same signature, they do the exactly same thing, you can use both to return your request:

Route.get('/welcome', ({ response }) => {


response.json({ hello: 'world' })
// or -> response.send({ hello: 'world' })
})

The helmet method

With this method you are going to apply all the Helmet response headers in your response:

Route.get('/welcome', async ({ response }) => {
if (condition) {
// we apply the default options
await response.helmet()
} else {
// we apply customized options
await response.helmet({ frameguard: false })
}
})

The status method

With this method you are going to apply the status code of your response:

Route.get('/welcome', async ({ response }) => {
response.status(200).json({ hello: 'World' })
})

The header, safeHeader and removeHeader methods

With these methods you can set custom header for your response, the header method will subscribe the already set headers, the safeHeader will only register the header if the header is not yet registered and the removeHeader will remove a header from the response:

Route.get('/welcome', async ({ response }) => {
response.header('content-type', 'application/json')
response.safeHeader('content-type', 'application/json')
response.removeHeader('content-type')
})

The redirectTo method

With this method you can redirect your response to another url and with a different status code:

Route.get('/hello', ctx => ctx.response.status(200))

Route.get('/welcome', async ({ response }) => {
response.redirectTo('/hello', 200)
})

The getFastifyResponse method

With this method you will be able to retrieve the vanilla Fastify response object to use more advanced getters and methods from Fastify:

Route.get('/welcome', ({ response }) => {
const fastifyResponse = response.getFastifyResponse()

/*....*/
})

The params object

Athenna params is just a simple object that contains the actual HTTP params of the request that is being handled by your application.

The queries object

Athenna queries is just a simple object that contains the actual HTTP queries of the request that is being handled by your application.

The data object

Athenna data is just a simple object that you can use to set data inside to transfer between middlewares. This is really useful for some cases. Let's see an example setting default pagination values if client has not sent page and limit:

export class PaginationMiddleware {
/**
* Handle method is executed before the request gets in your controller.
*
* @param {import('@athenna/http').HandleContextContract} ctx
*/
async handle({ request, data }) {
const page = request.queries.page ? parseInt(request.queries.page) : 0
const limit = request.queries.limit ? parseInt(request.queries.limit) : 10
const resourceUrl = `${Config.get('http.domain')}${request.baseUrl}`

data.pagination = {
page,
limit,
resourceUrl,
}
}
}

And now is very simple to get this pagination object inside your handler:

Route.get('/products', ({ response, data }) => {
return response.send({ paginationObj: data.pagination })
}).middleware('PaginationMiddleware')

The context object in middlewares

Handle middleware context

In handle method of middlewares Athenna set the HandleContextContract. This ctx is the same of ContextContract.

Intercept middleware context

In intercept method of middlewares Athenna set the InterceptContextContract. This ctx is quite the same of ContextContract, but it has an additional properties body and status. You should always return the intercepted response body in intercept middlewares.

Terminate middleware context

In terminate method of middlewares Athenna set the TerminateContextContract. This ctx is quite the same of ContextContract, but it has an additional properties body, status and responseTime.