Skip to main content

status codes

Psychic encourages developers to think in terms of http status codes names, rather than manually setting status codes and rendering json. We provide methods which will automatically apply the given statuses, and will render json automatically.

import { OpenAPI } from '@rvoh/psychic'
import Place from '../../../models/Place.js'
import V1HostBaseController from './BaseController.js'

const openApiTags = ['places']

export default class V1HostPlacesController extends V1HostBaseController {
@OpenAPI(Place, {
status: 200,
tags: openApiTags,
description: 'Fetch multiple Places',
many: true,
serializerKey: 'summary',
})
public async index() {
const places = await this.currentHost
.associationQuery('places')
.preloadFor('summary')
.all()
this.ok(places) // 200
}

@OpenAPI(Place, {
status: 200,
tags: openApiTags,
description: 'Fetch a Place',
})
public async show() {
const place = await this.place()
this.ok(place) // 200
}

@OpenAPI(Place, {
status: 201,
tags: openApiTags,
description: 'Create a Place',
})
public async create() {
let place = await this.currentHost.createAssociation(
'places',
this.paramsFor(Place)
)
if (place.isPersisted) place = await place.loadFor('default').execute()
this.created(place) // 201
}

@OpenAPI(Place, {
status: 204,
tags: openApiTags,
description: 'Update a Place',
})
public async update() {
const place = await this.place()
await place.update(this.paramsFor(Place))
this.noContent() // 204
}

private async place() {
return await this.currentHost
.associationQuery('places')
.findOrFail(this.castParam('id', 'string'))
}
}

Thinking in terms of status codes helps you to write a clearly-defined API, and will produce more consistent, standardized patterns for handling various operations. In addition to providing status codes for handling successes, Psychic also provides methods for handling non-success statuses, like so:

export default class V1HostPlacesController extends V1HostBaseController {
@OpenAPI(Place, {
status: 200,
tags: openApiTags,
description: 'Fetch a Place',
})
public async show() {
const place = await this.place()
this.ok(place)
}

private async place() {
return await this.currentHost
.associationQuery('places')
.findOrFail(this.castParam('id', 'string'))
}
}

Note that in the example above, there is no return statement encapsulating the this.notFound call. This may seem like a mistake, since this request looks like it would render both a 404 and a 200 response. However, calling the notFound method carefully raises an exception, which is then rescued within the request thread and rendered with the appropriate status. This is done so that any code after the notFound will not be run, guaranteeing that you don't mistakenly double-render your responses, which is a common problem in express applications.

Implicit status codes

In addition to explicit status-code messages, Psychic provides reactive mechanisms to encapsulate Dream functionality, providing default status codes out the gate for you, such as in castParam and findOrFail:

export default class V1HostPlacesController extends V1HostBaseController {
@OpenAPI(Place, {
status: 200,
tags: openApiTags,
description: 'Fetch a Place',
})
public async show() {
// if the id is not present, or is not a valid string, this throws an error which Psychic converts to a 400
const id = this.castParam('id', 'string')

// if a place is not found by that id for this host, this throws an error which Psychic converts to a 404
const place = await this.currentHost
.associationQuery('places')
.findOrFail(id)

this.ok(place)
}
}