Includes
Load associations on demand with include query parameters
API Definition
config/apis/loyal_hound.rb
rb
# frozen_string_literal: true
Apiwork::API.define '/loyal_hound' do
key_format :camel
export :openapi
export :apiwork
resources :books
endModels
app/models/loyal_hound/author.rb
rb
# frozen_string_literal: true
module LoyalHound
class Author < ApplicationRecord
has_many :books, dependent: :destroy
validates :name, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| created_at | datetime | ||
| name | string | ||
| updated_at | datetime |
app/models/loyal_hound/book.rb
rb
# frozen_string_literal: true
module LoyalHound
class Book < ApplicationRecord
belongs_to :author
has_many :reviews, dependent: :destroy
validates :title, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| author_id | string | ||
| created_at | datetime | ||
| published_on | date | ✓ | |
| title | string | ||
| updated_at | datetime |
app/models/loyal_hound/review.rb
rb
# frozen_string_literal: true
module LoyalHound
class Review < ApplicationRecord
belongs_to :book
validates :rating, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| body | text | ✓ | |
| book_id | string | ||
| created_at | datetime | ||
| rating | integer | ||
| updated_at | datetime |
Representations
app/representations/loyal_hound/book_representation.rb
rb
# frozen_string_literal: true
module LoyalHound
class BookRepresentation < Apiwork::Representation::Base
attribute :id
attribute :title, filterable: true, writable: true
attribute :published_on, sortable: true, writable: true
attribute :author_id, writable: true
attribute :created_at
attribute :updated_at
belongs_to :author
has_many :reviews
end
endapp/representations/loyal_hound/author_representation.rb
rb
# frozen_string_literal: true
module LoyalHound
class AuthorRepresentation < Apiwork::Representation::Base
attribute :id
attribute :name
end
endapp/representations/loyal_hound/review_representation.rb
rb
# frozen_string_literal: true
module LoyalHound
class ReviewRepresentation < Apiwork::Representation::Base
attribute :id
attribute :rating
attribute :body
end
endContracts
app/contracts/loyal_hound/book_contract.rb
rb
# frozen_string_literal: true
module LoyalHound
class BookContract < Apiwork::Contract::Base
representation BookRepresentation
end
endControllers
app/controllers/loyal_hound/books_controller.rb
rb
# frozen_string_literal: true
module LoyalHound
class BooksController < ApplicationController
before_action :set_book, only: %i[show update destroy]
def index
books = Book.all
expose books
end
def show
expose book
end
def create
book = Book.create(contract.body[:book])
expose book
end
def update
book.update(contract.body[:book])
expose book
end
def destroy
book.destroy
expose book
end
private
attr_reader :book
def set_book
@book = Book.find(params[:id])
end
end
endRequest Examples
List without includes
Request
http
GET /loyal_hound/booksResponse 200
json
{
"books": [
{
"id": "85b98b2f-99ee-554a-90ea-b7894ab980dc",
"title": "Pride and Prejudice",
"publishedOn": "1813-01-28",
"authorId": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
},
{
"id": "54317f80-8b94-5a49-8235-e05c73330abc",
"title": "Sense and Sensibility",
"publishedOn": "1811-10-30",
"authorId": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
],
"pagination": {
"items": 2,
"total": 1,
"current": 1,
"next": null,
"prev": null
}
}Include author
Request
http
GET /loyal_hound/books?include[author]=trueResponse 200
json
{
"books": [
{
"id": "85b98b2f-99ee-554a-90ea-b7894ab980dc",
"title": "Pride and Prejudice",
"publishedOn": "1813-01-28",
"authorId": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z",
"author": {
"id": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"name": "Jane Austen"
}
},
{
"id": "54317f80-8b94-5a49-8235-e05c73330abc",
"title": "Sense and Sensibility",
"publishedOn": "1811-10-30",
"authorId": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z",
"author": {
"id": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"name": "Jane Austen"
}
}
],
"pagination": {
"items": 2,
"total": 1,
"current": 1,
"next": null,
"prev": null
}
}Include reviews
Request
http
GET /loyal_hound/books?include[reviews]=trueResponse 200
json
{
"books": [
{
"id": "85b98b2f-99ee-554a-90ea-b7894ab980dc",
"title": "Pride and Prejudice",
"publishedOn": "1813-01-28",
"authorId": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z",
"reviews": [
{
"id": "6865e56a-0f49-5318-85dc-dc3942d92649",
"rating": 5,
"body": "A timeless classic"
},
{
"id": "e56dbc23-e351-57f2-808e-651847b3d1c5",
"rating": 4,
"body": "Beautifully written"
}
]
}
],
"pagination": {
"items": 1,
"total": 1,
"current": 1,
"next": null,
"prev": null
}
}Include both
Request
http
GET /loyal_hound/books?include[author]=true&include[reviews]=trueResponse 200
json
{
"books": [
{
"id": "85b98b2f-99ee-554a-90ea-b7894ab980dc",
"title": "Pride and Prejudice",
"publishedOn": "1813-01-28",
"authorId": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z",
"author": {
"id": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"name": "Jane Austen"
},
"reviews": [
{
"id": "6865e56a-0f49-5318-85dc-dc3942d92649",
"rating": 5,
"body": "A timeless classic"
}
]
}
],
"pagination": {
"items": 1,
"total": 1,
"current": 1,
"next": null,
"prev": null
}
}Show with includes
Request
http
GET /loyal_hound/books/85b98b2f-99ee-554a-90ea-b7894ab980dc?include[author]=true&include[reviews]=trueResponse 200
json
{
"book": {
"id": "85b98b2f-99ee-554a-90ea-b7894ab980dc",
"title": "Pride and Prejudice",
"publishedOn": "1813-01-28",
"authorId": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z",
"author": {
"id": "c9449e5d-d293-54b7-bbeb-3e1dd9682274",
"name": "Jane Austen"
},
"reviews": [
{
"id": "6865e56a-0f49-5318-85dc-dc3942d92649",
"rating": 5,
"body": "A timeless classic"
},
{
"id": "e56dbc23-e351-57f2-808e-651847b3d1c5",
"rating": 4,
"body": "Beautifully written"
}
]
}
}Exports
OpenAPI
yml
---
openapi: 3.1.0
info:
title: "/loyal_hound"
version: 1.0.0
paths:
"/books":
get:
operationId: booksIndex
parameters:
- in: query
name: filter
required: false
schema:
oneOf:
- "$ref": "#/components/schemas/bookFilter"
- items:
"$ref": "#/components/schemas/bookFilter"
type: array
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/bookInclude"
- in: query
name: page
required: false
schema:
"$ref": "#/components/schemas/bookPage"
- in: query
name: sort
required: false
schema:
oneOf:
- "$ref": "#/components/schemas/bookSort"
- items:
"$ref": "#/components/schemas/bookSort"
type: array
responses:
'200':
content:
application/json:
schema:
properties:
books:
items:
"$ref": "#/components/schemas/book"
type: array
meta:
properties: {}
type: object
pagination:
"$ref": "#/components/schemas/offsetPagination"
type: object
required:
- books
- pagination
description: ''
post:
operationId: booksCreate
parameters:
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/bookInclude"
requestBody:
content:
application/json:
schema:
properties:
book:
"$ref": "#/components/schemas/bookCreatePayload"
type: object
required:
- book
required: true
responses:
'200':
content:
application/json:
schema:
properties:
book:
"$ref": "#/components/schemas/book"
meta:
properties: {}
type: object
type: object
required:
- book
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
"/books/{id}":
get:
operationId: booksShow
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/bookInclude"
responses:
'200':
content:
application/json:
schema:
properties:
book:
"$ref": "#/components/schemas/book"
meta:
properties: {}
type: object
type: object
required:
- book
description: ''
patch:
operationId: booksUpdate
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/bookInclude"
requestBody:
content:
application/json:
schema:
properties:
book:
"$ref": "#/components/schemas/bookUpdatePayload"
type: object
required:
- book
required: true
responses:
'200':
content:
application/json:
schema:
properties:
book:
"$ref": "#/components/schemas/book"
meta:
properties: {}
type: object
type: object
required:
- book
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
delete:
operationId: booksDestroy
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/bookInclude"
responses:
'204':
description: ''
components:
schemas:
author:
properties:
id:
type: string
name:
type: string
type: object
required:
- id
- name
book:
properties:
author:
"$ref": "#/components/schemas/author"
authorId:
type: string
createdAt:
type: string
format: date-time
id:
type: string
publishedOn:
type:
- string
- 'null'
format: date
reviews:
items:
"$ref": "#/components/schemas/review"
type: array
title:
type: string
updatedAt:
type: string
format: date-time
type: object
required:
- authorId
- createdAt
- id
- publishedOn
- title
- updatedAt
bookCreatePayload:
properties:
authorId:
type: string
publishedOn:
type:
- string
- 'null'
format: date
default:
title:
type: string
type: object
required:
- authorId
- title
bookFilter:
properties:
AND:
items:
"$ref": "#/components/schemas/bookFilter"
type: array
NOT:
"$ref": "#/components/schemas/bookFilter"
OR:
items:
"$ref": "#/components/schemas/bookFilter"
type: array
title:
oneOf:
- type: string
- "$ref": "#/components/schemas/stringFilter"
type: object
bookInclude:
properties:
author:
type: boolean
reviews:
type: boolean
type: object
bookPage:
properties:
number:
type: integer
minimum: 1
size:
type: integer
minimum: 1
maximum: 100
type: object
bookSort:
properties:
publishedOn:
enum:
- asc
- desc
type: string
type: object
bookUpdatePayload:
properties:
authorId:
type: string
publishedOn:
type:
- string
- 'null'
format: date
title:
type: string
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
- total
review:
properties:
body:
type:
- string
- 'null'
id:
type: string
rating:
type: integer
type: object
required:
- body
- id
- rating
stringFilter:
properties:
contains:
type: string
endsWith:
type: string
eq:
type: string
in:
items:
type: string
type: array
startsWith:
type: string
type: objectApiwork
json
{
"base_path": "/loyal_hound",
"enums": [
{
"deprecated": false,
"description": null,
"example": null,
"name": "error_layer",
"scope": null,
"values": [
"http",
"contract",
"domain"
]
},
{
"deprecated": false,
"description": null,
"example": null,
"name": "sort_direction",
"scope": null,
"values": [
"asc",
"desc"
]
}
],
"error_codes": [
{
"description": "Unprocessable Entity",
"name": "unprocessable_entity",
"status": 422
}
],
"fingerprint": "323897f52c14ad8d",
"info": null,
"locales": [],
"resources": [
{
"actions": [
{
"name": "books.index",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/books",
"raises": [],
"request": {
"body": [],
"description": null,
"query": [
{
"name": "filter",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "union",
"discriminator": "",
"variants": [
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book_filter"
},
{
"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": "book_filter"
}
}
]
},
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "book_include"
},
{
"name": "page",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "book_page"
},
{
"name": "sort",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "union",
"discriminator": "",
"variants": [
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book_sort"
},
{
"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": "book_sort"
}
}
]
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "books",
"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": "book"
}
},
{
"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": "books.show",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/books/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "book_include"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "book",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "books.create",
"deprecated": false,
"description": null,
"method": "post",
"operation_id": null,
"path": "/books",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "book",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book_create_payload"
}
],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "book_include"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "book",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "books.update",
"deprecated": false,
"description": null,
"method": "patch",
"operation_id": null,
"path": "/books/:id",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "book",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book_update_payload"
}
],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "book_include"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "book",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "books.destroy",
"deprecated": false,
"description": null,
"method": "delete",
"operation_id": null,
"path": "/books/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "book_include"
}
]
},
"response": {
"body": null,
"description": null,
"no_content": true
},
"summary": null,
"tags": []
}
],
"identifier": "books",
"name": "books",
"parent_identifiers": [],
"path": "books",
"resources": [],
"scope": "book"
}
],
"types": [
{
"recursive": true,
"deprecated": false,
"description": null,
"example": null,
"name": "book_filter",
"scope": "book",
"type": "object",
"extends": [],
"shape": [
{
"name": "AND",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book_filter"
}
},
{
"name": "NOT",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "book_filter"
},
{
"name": "OR",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "book_filter"
}
},
{
"name": "title",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"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": "reference",
"reference": "string_filter"
}
]
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "author",
"scope": "author",
"type": "object",
"extends": [],
"shape": [
{
"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
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "book_create_payload",
"scope": "book",
"type": "object",
"extends": [],
"shape": [
{
"name": "authorId",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "publishedOn",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "date",
"default": null,
"example": null
},
{
"name": "title",
"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": "book_include",
"scope": "book",
"type": "object",
"extends": [],
"shape": [
{
"name": "author",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "boolean",
"example": null
},
{
"name": "reviews",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "boolean",
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "book_page",
"scope": "book",
"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": "book_sort",
"scope": "book",
"type": "object",
"extends": [],
"shape": [
{
"name": "publishedOn",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "sort_direction"
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "book_update_payload",
"scope": "book",
"type": "object",
"extends": [],
"shape": [
{
"name": "authorId",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "publishedOn",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "date",
"example": null
},
{
"name": "title",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": 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": "review",
"scope": "review",
"type": "object",
"extends": [],
"shape": [
{
"name": "body",
"deprecated": false,
"description": null,
"nullable": true,
"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": "rating",
"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": "string_filter",
"scope": null,
"type": "object",
"extends": [],
"shape": [
{
"name": "contains",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "endsWith",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "eq",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "in",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
}
},
{
"name": "startsWith",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "book",
"scope": "book",
"type": "object",
"extends": [],
"shape": [
{
"name": "author",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "author"
},
{
"name": "authorId",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "createdAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "datetime",
"example": null
},
{
"name": "id",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "publishedOn",
"deprecated": false,
"description": null,
"nullable": true,
"optional": false,
"type": "date",
"example": null
},
{
"name": "reviews",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "review"
}
},
{
"name": "title",
"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
}
]
},
{
"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 type SortDirection = 'asc' | 'desc';
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 StringFilter {
contains?: string;
endsWith?: string;
eq?: string;
in?: string[];
startsWith?: string;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
export interface Author {
id: string;
name: string;
}ts
import type { SortDirection, StringFilter } from '../api';
import type { Author } from './author';
import type { Review } from './review';
export interface BookFilter {
AND?: BookFilter[];
NOT?: BookFilter;
OR?: BookFilter[];
title?: string | StringFilter;
}
export interface BookCreatePayload {
authorId: string;
publishedOn?: string | null;
title: string;
}
export interface BookInclude {
author?: boolean;
reviews?: boolean;
}
export interface BookPage {
number?: number;
size?: number;
}
export interface BookSort {
publishedOn?: SortDirection;
}
export interface BookUpdatePayload {
authorId?: string;
publishedOn?: string | null;
title?: string;
}
export interface Book {
author?: Author;
authorId: string;
createdAt: string;
id: string;
publishedOn: string | null;
reviews?: Review[];
title: string;
updatedAt: string;
}ts
export * from './author';
export * from './book';
export * from './review';ts
export interface Review {
body: string | null;
id: string;
rating: number;
}ts
import type { OffsetPagination } from '../api';
import type {
Book,
BookCreatePayload,
BookFilter,
BookInclude,
BookPage,
BookSort,
BookUpdatePayload,
} from '../domains/book';
export type BooksIndexMethod = 'GET';
export type BooksIndexPath = '/books';
export interface BooksIndexRequestQuery {
filter?: BookFilter | BookFilter[];
include?: BookInclude;
page?: BookPage;
sort?: BookSort | BookSort[];
}
export type BooksIndexResponseBody = {
books: Book[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface BooksIndexRequest {
query: BooksIndexRequestQuery;
}
export interface BooksIndexResponse {
body: BooksIndexResponseBody;
}
export interface BooksIndex {
method: BooksIndexMethod;
path: BooksIndexPath;
request: BooksIndexRequest;
response: BooksIndexResponse;
}
export type BooksShowMethod = 'GET';
export type BooksShowPath = '/books/:id';
export interface BooksShowPathParams {
id: string;
}
export interface BooksShowRequestQuery {
include?: BookInclude;
}
export type BooksShowResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksShowRequest {
query: BooksShowRequestQuery;
}
export interface BooksShowResponse {
body: BooksShowResponseBody;
}
export interface BooksShow {
method: BooksShowMethod;
path: BooksShowPath;
pathParams: BooksShowPathParams;
request: BooksShowRequest;
response: BooksShowResponse;
}
export type BooksCreateMethod = 'POST';
export type BooksCreatePath = '/books';
export interface BooksCreateRequestQuery {
include?: BookInclude;
}
export interface BooksCreateRequestBody {
book: BookCreatePayload;
}
export type BooksCreateResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksCreateRequest {
body: BooksCreateRequestBody;
query: BooksCreateRequestQuery;
}
export interface BooksCreateResponse {
body: BooksCreateResponseBody;
}
export type BooksCreateErrors = 422;
export interface BooksCreate {
errors: BooksCreateErrors;
method: BooksCreateMethod;
path: BooksCreatePath;
request: BooksCreateRequest;
response: BooksCreateResponse;
}
export type BooksUpdateMethod = 'PATCH';
export type BooksUpdatePath = '/books/:id';
export interface BooksUpdatePathParams {
id: string;
}
export interface BooksUpdateRequestQuery {
include?: BookInclude;
}
export interface BooksUpdateRequestBody {
book: BookUpdatePayload;
}
export type BooksUpdateResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksUpdateRequest {
body: BooksUpdateRequestBody;
query: BooksUpdateRequestQuery;
}
export interface BooksUpdateResponse {
body: BooksUpdateResponseBody;
}
export type BooksUpdateErrors = 422;
export interface BooksUpdate {
errors: BooksUpdateErrors;
method: BooksUpdateMethod;
path: BooksUpdatePath;
pathParams: BooksUpdatePathParams;
request: BooksUpdateRequest;
response: BooksUpdateResponse;
}
export type BooksDestroyMethod = 'DELETE';
export type BooksDestroyPath = '/books/:id';
export interface BooksDestroyPathParams {
id: string;
}
export interface BooksDestroyRequestQuery {
include?: BookInclude;
}
export interface BooksDestroyRequest {
query: BooksDestroyRequestQuery;
}
export interface BooksDestroy {
method: BooksDestroyMethod;
path: BooksDestroyPath;
pathParams: BooksDestroyPathParams;
request: BooksDestroyRequest;
}ts
export * from './books';Zod
ts
import * as z from 'zod';
export const ErrorLayerSchema = z.enum(['contract', 'domain', 'http']);
export const SortDirectionSchema = z.enum(['asc', 'desc']);
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 StringFilterSchema = z.object({
contains: z.string().optional(),
endsWith: z.string().optional(),
eq: z.string().optional(),
in: z.array(z.string()).optional(),
startsWith: z.string().optional(),
});
export const ErrorSchema = z.object({
issues: z.array(ErrorIssueSchema),
layer: ErrorLayerSchema,
});
export type ErrorLayer = 'contract' | 'domain' | 'http';
export type SortDirection = 'asc' | 'desc';
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 StringFilter {
contains?: string;
endsWith?: string;
eq?: string;
in?: string[];
startsWith?: string;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import * as z from 'zod';
export const AuthorSchema = z.object({
id: z.string(),
name: z.string(),
});
export interface Author {
id: string;
name: string;
}ts
import type { SortDirection, StringFilter } from '../api';
import type { Author } from './author';
import type { Review } from './review';
import * as z from 'zod';
import { SortDirectionSchema, StringFilterSchema } from '../api';
import { AuthorSchema } from './author';
import { ReviewSchema } from './review';
export const BookFilterSchema: z.ZodType<BookFilter> = z.lazy(() =>
z.object({
AND: z.array(BookFilterSchema).optional(),
NOT: BookFilterSchema.optional(),
OR: z.array(BookFilterSchema).optional(),
title: z.union([z.string(), StringFilterSchema]).optional(),
}),
);
export const BookCreatePayloadSchema = z.object({
authorId: z.string(),
publishedOn: z.iso.date().nullable().default(null),
title: z.string(),
});
export const BookIncludeSchema = z.object({
author: z.boolean().optional(),
reviews: z.boolean().optional(),
});
export const BookPageSchema = z.object({
number: z.number().int().min(1).optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const BookSortSchema = z.object({
publishedOn: SortDirectionSchema.optional(),
});
export const BookUpdatePayloadSchema = z.object({
authorId: z.string().optional(),
publishedOn: z.iso.date().nullable().optional(),
title: z.string().optional(),
});
export const BookSchema = z.object({
author: AuthorSchema.optional(),
authorId: z.string(),
createdAt: z.iso.datetime(),
id: z.string(),
publishedOn: z.iso.date().nullable(),
reviews: z.array(ReviewSchema).optional(),
title: z.string(),
updatedAt: z.iso.datetime(),
});
export interface BookFilter {
AND?: BookFilter[];
NOT?: BookFilter;
OR?: BookFilter[];
title?: string | StringFilter;
}
export interface BookCreatePayload {
authorId: string;
publishedOn?: string | null;
title: string;
}
export interface BookInclude {
author?: boolean;
reviews?: boolean;
}
export interface BookPage {
number?: number;
size?: number;
}
export interface BookSort {
publishedOn?: SortDirection;
}
export interface BookUpdatePayload {
authorId?: string;
publishedOn?: string | null;
title?: string;
}
export interface Book {
author?: Author;
authorId: string;
createdAt: string;
id: string;
publishedOn: string | null;
reviews?: Review[];
title: string;
updatedAt: string;
}ts
export * from './author';
export * from './book';
export * from './review';ts
import * as z from 'zod';
export const ReviewSchema = z.object({
body: z.string().nullable(),
id: z.string(),
rating: z.number().int(),
});
export interface Review {
body: string | null;
id: string;
rating: number;
}ts
import type { OffsetPagination } from '../api';
import type {
Book,
BookCreatePayload,
BookFilter,
BookInclude,
BookPage,
BookSort,
BookUpdatePayload,
} from '../domains/book';
import * as z from 'zod';
import { OffsetPaginationSchema } from '../api';
import {
BookCreatePayloadSchema,
BookFilterSchema,
BookIncludeSchema,
BookPageSchema,
BookSchema,
BookSortSchema,
BookUpdatePayloadSchema,
} from '../domains/book';
export const BooksIndexRequestQuerySchema = z.object({
filter: z.union([BookFilterSchema, z.array(BookFilterSchema)]).optional(),
include: BookIncludeSchema.optional(),
page: BookPageSchema.optional(),
sort: z.union([BookSortSchema, z.array(BookSortSchema)]).optional(),
});
export const BooksIndexResponseBodySchema = z.object({
books: z.array(BookSchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: OffsetPaginationSchema,
});
export const BooksShowPathParamsSchema = z.object({ id: z.string() });
export const BooksShowRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export const BooksShowResponseBodySchema = z.object({
book: BookSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const BooksCreateRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export const BooksCreateRequestBodySchema = z.object({
book: BookCreatePayloadSchema,
});
export const BooksCreateResponseBodySchema = z.object({
book: BookSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const BooksUpdatePathParamsSchema = z.object({ id: z.string() });
export const BooksUpdateRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export const BooksUpdateRequestBodySchema = z.object({
book: BookUpdatePayloadSchema,
});
export const BooksUpdateResponseBodySchema = z.object({
book: BookSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const BooksDestroyPathParamsSchema = z.object({ id: z.string() });
export const BooksDestroyRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export type BooksIndexMethod = 'GET';
export type BooksIndexPath = '/books';
export interface BooksIndexRequestQuery {
filter?: BookFilter | BookFilter[];
include?: BookInclude;
page?: BookPage;
sort?: BookSort | BookSort[];
}
export type BooksIndexResponseBody = {
books: Book[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface BooksIndexRequest {
query: BooksIndexRequestQuery;
}
export interface BooksIndexResponse {
body: BooksIndexResponseBody;
}
export interface BooksIndex {
method: BooksIndexMethod;
path: BooksIndexPath;
request: BooksIndexRequest;
response: BooksIndexResponse;
}
export type BooksShowMethod = 'GET';
export type BooksShowPath = '/books/:id';
export interface BooksShowPathParams {
id: string;
}
export interface BooksShowRequestQuery {
include?: BookInclude;
}
export type BooksShowResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksShowRequest {
query: BooksShowRequestQuery;
}
export interface BooksShowResponse {
body: BooksShowResponseBody;
}
export interface BooksShow {
method: BooksShowMethod;
path: BooksShowPath;
pathParams: BooksShowPathParams;
request: BooksShowRequest;
response: BooksShowResponse;
}
export type BooksCreateMethod = 'POST';
export type BooksCreatePath = '/books';
export interface BooksCreateRequestQuery {
include?: BookInclude;
}
export interface BooksCreateRequestBody {
book: BookCreatePayload;
}
export type BooksCreateResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksCreateRequest {
body: BooksCreateRequestBody;
query: BooksCreateRequestQuery;
}
export interface BooksCreateResponse {
body: BooksCreateResponseBody;
}
export type BooksCreateErrors = 422;
export interface BooksCreate {
errors: BooksCreateErrors;
method: BooksCreateMethod;
path: BooksCreatePath;
request: BooksCreateRequest;
response: BooksCreateResponse;
}
export type BooksUpdateMethod = 'PATCH';
export type BooksUpdatePath = '/books/:id';
export interface BooksUpdatePathParams {
id: string;
}
export interface BooksUpdateRequestQuery {
include?: BookInclude;
}
export interface BooksUpdateRequestBody {
book: BookUpdatePayload;
}
export type BooksUpdateResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksUpdateRequest {
body: BooksUpdateRequestBody;
query: BooksUpdateRequestQuery;
}
export interface BooksUpdateResponse {
body: BooksUpdateResponseBody;
}
export type BooksUpdateErrors = 422;
export interface BooksUpdate {
errors: BooksUpdateErrors;
method: BooksUpdateMethod;
path: BooksUpdatePath;
pathParams: BooksUpdatePathParams;
request: BooksUpdateRequest;
response: BooksUpdateResponse;
}
export type BooksDestroyMethod = 'DELETE';
export type BooksDestroyPath = '/books/:id';
export interface BooksDestroyPathParams {
id: string;
}
export interface BooksDestroyRequestQuery {
include?: BookInclude;
}
export interface BooksDestroyRequest {
query: BooksDestroyRequestQuery;
}
export interface BooksDestroy {
method: BooksDestroyMethod;
path: BooksDestroyPath;
pathParams: BooksDestroyPathParams;
request: BooksDestroyRequest;
}ts
export * from './books';Sorbus
ts
import * as z from 'zod';
export const ErrorLayerSchema = z.enum(['contract', 'domain', 'http']);
export const SortDirectionSchema = z.enum(['asc', 'desc']);
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 StringFilterSchema = z.object({
contains: z.string().optional(),
endsWith: z.string().optional(),
eq: z.string().optional(),
in: z.array(z.string()).optional(),
startsWith: z.string().optional(),
});
export const ErrorSchema = z.object({
issues: z.array(ErrorIssueSchema),
layer: ErrorLayerSchema,
});
export type ErrorLayer = 'contract' | 'domain' | 'http';
export type SortDirection = 'asc' | 'desc';
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 StringFilter {
contains?: string;
endsWith?: string;
eq?: string;
in?: string[];
startsWith?: string;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import type { BooksOperationTree } from './endpoints';
import { createClientFactory } from 'sorbus';
import { contract } from './contract';
export interface Client {
books: BooksOperationTree;
}
export const createClient = createClientFactory<Client>(contract);ts
import { ErrorSchema } from './api';
import { books } from './endpoints';
export const contract = {
endpoints: {
books,
},
error: ErrorSchema,
} as const;ts
import * as z from 'zod';
export const AuthorSchema = z.object({
id: z.string(),
name: z.string(),
});
export interface Author {
id: string;
name: string;
}ts
import type { SortDirection, StringFilter } from '../api';
import type { Author } from './author';
import type { Review } from './review';
import * as z from 'zod';
import { SortDirectionSchema, StringFilterSchema } from '../api';
import { AuthorSchema } from './author';
import { ReviewSchema } from './review';
export const BookFilterSchema: z.ZodType<BookFilter> = z.lazy(() =>
z.object({
AND: z.array(BookFilterSchema).optional(),
NOT: BookFilterSchema.optional(),
OR: z.array(BookFilterSchema).optional(),
title: z.union([z.string(), StringFilterSchema]).optional(),
}),
);
export const BookCreatePayloadSchema = z.object({
authorId: z.string(),
publishedOn: z.iso.date().nullable().default(null),
title: z.string(),
});
export const BookIncludeSchema = z.object({
author: z.boolean().optional(),
reviews: z.boolean().optional(),
});
export const BookPageSchema = z.object({
number: z.number().int().min(1).optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const BookSortSchema = z.object({
publishedOn: SortDirectionSchema.optional(),
});
export const BookUpdatePayloadSchema = z.object({
authorId: z.string().optional(),
publishedOn: z.iso.date().nullable().optional(),
title: z.string().optional(),
});
export const BookSchema = z.object({
author: AuthorSchema.optional(),
authorId: z.string(),
createdAt: z.iso.datetime(),
id: z.string(),
publishedOn: z.iso.date().nullable(),
reviews: z.array(ReviewSchema).optional(),
title: z.string(),
updatedAt: z.iso.datetime(),
});
export interface BookFilter {
AND?: BookFilter[];
NOT?: BookFilter;
OR?: BookFilter[];
title?: string | StringFilter;
}
export interface BookCreatePayload {
authorId: string;
publishedOn?: string | null;
title: string;
}
export interface BookInclude {
author?: boolean;
reviews?: boolean;
}
export interface BookPage {
number?: number;
size?: number;
}
export interface BookSort {
publishedOn?: SortDirection;
}
export interface BookUpdatePayload {
authorId?: string;
publishedOn?: string | null;
title?: string;
}
export interface Book {
author?: Author;
authorId: string;
createdAt: string;
id: string;
publishedOn: string | null;
reviews?: Review[];
title: string;
updatedAt: string;
}ts
export * from './author';
export * from './book';
export * from './review';ts
import * as z from 'zod';
export const ReviewSchema = z.object({
body: z.string().nullable(),
id: z.string(),
rating: z.number().int(),
});
export interface Review {
body: string | null;
id: string;
rating: number;
}ts
import type { Operation } from 'sorbus';
import type { OffsetPagination } from '../api';
import type {
Book,
BookCreatePayload,
BookFilter,
BookInclude,
BookPage,
BookSort,
BookUpdatePayload,
} from '../domains/book';
import * as z from 'zod';
import { OffsetPaginationSchema } from '../api';
import {
BookCreatePayloadSchema,
BookFilterSchema,
BookIncludeSchema,
BookPageSchema,
BookSchema,
BookSortSchema,
BookUpdatePayloadSchema,
} from '../domains/book';
export const BooksIndexRequestQuerySchema = z.object({
filter: z.union([BookFilterSchema, z.array(BookFilterSchema)]).optional(),
include: BookIncludeSchema.optional(),
page: BookPageSchema.optional(),
sort: z.union([BookSortSchema, z.array(BookSortSchema)]).optional(),
});
export const BooksIndexResponseBodySchema = z.object({
books: z.array(BookSchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: OffsetPaginationSchema,
});
export const BooksShowPathParamsSchema = z.object({ id: z.string() });
export const BooksShowRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export const BooksShowResponseBodySchema = z.object({
book: BookSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const BooksCreateRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export const BooksCreateRequestBodySchema = z.object({
book: BookCreatePayloadSchema,
});
export const BooksCreateResponseBodySchema = z.object({
book: BookSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const BooksUpdatePathParamsSchema = z.object({ id: z.string() });
export const BooksUpdateRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export const BooksUpdateRequestBodySchema = z.object({
book: BookUpdatePayloadSchema,
});
export const BooksUpdateResponseBodySchema = z.object({
book: BookSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const BooksDestroyPathParamsSchema = z.object({ id: z.string() });
export const BooksDestroyRequestQuerySchema = z.object({
include: BookIncludeSchema.optional(),
});
export type BooksIndexMethod = 'GET';
export type BooksIndexPath = '/books';
export interface BooksIndexRequestQuery {
filter?: BookFilter | BookFilter[];
include?: BookInclude;
page?: BookPage;
sort?: BookSort | BookSort[];
}
export type BooksIndexResponseBody = {
books: Book[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface BooksIndexRequest {
query: BooksIndexRequestQuery;
}
export interface BooksIndexResponse {
body: BooksIndexResponseBody;
}
export interface BooksIndex {
method: BooksIndexMethod;
path: BooksIndexPath;
request: BooksIndexRequest;
response: BooksIndexResponse;
}
export type BooksShowMethod = 'GET';
export type BooksShowPath = '/books/:id';
export interface BooksShowPathParams {
id: string;
}
export interface BooksShowRequestQuery {
include?: BookInclude;
}
export type BooksShowResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksShowRequest {
query: BooksShowRequestQuery;
}
export interface BooksShowResponse {
body: BooksShowResponseBody;
}
export interface BooksShow {
method: BooksShowMethod;
path: BooksShowPath;
pathParams: BooksShowPathParams;
request: BooksShowRequest;
response: BooksShowResponse;
}
export type BooksCreateMethod = 'POST';
export type BooksCreatePath = '/books';
export interface BooksCreateRequestQuery {
include?: BookInclude;
}
export interface BooksCreateRequestBody {
book: BookCreatePayload;
}
export type BooksCreateResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksCreateRequest {
body: BooksCreateRequestBody;
query: BooksCreateRequestQuery;
}
export interface BooksCreateResponse {
body: BooksCreateResponseBody;
}
export type BooksCreateErrors = 422;
export interface BooksCreate {
errors: BooksCreateErrors;
method: BooksCreateMethod;
path: BooksCreatePath;
request: BooksCreateRequest;
response: BooksCreateResponse;
}
export type BooksUpdateMethod = 'PATCH';
export type BooksUpdatePath = '/books/:id';
export interface BooksUpdatePathParams {
id: string;
}
export interface BooksUpdateRequestQuery {
include?: BookInclude;
}
export interface BooksUpdateRequestBody {
book: BookUpdatePayload;
}
export type BooksUpdateResponseBody = {
book: Book;
meta?: Record<string, unknown>;
};
export interface BooksUpdateRequest {
body: BooksUpdateRequestBody;
query: BooksUpdateRequestQuery;
}
export interface BooksUpdateResponse {
body: BooksUpdateResponseBody;
}
export type BooksUpdateErrors = 422;
export interface BooksUpdate {
errors: BooksUpdateErrors;
method: BooksUpdateMethod;
path: BooksUpdatePath;
pathParams: BooksUpdatePathParams;
request: BooksUpdateRequest;
response: BooksUpdateResponse;
}
export type BooksDestroyMethod = 'DELETE';
export type BooksDestroyPath = '/books/:id';
export interface BooksDestroyPathParams {
id: string;
}
export interface BooksDestroyRequestQuery {
include?: BookInclude;
}
export interface BooksDestroyRequest {
query: BooksDestroyRequestQuery;
}
export interface BooksDestroy {
method: BooksDestroyMethod;
path: BooksDestroyPath;
pathParams: BooksDestroyPathParams;
request: BooksDestroyRequest;
}
export const books = {
create: {
errors: [422],
method: 'POST',
path: '/books',
request: {
body: BooksCreateRequestBodySchema,
query: BooksCreateRequestQuerySchema,
},
response: {
body: BooksCreateResponseBodySchema,
},
},
destroy: {
method: 'DELETE',
path: '/books/:id',
pathParams: BooksDestroyPathParamsSchema,
request: {
query: BooksDestroyRequestQuerySchema,
},
},
index: {
method: 'GET',
path: '/books',
request: {
query: BooksIndexRequestQuerySchema,
},
response: {
body: BooksIndexResponseBodySchema,
},
},
show: {
method: 'GET',
path: '/books/:id',
pathParams: BooksShowPathParamsSchema,
request: {
query: BooksShowRequestQuerySchema,
},
response: {
body: BooksShowResponseBodySchema,
},
},
update: {
errors: [422],
method: 'PATCH',
path: '/books/:id',
pathParams: BooksUpdatePathParamsSchema,
request: {
body: BooksUpdateRequestBodySchema,
query: BooksUpdateRequestQuerySchema,
},
response: {
body: BooksUpdateResponseBodySchema,
},
},
} as const;
export interface BooksOperationTree {
create: Operation<BooksCreate>;
destroy: Operation<BooksDestroy>;
index: Operation<BooksIndex>;
show: Operation<BooksShow>;
update: Operation<BooksUpdate>;
}ts
export * from './books';