Write Modes
Control which fields are accepted on create vs update
API Definition
config/apis/sharp_hawk.rb
rb
# frozen_string_literal: true
Apiwork::API.define '/sharp_hawk' do
key_format :camel
export :openapi
export :apiwork
resources :accounts
endModels
app/models/sharp_hawk/account.rb
rb
# frozen_string_literal: true
module SharpHawk
class Account < ApplicationRecord
validates :email, :name, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| created_at | datetime | ||
| string | |||
| name | string | ||
| role | string | member | |
| updated_at | datetime | ||
| verified | boolean |
Representations
app/representations/sharp_hawk/account_representation.rb
rb
# frozen_string_literal: true
module SharpHawk
class AccountRepresentation < Apiwork::Representation::Base
attribute :id
attribute :email, writable: :create
attribute :name, writable: true
attribute :role, writable: :update
attribute :verified, writable: :update
attribute :created_at
attribute :updated_at
end
endContracts
app/contracts/sharp_hawk/account_contract.rb
rb
# frozen_string_literal: true
module SharpHawk
class AccountContract < Apiwork::Contract::Base
representation AccountRepresentation
end
endControllers
app/controllers/sharp_hawk/accounts_controller.rb
rb
# frozen_string_literal: true
module SharpHawk
class AccountsController < ApplicationController
before_action :set_account, only: %i[show update destroy]
def index
accounts = Account.all
expose accounts
end
def show
expose account
end
def create
account = Account.create(contract.body[:account])
expose account
end
def update
account.update(contract.body[:account])
expose account
end
def destroy
account.destroy
expose account
end
private
attr_reader :account
def set_account
@account = Account.find(params[:id])
end
end
endRequest Examples
Create account
Request
http
POST /sharp_hawk/accounts
Content-Type: application/json
{
"account": {
"email": "alice@example.com",
"name": "Alice Johnson"
}
}Response 201
json
{
"account": {
"id": "8324758d-5d34-504c-922a-694658537a93",
"email": "alice@example.com",
"name": "Alice Johnson",
"role": "member",
"verified": false,
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
}Update account
Request
http
PATCH /sharp_hawk/accounts/8324758d-5d34-504c-922a-694658537a93
Content-Type: application/json
{
"account": {
"name": "Alice Smith",
"role": "admin",
"verified": true
}
}Response 200
json
{
"account": {
"id": "8324758d-5d34-504c-922a-694658537a93",
"email": "alice@example.com",
"name": "Alice Smith",
"role": "admin",
"verified": true,
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
}Show account
Request
http
GET /sharp_hawk/accounts/8324758d-5d34-504c-922a-694658537a93Response 200
json
{
"account": {
"id": "8324758d-5d34-504c-922a-694658537a93",
"email": "alice@example.com",
"name": "Alice Johnson",
"role": "admin",
"verified": true,
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
}Exports
OpenAPI
yml
---
openapi: 3.1.0
info:
title: "/sharp_hawk"
version: 1.0.0
paths:
"/accounts":
get:
operationId: accountsIndex
parameters:
- in: query
name: page
required: false
schema:
"$ref": "#/components/schemas/accountPage"
responses:
'200':
content:
application/json:
schema:
properties:
accounts:
items:
"$ref": "#/components/schemas/account"
type: array
meta:
properties: {}
type: object
pagination:
"$ref": "#/components/schemas/offsetPagination"
type: object
required:
- accounts
- pagination
description: ''
post:
operationId: accountsCreate
requestBody:
content:
application/json:
schema:
properties:
account:
"$ref": "#/components/schemas/accountCreatePayload"
type: object
required:
- account
required: true
responses:
'200':
content:
application/json:
schema:
properties:
account:
"$ref": "#/components/schemas/account"
meta:
properties: {}
type: object
type: object
required:
- account
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
"/accounts/{id}":
get:
operationId: accountsShow
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
content:
application/json:
schema:
properties:
account:
"$ref": "#/components/schemas/account"
meta:
properties: {}
type: object
type: object
required:
- account
description: ''
patch:
operationId: accountsUpdate
parameters:
- in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
properties:
account:
"$ref": "#/components/schemas/accountUpdatePayload"
type: object
required:
- account
required: true
responses:
'200':
content:
application/json:
schema:
properties:
account:
"$ref": "#/components/schemas/account"
meta:
properties: {}
type: object
type: object
required:
- account
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
delete:
operationId: accountsDestroy
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'204':
description: ''
components:
schemas:
account:
properties:
createdAt:
type: string
format: date-time
email:
type: string
id:
type: string
name:
type: string
role:
type: string
updatedAt:
type: string
format: date-time
verified:
type: boolean
type: object
required:
- createdAt
- email
- id
- name
- role
- updatedAt
- verified
accountCreatePayload:
properties:
email:
type: string
name:
type: string
type: object
required:
- email
- name
accountPage:
properties:
number:
type: integer
minimum: 1
size:
type: integer
minimum: 1
maximum: 100
type: object
accountUpdatePayload:
properties:
name:
type: string
role:
type: string
verified:
type: boolean
type: object
error:
properties:
issues:
items:
"$ref": "#/components/schemas/errorIssue"
type: array
layer:
enum:
- http
- contract
- domain
type: string
type: object
required:
- issues
- layer
errorIssue:
properties:
code:
type: string
detail:
type: string
meta:
properties: {}
type: object
path:
items:
oneOf:
- type: string
- type: integer
type: array
pointer:
type: string
type: object
required:
- code
- detail
- meta
- path
- pointer
offsetPagination:
properties:
current:
type: integer
items:
type: integer
next:
type:
- integer
- 'null'
prev:
type:
- integer
- 'null'
total:
type: integer
type: object
required:
- current
- items
- totalApiwork
json
{
"base_path": "/sharp_hawk",
"enums": [
{
"deprecated": false,
"description": null,
"example": null,
"name": "error_layer",
"scope": null,
"values": [
"http",
"contract",
"domain"
]
}
],
"error_codes": [
{
"description": "Unprocessable Entity",
"name": "unprocessable_entity",
"status": 422
}
],
"fingerprint": "3f3aa02af6f1b526",
"info": null,
"locales": [],
"resources": [
{
"actions": [
{
"name": "accounts.index",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/accounts",
"raises": [],
"request": {
"body": [],
"description": null,
"query": [
{
"name": "page",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "account_page"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "accounts",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "account"
}
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
},
{
"name": "pagination",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "offset_pagination"
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "accounts.show",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/accounts/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "account",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "account"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "accounts.create",
"deprecated": false,
"description": null,
"method": "post",
"operation_id": null,
"path": "/accounts",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "account",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "account_create_payload"
}
],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "account",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "account"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "accounts.update",
"deprecated": false,
"description": null,
"method": "patch",
"operation_id": null,
"path": "/accounts/:id",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "account",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "account_update_payload"
}
],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "account",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "account"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "accounts.destroy",
"deprecated": false,
"description": null,
"method": "delete",
"operation_id": null,
"path": "/accounts/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": []
},
"response": {
"body": null,
"description": null,
"no_content": true
},
"summary": null,
"tags": []
}
],
"identifier": "accounts",
"name": "accounts",
"parent_identifiers": [],
"path": "accounts",
"resources": [],
"scope": "account"
}
],
"types": [
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "account",
"scope": "account",
"type": "object",
"extends": [],
"shape": [
{
"name": "createdAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "datetime",
"example": null
},
{
"name": "email",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "id",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "name",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "role",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "updatedAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "datetime",
"example": null
},
{
"name": "verified",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "boolean",
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "account_create_payload",
"scope": "account",
"type": "object",
"extends": [],
"shape": [
{
"name": "email",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "name",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "account_page",
"scope": "account",
"type": "object",
"extends": [],
"shape": [
{
"name": "number",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": 1
},
{
"name": "size",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "integer",
"example": null,
"format": null,
"max": 100,
"min": 1
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "account_update_payload",
"scope": "account",
"type": "object",
"extends": [],
"shape": [
{
"name": "name",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "role",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "verified",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "boolean",
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "error_issue",
"scope": null,
"type": "object",
"extends": [],
"shape": [
{
"name": "code",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "detail",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "object",
"partial": false,
"shape": []
},
{
"name": "path",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "union",
"discriminator": "",
"variants": [
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
}
]
}
},
{
"name": "pointer",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "offset_pagination",
"scope": null,
"type": "object",
"extends": [],
"shape": [
{
"name": "current",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "items",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "next",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "prev",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "total",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "integer",
"example": null,
"format": null,
"max": null,
"min": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "error",
"scope": null,
"type": "object",
"extends": [],
"shape": [
{
"name": "issues",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "error_issue"
}
},
{
"name": "layer",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "error_layer"
}
]
}
]
}Codegen
TypeScript
ts
export type ErrorLayer = 'http' | 'contract' | 'domain';
export interface ErrorIssue {
code: string;
detail: string;
meta: Record<string, unknown>;
path: string | number[];
pointer: string;
}
export interface OffsetPagination {
current: number;
items: number;
next?: number | null;
prev?: number | null;
total: number;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
export interface Account {
createdAt: string;
email: string;
id: string;
name: string;
role: string;
updatedAt: string;
verified: boolean;
}
export interface AccountCreatePayload {
email: string;
name: string;
}
export interface AccountPage {
number?: number;
size?: number;
}
export interface AccountUpdatePayload {
name?: string;
role?: string;
verified?: boolean;
}ts
export * from './account';ts
import type { OffsetPagination } from '../api';
import type {
Account,
AccountCreatePayload,
AccountPage,
AccountUpdatePayload,
} from '../domains/account';
export type AccountsIndexMethod = 'GET';
export type AccountsIndexPath = '/accounts';
export interface AccountsIndexRequestQuery {
page?: AccountPage;
}
export type AccountsIndexResponseBody = {
accounts: Account[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface AccountsIndexRequest {
query: AccountsIndexRequestQuery;
}
export interface AccountsIndexResponse {
body: AccountsIndexResponseBody;
}
export interface AccountsIndex {
method: AccountsIndexMethod;
path: AccountsIndexPath;
request: AccountsIndexRequest;
response: AccountsIndexResponse;
}
export type AccountsShowMethod = 'GET';
export type AccountsShowPath = '/accounts/:id';
export interface AccountsShowPathParams {
id: string;
}
export type AccountsShowResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsShowResponse {
body: AccountsShowResponseBody;
}
export interface AccountsShow {
method: AccountsShowMethod;
path: AccountsShowPath;
pathParams: AccountsShowPathParams;
response: AccountsShowResponse;
}
export type AccountsCreateMethod = 'POST';
export type AccountsCreatePath = '/accounts';
export interface AccountsCreateRequestBody {
account: AccountCreatePayload;
}
export type AccountsCreateResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsCreateRequest {
body: AccountsCreateRequestBody;
}
export interface AccountsCreateResponse {
body: AccountsCreateResponseBody;
}
export type AccountsCreateErrors = 422;
export interface AccountsCreate {
errors: AccountsCreateErrors;
method: AccountsCreateMethod;
path: AccountsCreatePath;
request: AccountsCreateRequest;
response: AccountsCreateResponse;
}
export type AccountsUpdateMethod = 'PATCH';
export type AccountsUpdatePath = '/accounts/:id';
export interface AccountsUpdatePathParams {
id: string;
}
export interface AccountsUpdateRequestBody {
account: AccountUpdatePayload;
}
export type AccountsUpdateResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsUpdateRequest {
body: AccountsUpdateRequestBody;
}
export interface AccountsUpdateResponse {
body: AccountsUpdateResponseBody;
}
export type AccountsUpdateErrors = 422;
export interface AccountsUpdate {
errors: AccountsUpdateErrors;
method: AccountsUpdateMethod;
path: AccountsUpdatePath;
pathParams: AccountsUpdatePathParams;
request: AccountsUpdateRequest;
response: AccountsUpdateResponse;
}
export type AccountsDestroyMethod = 'DELETE';
export type AccountsDestroyPath = '/accounts/:id';
export interface AccountsDestroyPathParams {
id: string;
}
export interface AccountsDestroy {
method: AccountsDestroyMethod;
path: AccountsDestroyPath;
pathParams: AccountsDestroyPathParams;
}ts
export * from './accounts';Zod
ts
import * as z from 'zod';
export const ErrorLayerSchema = z.enum(['contract', 'domain', 'http']);
export const ErrorIssueSchema = z.object({
code: z.string(),
detail: z.string(),
meta: z.record(z.string(), z.unknown()),
path: z.array(z.union([z.string(), z.number().int()])),
pointer: z.string(),
});
export const OffsetPaginationSchema = z.object({
current: z.number().int(),
items: z.number().int(),
next: z.number().int().nullable().optional(),
prev: z.number().int().nullable().optional(),
total: z.number().int(),
});
export const ErrorSchema = z.object({
issues: z.array(ErrorIssueSchema),
layer: ErrorLayerSchema,
});
export type ErrorLayer = 'contract' | 'domain' | 'http';
export interface ErrorIssue {
code: string;
detail: string;
meta: Record<string, unknown>;
path: string | number[];
pointer: string;
}
export interface OffsetPagination {
current: number;
items: number;
next?: number | null;
prev?: number | null;
total: number;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import * as z from 'zod';
export const AccountSchema = z.object({
createdAt: z.iso.datetime(),
email: z.string(),
id: z.string(),
name: z.string(),
role: z.string(),
updatedAt: z.iso.datetime(),
verified: z.boolean(),
});
export const AccountCreatePayloadSchema = z.object({
email: z.string(),
name: z.string(),
});
export const AccountPageSchema = z.object({
number: z.number().int().min(1).optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const AccountUpdatePayloadSchema = z.object({
name: z.string().optional(),
role: z.string().optional(),
verified: z.boolean().optional(),
});
export interface Account {
createdAt: string;
email: string;
id: string;
name: string;
role: string;
updatedAt: string;
verified: boolean;
}
export interface AccountCreatePayload {
email: string;
name: string;
}
export interface AccountPage {
number?: number;
size?: number;
}
export interface AccountUpdatePayload {
name?: string;
role?: string;
verified?: boolean;
}ts
export * from './account';ts
import type { OffsetPagination } from '../api';
import type {
Account,
AccountCreatePayload,
AccountPage,
AccountUpdatePayload,
} from '../domains/account';
import * as z from 'zod';
import { OffsetPaginationSchema } from '../api';
import {
AccountCreatePayloadSchema,
AccountPageSchema,
AccountSchema,
AccountUpdatePayloadSchema,
} from '../domains/account';
export const AccountsIndexRequestQuerySchema = z.object({
page: AccountPageSchema.optional(),
});
export const AccountsIndexResponseBodySchema = z.object({
accounts: z.array(AccountSchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: OffsetPaginationSchema,
});
export const AccountsShowPathParamsSchema = z.object({ id: z.string() });
export const AccountsShowResponseBodySchema = z.object({
account: AccountSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const AccountsCreateRequestBodySchema = z.object({
account: AccountCreatePayloadSchema,
});
export const AccountsCreateResponseBodySchema = z.object({
account: AccountSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const AccountsUpdatePathParamsSchema = z.object({ id: z.string() });
export const AccountsUpdateRequestBodySchema = z.object({
account: AccountUpdatePayloadSchema,
});
export const AccountsUpdateResponseBodySchema = z.object({
account: AccountSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const AccountsDestroyPathParamsSchema = z.object({ id: z.string() });
export type AccountsIndexMethod = 'GET';
export type AccountsIndexPath = '/accounts';
export interface AccountsIndexRequestQuery {
page?: AccountPage;
}
export type AccountsIndexResponseBody = {
accounts: Account[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface AccountsIndexRequest {
query: AccountsIndexRequestQuery;
}
export interface AccountsIndexResponse {
body: AccountsIndexResponseBody;
}
export interface AccountsIndex {
method: AccountsIndexMethod;
path: AccountsIndexPath;
request: AccountsIndexRequest;
response: AccountsIndexResponse;
}
export type AccountsShowMethod = 'GET';
export type AccountsShowPath = '/accounts/:id';
export interface AccountsShowPathParams {
id: string;
}
export type AccountsShowResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsShowResponse {
body: AccountsShowResponseBody;
}
export interface AccountsShow {
method: AccountsShowMethod;
path: AccountsShowPath;
pathParams: AccountsShowPathParams;
response: AccountsShowResponse;
}
export type AccountsCreateMethod = 'POST';
export type AccountsCreatePath = '/accounts';
export interface AccountsCreateRequestBody {
account: AccountCreatePayload;
}
export type AccountsCreateResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsCreateRequest {
body: AccountsCreateRequestBody;
}
export interface AccountsCreateResponse {
body: AccountsCreateResponseBody;
}
export type AccountsCreateErrors = 422;
export interface AccountsCreate {
errors: AccountsCreateErrors;
method: AccountsCreateMethod;
path: AccountsCreatePath;
request: AccountsCreateRequest;
response: AccountsCreateResponse;
}
export type AccountsUpdateMethod = 'PATCH';
export type AccountsUpdatePath = '/accounts/:id';
export interface AccountsUpdatePathParams {
id: string;
}
export interface AccountsUpdateRequestBody {
account: AccountUpdatePayload;
}
export type AccountsUpdateResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsUpdateRequest {
body: AccountsUpdateRequestBody;
}
export interface AccountsUpdateResponse {
body: AccountsUpdateResponseBody;
}
export type AccountsUpdateErrors = 422;
export interface AccountsUpdate {
errors: AccountsUpdateErrors;
method: AccountsUpdateMethod;
path: AccountsUpdatePath;
pathParams: AccountsUpdatePathParams;
request: AccountsUpdateRequest;
response: AccountsUpdateResponse;
}
export type AccountsDestroyMethod = 'DELETE';
export type AccountsDestroyPath = '/accounts/:id';
export interface AccountsDestroyPathParams {
id: string;
}
export interface AccountsDestroy {
method: AccountsDestroyMethod;
path: AccountsDestroyPath;
pathParams: AccountsDestroyPathParams;
}ts
export * from './accounts';Sorbus
ts
import * as z from 'zod';
export const ErrorLayerSchema = z.enum(['contract', 'domain', 'http']);
export const ErrorIssueSchema = z.object({
code: z.string(),
detail: z.string(),
meta: z.record(z.string(), z.unknown()),
path: z.array(z.union([z.string(), z.number().int()])),
pointer: z.string(),
});
export const OffsetPaginationSchema = z.object({
current: z.number().int(),
items: z.number().int(),
next: z.number().int().nullable().optional(),
prev: z.number().int().nullable().optional(),
total: z.number().int(),
});
export const ErrorSchema = z.object({
issues: z.array(ErrorIssueSchema),
layer: ErrorLayerSchema,
});
export type ErrorLayer = 'contract' | 'domain' | 'http';
export interface ErrorIssue {
code: string;
detail: string;
meta: Record<string, unknown>;
path: string | number[];
pointer: string;
}
export interface OffsetPagination {
current: number;
items: number;
next?: number | null;
prev?: number | null;
total: number;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import type { AccountsOperationTree } from './endpoints';
import { createClientFactory } from 'sorbus';
import { contract } from './contract';
export interface Client {
accounts: AccountsOperationTree;
}
export const createClient = createClientFactory<Client>(contract);ts
import { ErrorSchema } from './api';
import { accounts } from './endpoints';
export const contract = {
endpoints: {
accounts,
},
error: ErrorSchema,
} as const;ts
import * as z from 'zod';
export const AccountSchema = z.object({
createdAt: z.iso.datetime(),
email: z.string(),
id: z.string(),
name: z.string(),
role: z.string(),
updatedAt: z.iso.datetime(),
verified: z.boolean(),
});
export const AccountCreatePayloadSchema = z.object({
email: z.string(),
name: z.string(),
});
export const AccountPageSchema = z.object({
number: z.number().int().min(1).optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const AccountUpdatePayloadSchema = z.object({
name: z.string().optional(),
role: z.string().optional(),
verified: z.boolean().optional(),
});
export interface Account {
createdAt: string;
email: string;
id: string;
name: string;
role: string;
updatedAt: string;
verified: boolean;
}
export interface AccountCreatePayload {
email: string;
name: string;
}
export interface AccountPage {
number?: number;
size?: number;
}
export interface AccountUpdatePayload {
name?: string;
role?: string;
verified?: boolean;
}ts
export * from './account';ts
import type { Operation } from 'sorbus';
import type { OffsetPagination } from '../api';
import type {
Account,
AccountCreatePayload,
AccountPage,
AccountUpdatePayload,
} from '../domains/account';
import * as z from 'zod';
import { OffsetPaginationSchema } from '../api';
import {
AccountCreatePayloadSchema,
AccountPageSchema,
AccountSchema,
AccountUpdatePayloadSchema,
} from '../domains/account';
export const AccountsIndexRequestQuerySchema = z.object({
page: AccountPageSchema.optional(),
});
export const AccountsIndexResponseBodySchema = z.object({
accounts: z.array(AccountSchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: OffsetPaginationSchema,
});
export const AccountsShowPathParamsSchema = z.object({ id: z.string() });
export const AccountsShowResponseBodySchema = z.object({
account: AccountSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const AccountsCreateRequestBodySchema = z.object({
account: AccountCreatePayloadSchema,
});
export const AccountsCreateResponseBodySchema = z.object({
account: AccountSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const AccountsUpdatePathParamsSchema = z.object({ id: z.string() });
export const AccountsUpdateRequestBodySchema = z.object({
account: AccountUpdatePayloadSchema,
});
export const AccountsUpdateResponseBodySchema = z.object({
account: AccountSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const AccountsDestroyPathParamsSchema = z.object({ id: z.string() });
export type AccountsIndexMethod = 'GET';
export type AccountsIndexPath = '/accounts';
export interface AccountsIndexRequestQuery {
page?: AccountPage;
}
export type AccountsIndexResponseBody = {
accounts: Account[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface AccountsIndexRequest {
query: AccountsIndexRequestQuery;
}
export interface AccountsIndexResponse {
body: AccountsIndexResponseBody;
}
export interface AccountsIndex {
method: AccountsIndexMethod;
path: AccountsIndexPath;
request: AccountsIndexRequest;
response: AccountsIndexResponse;
}
export type AccountsShowMethod = 'GET';
export type AccountsShowPath = '/accounts/:id';
export interface AccountsShowPathParams {
id: string;
}
export type AccountsShowResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsShowResponse {
body: AccountsShowResponseBody;
}
export interface AccountsShow {
method: AccountsShowMethod;
path: AccountsShowPath;
pathParams: AccountsShowPathParams;
response: AccountsShowResponse;
}
export type AccountsCreateMethod = 'POST';
export type AccountsCreatePath = '/accounts';
export interface AccountsCreateRequestBody {
account: AccountCreatePayload;
}
export type AccountsCreateResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsCreateRequest {
body: AccountsCreateRequestBody;
}
export interface AccountsCreateResponse {
body: AccountsCreateResponseBody;
}
export type AccountsCreateErrors = 422;
export interface AccountsCreate {
errors: AccountsCreateErrors;
method: AccountsCreateMethod;
path: AccountsCreatePath;
request: AccountsCreateRequest;
response: AccountsCreateResponse;
}
export type AccountsUpdateMethod = 'PATCH';
export type AccountsUpdatePath = '/accounts/:id';
export interface AccountsUpdatePathParams {
id: string;
}
export interface AccountsUpdateRequestBody {
account: AccountUpdatePayload;
}
export type AccountsUpdateResponseBody = {
account: Account;
meta?: Record<string, unknown>;
};
export interface AccountsUpdateRequest {
body: AccountsUpdateRequestBody;
}
export interface AccountsUpdateResponse {
body: AccountsUpdateResponseBody;
}
export type AccountsUpdateErrors = 422;
export interface AccountsUpdate {
errors: AccountsUpdateErrors;
method: AccountsUpdateMethod;
path: AccountsUpdatePath;
pathParams: AccountsUpdatePathParams;
request: AccountsUpdateRequest;
response: AccountsUpdateResponse;
}
export type AccountsDestroyMethod = 'DELETE';
export type AccountsDestroyPath = '/accounts/:id';
export interface AccountsDestroyPathParams {
id: string;
}
export interface AccountsDestroy {
method: AccountsDestroyMethod;
path: AccountsDestroyPath;
pathParams: AccountsDestroyPathParams;
}
export const accounts = {
create: {
errors: [422],
method: 'POST',
path: '/accounts',
request: {
body: AccountsCreateRequestBodySchema,
},
response: {
body: AccountsCreateResponseBodySchema,
},
},
destroy: {
method: 'DELETE',
path: '/accounts/:id',
pathParams: AccountsDestroyPathParamsSchema,
},
index: {
method: 'GET',
path: '/accounts',
request: {
query: AccountsIndexRequestQuerySchema,
},
response: {
body: AccountsIndexResponseBodySchema,
},
},
show: {
method: 'GET',
path: '/accounts/:id',
pathParams: AccountsShowPathParamsSchema,
response: {
body: AccountsShowResponseBodySchema,
},
},
update: {
errors: [422],
method: 'PATCH',
path: '/accounts/:id',
pathParams: AccountsUpdatePathParamsSchema,
request: {
body: AccountsUpdateRequestBodySchema,
},
response: {
body: AccountsUpdateResponseBodySchema,
},
},
} as const;
export interface AccountsOperationTree {
create: Operation<AccountsCreate>;
destroy: Operation<AccountsDestroy>;
index: Operation<AccountsIndex>;
show: Operation<AccountsShow>;
update: Operation<AccountsUpdate>;
}ts
export * from './accounts';