Skip to main content

Anatomy of Request handlers

Basics​

Here's a very basic request handler:

builder.get(async () => {
return {
result: 'success' as const,
statusCode: 200 as const,
message: "Hey!"
}
})
  • The builder is the request handler builder that you've created during the installation.
  • The .get sets the final middleware with the appropriate HTTP method.
    • The supported methods are: get, post, put, patch, delete
  • The async () => { /* ... */ } is the function where your controller logic leaves
    • The return type should contain these fields:
      • result: a unique const indentifier for each possible responses.
      • statusCode: the HTTP status code. For client-side typing it needs to be const as well.

Response factory methods​

Adding result and statusCode manually can be noisy. It's advised to create factory methods for common response types like success.

success is included in the package:

import { success } from '@cuple/server'

builder.get(async () => {
return success({ message: "Hey!" })
})

Cuple needs validationErrors internally, so you can access these as well:

import { validationError } from '@cuple/server'

builder.get(async () => {
return validationError({
message: "We found some issues with your request"
})
})
import { zodValidationError } from '@cuple/server'

builder.get(async () => {
return zodValidationError([{
code: "custom",
path: ["password2"],
message: "Passwords do not match.",
}]);
})

And there's also a generic apiResponse method, so you don't have to keep track the shape of the return type:

import { apiResponse } from '@cuple/server'

builder.get(async () => {
return apiResponse("not-found-error", 404, {
message: "Resource not found",
});
})

Example for custom factory method:

export function notFoundError(others?: {message: string}) {
return apiResponse("not-found-error", 404, {
message: others?.message || "Resource not found",
});
}

Access Request and Response object​

The first argument is an object containing 3 elements:

  • req: Express' Request object
  • res: Express' Response object
  • data: custom data from the middlewares
builder
.chain(auth)
.bodySchema(
z.object({
oldPassword: z.string(),
password1: z.string().min(6),
password2: z.string().min(6),
}),
)
.post(async ({ req, res, data }) => {

// You can access request object
req.body

// but you have better, type-safe way to deal with the incoming data
data.body.password1

})