Hash Responses
Expose plain hashes instead of ActiveRecord models
API Definition
config/apis/lazy_cow.rb
rb
# frozen_string_literal: true
Apiwork::API.define '/lazy_cow' do
key_format :camel
export :openapi
export :apiwork
resource :status, only: [] do
collection do
get :health
get :stats
end
end
endContracts
app/contracts/lazy_cow/status_contract.rb
rb
# frozen_string_literal: true
module LazyCow
class StatusContract < Apiwork::Contract::Base
action :health do
response do
body do
string :status
datetime :timestamp
string :version
end
end
end
action :stats do
response do
body do
integer :users_count
integer :posts_count
integer :uptime_seconds
end
end
end
end
endControllers
app/controllers/lazy_cow/statuses_controller.rb
rb
# frozen_string_literal: true
module LazyCow
class StatusesController < ApplicationController
def health
expose(
{
status: 'ok',
timestamp: Time.current,
version: '1.0.0',
},
)
end
def stats
expose(
{
posts_count: 5678,
uptime_seconds: 86_400,
users_count: 1234,
},
)
end
end
endRequest Examples
Health check
Request
http
GET /lazy_cow/status/healthResponse 200
json
{
"status": "ok",
"timestamp": "2026-04-01T12:00:00.000Z",
"version": "1.0.0"
}System statistics
Request
http
GET /lazy_cow/status/statsResponse 200
json
{
"postsCount": 5678,
"uptimeSeconds": 86400,
"usersCount": 1234
}Exports
OpenAPI
yml
---
openapi: 3.1.0
info:
title: "/lazy_cow"
version: 1.0.0
paths:
"/status/health":
get:
operationId: statusHealth
responses:
'200':
content:
application/json:
schema:
properties:
status:
type: string
timestamp:
type: string
format: date-time
version:
type: string
type: object
required:
- status
- timestamp
- version
description: ''
"/status/stats":
get:
operationId: statusStats
responses:
'200':
content:
application/json:
schema:
properties:
postsCount:
type: integer
uptimeSeconds:
type: integer
usersCount:
type: integer
type: object
required:
- postsCount
- uptimeSeconds
- usersCount
description: ''
components:
schemas: {}Apiwork
json
{
"base_path": "/lazy_cow",
"enums": [],
"error_codes": [],
"fingerprint": "fc4971696fedaab0",
"info": null,
"locales": [],
"resources": [
{
"actions": [
{
"name": "status.health",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/status/health",
"raises": [],
"request": {
"body": [],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "status",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "timestamp",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "datetime",
"example": null
},
{
"name": "version",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "status.stats",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/status/stats",
"raises": [],
"request": {
"body": [],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "postsCount",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "uptimeSeconds",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "usersCount",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
}
],
"identifier": "status",
"name": "status",
"parent_identifiers": [],
"path": "status",
"resources": [],
"scope": "status"
}
],
"types": []
}Codegen
TypeScript
ts
export * from './status';ts
export type StatusHealthMethod = 'GET';
export type StatusHealthPath = '/status/health';
export type StatusHealthResponseBody = {
status: string;
timestamp: string;
version: string;
};
export interface StatusHealthResponse {
body: StatusHealthResponseBody;
}
export interface StatusHealth {
method: StatusHealthMethod;
path: StatusHealthPath;
response: StatusHealthResponse;
}
export type StatusStatsMethod = 'GET';
export type StatusStatsPath = '/status/stats';
export type StatusStatsResponseBody = {
postsCount: number;
uptimeSeconds: number;
usersCount: number;
};
export interface StatusStatsResponse {
body: StatusStatsResponseBody;
}
export interface StatusStats {
method: StatusStatsMethod;
path: StatusStatsPath;
response: StatusStatsResponse;
}Zod
ts
export * from './status';ts
import * as z from 'zod';
export const StatusHealthResponseBodySchema = z.object({
status: z.string(),
timestamp: z.iso.datetime(),
version: z.string(),
});
export const StatusStatsResponseBodySchema = z.object({
postsCount: z.number().int(),
uptimeSeconds: z.number().int(),
usersCount: z.number().int(),
});
export type StatusHealthMethod = 'GET';
export type StatusHealthPath = '/status/health';
export type StatusHealthResponseBody = {
status: string;
timestamp: string;
version: string;
};
export interface StatusHealthResponse {
body: StatusHealthResponseBody;
}
export interface StatusHealth {
method: StatusHealthMethod;
path: StatusHealthPath;
response: StatusHealthResponse;
}
export type StatusStatsMethod = 'GET';
export type StatusStatsPath = '/status/stats';
export type StatusStatsResponseBody = {
postsCount: number;
uptimeSeconds: number;
usersCount: number;
};
export interface StatusStatsResponse {
body: StatusStatsResponseBody;
}
export interface StatusStats {
method: StatusStatsMethod;
path: StatusStatsPath;
response: StatusStatsResponse;
}Sorbus
ts
import type { StatusOperationTree } from './endpoints';
import { createClientFactory } from 'sorbus';
import { contract } from './contract';
export interface Client {
status: StatusOperationTree;
}
export const createClient = createClientFactory<Client>(contract);ts
import { status } from './endpoints';
export const contract = {
endpoints: {
status,
},
} as const;ts
export * from './status';ts
import type { Operation } from 'sorbus';
import * as z from 'zod';
export const StatusHealthResponseBodySchema = z.object({
status: z.string(),
timestamp: z.iso.datetime(),
version: z.string(),
});
export const StatusStatsResponseBodySchema = z.object({
postsCount: z.number().int(),
uptimeSeconds: z.number().int(),
usersCount: z.number().int(),
});
export type StatusHealthMethod = 'GET';
export type StatusHealthPath = '/status/health';
export type StatusHealthResponseBody = {
status: string;
timestamp: string;
version: string;
};
export interface StatusHealthResponse {
body: StatusHealthResponseBody;
}
export interface StatusHealth {
method: StatusHealthMethod;
path: StatusHealthPath;
response: StatusHealthResponse;
}
export type StatusStatsMethod = 'GET';
export type StatusStatsPath = '/status/stats';
export type StatusStatsResponseBody = {
postsCount: number;
uptimeSeconds: number;
usersCount: number;
};
export interface StatusStatsResponse {
body: StatusStatsResponseBody;
}
export interface StatusStats {
method: StatusStatsMethod;
path: StatusStatsPath;
response: StatusStatsResponse;
}
export const status = {
health: {
method: 'GET',
path: '/status/health',
response: {
body: StatusHealthResponseBodySchema,
},
},
stats: {
method: 'GET',
path: '/status/stats',
response: {
body: StatusStatsResponseBodySchema,
},
},
} as const;
export interface StatusOperationTree {
health: Operation<StatusHealth>;
stats: Operation<StatusStats>;
}