Skip to content

Using Resora Outside H3/Express (Non-Connect Frameworks)

Resora works as a pure transformation layer, even when your framework does not expose a Connect-style response object.

In these frameworks, use Resora to build a JSON-safe payload or a complete response snapshot, then return or send it with your framework's native response API.

Core Pattern

ts
import { Resource } from 'resora';

const body = new Resource(user).getBody();

// send `body` using your framework's response mechanism

This approach works everywhere because it does not rely on .response() transport binding.

Status And Headers

Use toResponseData() when the adapter needs Resora's body, status, and headers:

ts
const response = new Resource(user)
  .response()
  .setStatusCode(201)
  .setHeaders({
    'X-Resource': 'user',
  })
  .toResponseData();

return framework.send({
  body: response.body,
  status: response.status,
  headers: response.headers,
});

The snapshot is a plain object rather than a thenable. This makes it safe to return from async controller and adapter code without losing status or header metadata.

Why this works

Resora separates:

  • Transformation: resource shape, metadata, conditional attributes
  • Transport: headers/status/cookies at framework response layer

When Connect-style response objects are unavailable, keep transport in your framework and use Resora for transformation only.

ts
import { Resource } from 'resora';

fastify.get('/users/:id', async (request, reply) => {
  const user = await findUser(request.params.id);

  const body = new Resource(user).withMeta({ traceId: request.id }).getBody();

  return reply.code(200).send(body);
});

Return value style

ts
import { Resource } from 'resora'

@Get(':id')
async findOne(@Param('id') id: string) {
  const user = await this.usersService.findOne(id)

  return new Resource(user)
    .withMeta({ source: 'users-controller' })
    .getBody()
}

@Res() style

ts
import { Resource } from 'resora'

@Get(':id')
async findOne(@Param('id') id: string, @Res() res: any) {
  const user = await this.usersService.findOne(id)

  const body = new Resource(user).getBody()

  return res.status(200).json(body)
}

Collections and Pagination in Non-Connect Frameworks

ts
import { ResourceCollection } from 'resora';

const body = new ResourceCollection({
  data: users,
  pagination: {
    currentPage: 1,
    lastPage: 10,
    total: 100,
    perPage: 10,
    path: '/users',
  },
}).getBody();

Then send body with your framework's normal response API.

Conditional Attributes Still Work

ts
class UserResource extends Resource {
  data() {
    return {
      id: this.id,
      email: this.whenNotNull(this.email),
      role: this.when(this.isAdmin, 'admin'),
    };
  }
}

All conditional helpers work the same because they are transformation features, not transport features.

Choosing The Integration Style

  • Use .getBody() when only the transformed JSON payload is needed.
  • Use .response().toResponseData() when status or headers should travel with the payload.
  • Use .response(rawResponse) or await the response when Resora should dispatch directly through a supported mutable response object.