Polymorphic Associations
Comments that belong to different content types (posts, videos)
API Definition
config/apis/gentle_owl.rb
rb
# frozen_string_literal: true
Apiwork::API.define '/gentle_owl' do
key_format :camel
export :openapi
export :apiwork
resources :comments
endModels
app/models/gentle_owl/comment.rb
rb
# frozen_string_literal: true
module GentleOwl
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
validates :body, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| author_name | string | ✓ | |
| body | text | ||
| commentable_id | string | ||
| commentable_type | string | ||
| created_at | datetime | ||
| updated_at | datetime |
app/models/gentle_owl/post.rb
rb
# frozen_string_literal: true
module GentleOwl
class Post < ApplicationRecord
has_many :comments, as: :commentable, dependent: :destroy
validates :title, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| body | text | ✓ | |
| created_at | datetime | ||
| title | string | ||
| updated_at | datetime |
app/models/gentle_owl/video.rb
rb
# frozen_string_literal: true
module GentleOwl
class Video < ApplicationRecord
has_many :comments, as: :commentable, dependent: :destroy
validates :title, :url, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| created_at | datetime | ||
| duration | integer | ✓ | |
| title | string | ||
| updated_at | datetime | ||
| url | string |
Representations
app/representations/gentle_owl/comment_representation.rb
rb
# frozen_string_literal: true
module GentleOwl
class CommentRepresentation < Apiwork::Representation::Base
attribute :id
attribute :body, writable: true
attribute :author_name, writable: true
attribute :commentable_type, filterable: true, writable: true
attribute :commentable_id, writable: true
attribute :created_at, sortable: true
attribute :updated_at, sortable: true
belongs_to :commentable, polymorphic: [PostRepresentation, VideoRepresentation]
end
endapp/representations/gentle_owl/post_representation.rb
rb
# frozen_string_literal: true
module GentleOwl
class PostRepresentation < Apiwork::Representation::Base
type_name :post
attribute :id
attribute :title, filterable: true, writable: true
attribute :body, writable: true
attribute :created_at, sortable: true
attribute :updated_at, sortable: true
has_many :comments
end
endapp/representations/gentle_owl/video_representation.rb
rb
# frozen_string_literal: true
module GentleOwl
class VideoRepresentation < Apiwork::Representation::Base
type_name :video
attribute :id
attribute :title, filterable: true, writable: true
attribute :url, writable: true
attribute :duration, writable: true
attribute :created_at, sortable: true
attribute :updated_at, sortable: true
has_many :comments
end
endContracts
app/contracts/gentle_owl/comment_contract.rb
rb
# frozen_string_literal: true
module GentleOwl
class CommentContract < Apiwork::Contract::Base
representation CommentRepresentation
end
endControllers
app/controllers/gentle_owl/comments_controller.rb
rb
# frozen_string_literal: true
module GentleOwl
class CommentsController < ApplicationController
before_action :set_comment, only: %i[show update destroy]
def index
comments = Comment.all
expose comments
end
def show
expose comment
end
def create
comment = Comment.create(contract.body[:comment])
expose comment
end
def update
comment.update(contract.body[:comment])
expose comment
end
def destroy
comment.destroy
expose comment
end
private
attr_reader :comment
def set_comment
@comment = Comment.find(params[:id])
end
end
endRequest Examples
List all comments
Request
http
GET /gentle_owl/commentsResponse 200
json
{
"comments": [
{
"id": "d1ff1866-6fad-545c-839e-2d972eb5729c",
"body": "Great post!",
"authorName": "John Doe",
"commentableType": "post",
"commentableId": "96988365-65b2-5455-a8a8-491aa772ba47",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
},
{
"id": "6027b33b-0a17-5c68-bcc1-527ae6105f2c",
"body": "Helpful video!",
"authorName": "Jane Doe",
"commentableType": "video",
"commentableId": "df4ddc5a-953d-52f5-b5b5-7ddf16fa8f57",
"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
}
}Get comment details
Request
http
GET /gentle_owl/comments/d1ff1866-6fad-545c-839e-2d972eb5729cResponse 200
json
{
"comment": {
"id": "d1ff1866-6fad-545c-839e-2d972eb5729c",
"body": "Great post!",
"authorName": "John Doe",
"commentableType": "post",
"commentableId": "96988365-65b2-5455-a8a8-491aa772ba47",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
}Create comment on post
Request
http
POST /gentle_owl/comments
Content-Type: application/json
{
"comment": {
"body": "This is a great article!",
"authorName": "Jane Doe",
"commentableType": "post",
"commentableId": "96988365-65b2-5455-a8a8-491aa772ba47"
}
}Response 201
json
{
"comment": {
"id": "d1ff1866-6fad-545c-839e-2d972eb5729c",
"body": "This is a great article!",
"authorName": "Jane Doe",
"commentableType": "post",
"commentableId": "96988365-65b2-5455-a8a8-491aa772ba47",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
}Create comment on video
Request
http
POST /gentle_owl/comments
Content-Type: application/json
{
"comment": {
"body": "Very helpful video!",
"authorName": "Bob Smith",
"commentableType": "video",
"commentableId": "df4ddc5a-953d-52f5-b5b5-7ddf16fa8f57"
}
}Response 201
json
{
"comment": {
"id": "d1ff1866-6fad-545c-839e-2d972eb5729c",
"body": "Very helpful video!",
"authorName": "Bob Smith",
"commentableType": "video",
"commentableId": "df4ddc5a-953d-52f5-b5b5-7ddf16fa8f57",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
}Filter by content type
Request
http
GET /gentle_owl/comments?filter[commentableType][eq]=postResponse 200
json
{
"comments": [
{
"id": "d1ff1866-6fad-545c-839e-2d972eb5729c",
"body": "Post comment",
"authorName": "User 1",
"commentableType": "post",
"commentableId": "96988365-65b2-5455-a8a8-491aa772ba47",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
],
"pagination": {
"items": 1,
"total": 1,
"current": 1,
"next": null,
"prev": null
}
}Exports
OpenAPI
yml
---
openapi: 3.1.0
info:
title: "/gentle_owl"
version: 1.0.0
paths:
"/comments":
get:
operationId: commentsIndex
parameters:
- in: query
name: filter
required: false
schema:
oneOf:
- "$ref": "#/components/schemas/commentFilter"
- items:
"$ref": "#/components/schemas/commentFilter"
type: array
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/commentInclude"
- in: query
name: page
required: false
schema:
"$ref": "#/components/schemas/commentPage"
- in: query
name: sort
required: false
schema:
oneOf:
- "$ref": "#/components/schemas/commentSort"
- items:
"$ref": "#/components/schemas/commentSort"
type: array
responses:
'200':
content:
application/json:
schema:
properties:
comments:
items:
"$ref": "#/components/schemas/comment"
type: array
meta:
properties: {}
type: object
pagination:
"$ref": "#/components/schemas/offsetPagination"
type: object
required:
- comments
- pagination
description: ''
post:
operationId: commentsCreate
parameters:
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/commentInclude"
requestBody:
content:
application/json:
schema:
properties:
comment:
"$ref": "#/components/schemas/commentCreatePayload"
type: object
required:
- comment
required: true
responses:
'200':
content:
application/json:
schema:
properties:
comment:
"$ref": "#/components/schemas/comment"
meta:
properties: {}
type: object
type: object
required:
- comment
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
"/comments/{id}":
get:
operationId: commentsShow
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/commentInclude"
responses:
'200':
content:
application/json:
schema:
properties:
comment:
"$ref": "#/components/schemas/comment"
meta:
properties: {}
type: object
type: object
required:
- comment
description: ''
patch:
operationId: commentsUpdate
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/commentInclude"
requestBody:
content:
application/json:
schema:
properties:
comment:
"$ref": "#/components/schemas/commentUpdatePayload"
type: object
required:
- comment
required: true
responses:
'200':
content:
application/json:
schema:
properties:
comment:
"$ref": "#/components/schemas/comment"
meta:
properties: {}
type: object
type: object
required:
- comment
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
delete:
operationId: commentsDestroy
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: query
name: include
required: false
schema:
"$ref": "#/components/schemas/commentInclude"
responses:
'204':
description: ''
components:
schemas:
comment:
properties:
authorName:
type:
- string
- 'null'
body:
type: string
commentable:
"$ref": "#/components/schemas/commentCommentable"
commentableId:
type: string
commentableType:
type: string
createdAt:
type: string
format: date-time
id:
type: string
updatedAt:
type: string
format: date-time
type: object
required:
- authorName
- body
- commentableId
- commentableType
- createdAt
- id
- updatedAt
commentCommentable:
oneOf:
- allOf:
- "$ref": "#/components/schemas/post"
- properties:
commentableType:
const: post
type: string
required:
- commentableType
type: object
- allOf:
- "$ref": "#/components/schemas/video"
- properties:
commentableType:
const: video
type: string
required:
- commentableType
type: object
discriminator:
mapping:
post: "#/components/schemas/post"
video: "#/components/schemas/video"
propertyName: commentableType
commentCommentableTypeFilter:
oneOf:
- enum:
- post
- video
type: string
- properties:
eq:
enum:
- post
- video
type: string
in:
items:
enum:
- post
- video
type: string
type: array
type: object
required:
- eq
- in
commentCreatePayload:
properties:
authorName:
type:
- string
- 'null'
default:
body:
type: string
commentableId:
type: string
commentableType:
enum:
- post
- video
type: string
type: object
required:
- body
- commentableId
- commentableType
commentFilter:
properties:
AND:
items:
"$ref": "#/components/schemas/commentFilter"
type: array
NOT:
"$ref": "#/components/schemas/commentFilter"
OR:
items:
"$ref": "#/components/schemas/commentFilter"
type: array
commentableType:
"$ref": "#/components/schemas/commentCommentableTypeFilter"
type: object
commentInclude:
properties:
commentable:
type: boolean
type: object
commentPage:
properties:
number:
type: integer
minimum: 1
size:
type: integer
minimum: 1
maximum: 100
type: object
commentSort:
properties:
createdAt:
enum:
- asc
- desc
type: string
updatedAt:
enum:
- asc
- desc
type: string
type: object
commentUpdatePayload:
properties:
authorName:
type:
- string
- 'null'
body:
type: string
commentableId:
type: string
commentableType:
enum:
- post
- video
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
post:
properties:
body:
type:
- string
- 'null'
comments:
items:
"$ref": "#/components/schemas/comment"
type: array
createdAt:
type: string
format: date-time
id:
type: string
title:
type: string
updatedAt:
type: string
format: date-time
type: object
required:
- body
- createdAt
- id
- title
- updatedAt
video:
properties:
comments:
items:
"$ref": "#/components/schemas/comment"
type: array
createdAt:
type: string
format: date-time
duration:
type:
- integer
- 'null'
id:
type: string
title:
type: string
updatedAt:
type: string
format: date-time
url:
type: string
type: object
required:
- createdAt
- duration
- id
- title
- updatedAt
- urlApiwork
json
{
"base_path": "/gentle_owl",
"enums": [
{
"deprecated": false,
"description": null,
"example": null,
"name": "comment_commentable_type",
"scope": "comment",
"values": [
"post",
"video"
]
},
{
"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": "fbb5cab4725d6f92",
"info": null,
"locales": [],
"resources": [
{
"actions": [
{
"name": "comments.index",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/comments",
"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": "comment_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": "comment_filter"
}
}
]
},
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_include"
},
{
"name": "page",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_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": "comment_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": "comment_sort"
}
}
]
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "comments",
"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": "comment"
}
},
{
"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": "comments.show",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/comments/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_include"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "comment",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "comment"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "comments.create",
"deprecated": false,
"description": null,
"method": "post",
"operation_id": null,
"path": "/comments",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "comment",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "comment_create_payload"
}
],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_include"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "comment",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "comment"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "comments.update",
"deprecated": false,
"description": null,
"method": "patch",
"operation_id": null,
"path": "/comments/:id",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "comment",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "comment_update_payload"
}
],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_include"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "comment",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "comment"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "comments.destroy",
"deprecated": false,
"description": null,
"method": "delete",
"operation_id": null,
"path": "/comments/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": [
{
"name": "include",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_include"
}
]
},
"response": {
"body": null,
"description": null,
"no_content": true
},
"summary": null,
"tags": []
}
],
"identifier": "comments",
"name": "comments",
"parent_identifiers": [],
"path": "comments",
"resources": [],
"scope": "comment"
}
],
"types": [
{
"recursive": true,
"deprecated": false,
"description": null,
"example": null,
"name": "comment_commentable",
"scope": "comment",
"type": "union",
"discriminator": "commentableType",
"variants": [
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "post",
"tag": "post"
},
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "video",
"tag": "video"
}
]
},
{
"recursive": true,
"deprecated": false,
"description": null,
"example": null,
"name": "comment_filter",
"scope": "comment",
"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": "comment_filter"
}
},
{
"name": "NOT",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_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": "comment_filter"
}
},
{
"name": "commentableType",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_commentable_type_filter"
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "comment",
"scope": "comment",
"type": "object",
"extends": [],
"shape": [
{
"name": "authorName",
"deprecated": false,
"description": null,
"nullable": true,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "body",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "commentable",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "comment_commentable"
},
{
"name": "commentableId",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "commentableType",
"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": "updatedAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "datetime",
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "comment_commentable_type_filter",
"scope": "comment",
"type": "union",
"discriminator": "",
"variants": [
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "comment_commentable_type"
},
{
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "object",
"partial": true,
"shape": [
{
"name": "eq",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "comment_commentable_type"
},
{
"name": "in",
"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": "comment_commentable_type"
}
}
]
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "comment_create_payload",
"scope": "comment",
"type": "object",
"extends": [],
"shape": [
{
"name": "authorName",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "string",
"default": null,
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "body",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "commentableId",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "commentableType",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"enum": "[\"post\", \"video\"]",
"example": null,
"format": null,
"max": null,
"min": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "comment_include",
"scope": "comment",
"type": "object",
"extends": [],
"shape": [
{
"name": "commentable",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "boolean",
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "comment_page",
"scope": "comment",
"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": "comment_sort",
"scope": "comment",
"type": "object",
"extends": [],
"shape": [
{
"name": "createdAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "sort_direction"
},
{
"name": "updatedAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "sort_direction"
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "comment_update_payload",
"scope": "comment",
"type": "object",
"extends": [],
"shape": [
{
"name": "authorName",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "body",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "commentableId",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "commentableType",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"enum": "[\"post\", \"video\"]",
"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": "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"
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "post",
"scope": "post",
"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": "comments",
"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": "comment"
}
},
{
"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": "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": "video",
"scope": "video",
"type": "object",
"extends": [],
"shape": [
{
"name": "comments",
"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": "comment"
}
},
{
"name": "createdAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "datetime",
"example": null
},
{
"name": "duration",
"deprecated": false,
"description": null,
"nullable": true,
"optional": false,
"type": "integer",
"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": "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
},
{
"name": "url",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
}
]
}
]
}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 Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import type { SortDirection } from '../api';
import type { Post } from './post';
import type { Video } from './video';
export type CommentCommentableType = 'post' | 'video';
export type CommentCommentable =
| (Post & { commentableType: 'post' })
| (Video & { commentableType: 'video' });
export interface CommentFilter {
AND?: CommentFilter[];
commentableType?: CommentCommentableTypeFilter;
NOT?: CommentFilter;
OR?: CommentFilter[];
}
export interface Comment {
authorName: string | null;
body: string;
commentable?: CommentCommentable;
commentableId: string;
commentableType: string;
createdAt: string;
id: string;
updatedAt: string;
}
export type CommentCommentableTypeFilter =
| CommentCommentableType
| { eq?: CommentCommentableType; in?: CommentCommentableType[] };
export interface CommentCreatePayload {
authorName?: string | null;
body: string;
commentableId: string;
commentableType: ['post', 'video'];
}
export interface CommentInclude {
commentable?: boolean;
}
export interface CommentPage {
number?: number;
size?: number;
}
export interface CommentSort {
createdAt?: SortDirection;
updatedAt?: SortDirection;
}
export interface CommentUpdatePayload {
authorName?: string | null;
body?: string;
commentableId?: string;
commentableType?: ['post', 'video'];
}ts
export * from './comment';
export * from './post';
export * from './video';ts
import type { Comment } from './comment';
export interface Post {
body: string | null;
comments?: Comment[];
createdAt: string;
id: string;
title: string;
updatedAt: string;
}ts
import type { Comment } from './comment';
export interface Video {
comments?: Comment[];
createdAt: string;
duration: number | null;
id: string;
title: string;
updatedAt: string;
url: string;
}ts
import type { OffsetPagination } from '../api';
import type {
Comment,
CommentCreatePayload,
CommentFilter,
CommentInclude,
CommentPage,
CommentSort,
CommentUpdatePayload,
} from '../domains/comment';
export type CommentsIndexMethod = 'GET';
export type CommentsIndexPath = '/comments';
export interface CommentsIndexRequestQuery {
filter?: CommentFilter | CommentFilter[];
include?: CommentInclude;
page?: CommentPage;
sort?: CommentSort | CommentSort[];
}
export type CommentsIndexResponseBody = {
comments: Comment[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface CommentsIndexRequest {
query: CommentsIndexRequestQuery;
}
export interface CommentsIndexResponse {
body: CommentsIndexResponseBody;
}
export interface CommentsIndex {
method: CommentsIndexMethod;
path: CommentsIndexPath;
request: CommentsIndexRequest;
response: CommentsIndexResponse;
}
export type CommentsShowMethod = 'GET';
export type CommentsShowPath = '/comments/:id';
export interface CommentsShowPathParams {
id: string;
}
export interface CommentsShowRequestQuery {
include?: CommentInclude;
}
export type CommentsShowResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsShowRequest {
query: CommentsShowRequestQuery;
}
export interface CommentsShowResponse {
body: CommentsShowResponseBody;
}
export interface CommentsShow {
method: CommentsShowMethod;
path: CommentsShowPath;
pathParams: CommentsShowPathParams;
request: CommentsShowRequest;
response: CommentsShowResponse;
}
export type CommentsCreateMethod = 'POST';
export type CommentsCreatePath = '/comments';
export interface CommentsCreateRequestQuery {
include?: CommentInclude;
}
export interface CommentsCreateRequestBody {
comment: CommentCreatePayload;
}
export type CommentsCreateResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsCreateRequest {
body: CommentsCreateRequestBody;
query: CommentsCreateRequestQuery;
}
export interface CommentsCreateResponse {
body: CommentsCreateResponseBody;
}
export type CommentsCreateErrors = 422;
export interface CommentsCreate {
errors: CommentsCreateErrors;
method: CommentsCreateMethod;
path: CommentsCreatePath;
request: CommentsCreateRequest;
response: CommentsCreateResponse;
}
export type CommentsUpdateMethod = 'PATCH';
export type CommentsUpdatePath = '/comments/:id';
export interface CommentsUpdatePathParams {
id: string;
}
export interface CommentsUpdateRequestQuery {
include?: CommentInclude;
}
export interface CommentsUpdateRequestBody {
comment: CommentUpdatePayload;
}
export type CommentsUpdateResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsUpdateRequest {
body: CommentsUpdateRequestBody;
query: CommentsUpdateRequestQuery;
}
export interface CommentsUpdateResponse {
body: CommentsUpdateResponseBody;
}
export type CommentsUpdateErrors = 422;
export interface CommentsUpdate {
errors: CommentsUpdateErrors;
method: CommentsUpdateMethod;
path: CommentsUpdatePath;
pathParams: CommentsUpdatePathParams;
request: CommentsUpdateRequest;
response: CommentsUpdateResponse;
}
export type CommentsDestroyMethod = 'DELETE';
export type CommentsDestroyPath = '/comments/:id';
export interface CommentsDestroyPathParams {
id: string;
}
export interface CommentsDestroyRequestQuery {
include?: CommentInclude;
}
export interface CommentsDestroyRequest {
query: CommentsDestroyRequestQuery;
}
export interface CommentsDestroy {
method: CommentsDestroyMethod;
path: CommentsDestroyPath;
pathParams: CommentsDestroyPathParams;
request: CommentsDestroyRequest;
}ts
export * from './comments';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 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 Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import type { SortDirection } from '../api';
import type { Post } from './post';
import type { Video } from './video';
import * as z from 'zod';
import { SortDirectionSchema } from '../api';
import { PostSchema } from './post';
import { VideoSchema } from './video';
export const CommentCommentableTypeSchema = z.enum(['post', 'video']);
export const CommentCommentableSchema: z.ZodType<CommentCommentable> = z.lazy(
() =>
z.discriminatedUnion('commentableType', [
PostSchema.extend({ commentableType: z.literal('post') }),
VideoSchema.extend({ commentableType: z.literal('video') }),
]),
);
export const CommentFilterSchema: z.ZodType<CommentFilter> = z.lazy(() =>
z.object({
AND: z.array(CommentFilterSchema).optional(),
commentableType: CommentCommentableTypeFilterSchema.optional(),
NOT: CommentFilterSchema.optional(),
OR: z.array(CommentFilterSchema).optional(),
}),
);
export const CommentSchema = z.object({
authorName: z.string().nullable(),
body: z.string(),
commentable: CommentCommentableSchema.optional(),
commentableId: z.string(),
commentableType: z.string(),
createdAt: z.iso.datetime(),
id: z.string(),
updatedAt: z.iso.datetime(),
});
export const CommentCommentableTypeFilterSchema = z.union([
CommentCommentableTypeSchema,
z
.object({
eq: CommentCommentableTypeSchema,
in: z.array(CommentCommentableTypeSchema),
})
.partial(),
]);
export const CommentCreatePayloadSchema = z.object({
authorName: z.string().nullable().default(null),
body: z.string(),
commentableId: z.string(),
commentableType: ["post", "video"]Schema,
});
export const CommentIncludeSchema = z.object({
commentable: z.boolean().optional(),
});
export const CommentPageSchema = z.object({
number: z.number().int().min(1).optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const CommentSortSchema = z.object({
createdAt: SortDirectionSchema.optional(),
updatedAt: SortDirectionSchema.optional(),
});
export const CommentUpdatePayloadSchema = z.object({
authorName: z.string().nullable().optional(),
body: z.string().optional(),
commentableId: z.string().optional(),
commentableType: ["post", "video"]Schema.optional(),
});
export type CommentCommentableType = 'post' | 'video';
export type CommentCommentable =
| (Post & { commentableType: 'post' })
| (Video & { commentableType: 'video' });
export interface CommentFilter {
AND?: CommentFilter[];
commentableType?: CommentCommentableTypeFilter;
NOT?: CommentFilter;
OR?: CommentFilter[];
}
export interface Comment {
authorName: string | null;
body: string;
commentable?: CommentCommentable;
commentableId: string;
commentableType: string;
createdAt: string;
id: string;
updatedAt: string;
}
export type CommentCommentableTypeFilter =
| CommentCommentableType
| { eq?: CommentCommentableType; in?: CommentCommentableType[] };
export interface CommentCreatePayload {
authorName?: string | null;
body: string;
commentableId: string;
commentableType: ['post', 'video'];
}
export interface CommentInclude {
commentable?: boolean;
}
export interface CommentPage {
number?: number;
size?: number;
}
export interface CommentSort {
createdAt?: SortDirection;
updatedAt?: SortDirection;
}
export interface CommentUpdatePayload {
authorName?: string | null;
body?: string;
commentableId?: string;
commentableType?: ['post', 'video'];
}ts
export * from './comment';
export * from './post';
export * from './video';ts
import type { Comment } from './comment';
import * as z from 'zod';
import { CommentSchema } from './comment';
export const PostSchema = z.object({
body: z.string().nullable(),
comments: z.array(CommentSchema).optional(),
createdAt: z.iso.datetime(),
id: z.string(),
title: z.string(),
updatedAt: z.iso.datetime(),
});
export interface Post {
body: string | null;
comments?: Comment[];
createdAt: string;
id: string;
title: string;
updatedAt: string;
}ts
import type { Comment } from './comment';
import * as z from 'zod';
import { CommentSchema } from './comment';
export const VideoSchema = z.object({
comments: z.array(CommentSchema).optional(),
createdAt: z.iso.datetime(),
duration: z.number().int().nullable(),
id: z.string(),
title: z.string(),
updatedAt: z.iso.datetime(),
url: z.string(),
});
export interface Video {
comments?: Comment[];
createdAt: string;
duration: number | null;
id: string;
title: string;
updatedAt: string;
url: string;
}ts
import type { OffsetPagination } from '../api';
import type {
Comment,
CommentCreatePayload,
CommentFilter,
CommentInclude,
CommentPage,
CommentSort,
CommentUpdatePayload,
} from '../domains/comment';
import * as z from 'zod';
import { OffsetPaginationSchema } from '../api';
import {
CommentCreatePayloadSchema,
CommentFilterSchema,
CommentIncludeSchema,
CommentPageSchema,
CommentSchema,
CommentSortSchema,
CommentUpdatePayloadSchema,
} from '../domains/comment';
export const CommentsIndexRequestQuerySchema = z.object({
filter: z
.union([CommentFilterSchema, z.array(CommentFilterSchema)])
.optional(),
include: CommentIncludeSchema.optional(),
page: CommentPageSchema.optional(),
sort: z.union([CommentSortSchema, z.array(CommentSortSchema)]).optional(),
});
export const CommentsIndexResponseBodySchema = z.object({
comments: z.array(CommentSchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: OffsetPaginationSchema,
});
export const CommentsShowPathParamsSchema = z.object({ id: z.string() });
export const CommentsShowRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export const CommentsShowResponseBodySchema = z.object({
comment: CommentSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const CommentsCreateRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export const CommentsCreateRequestBodySchema = z.object({
comment: CommentCreatePayloadSchema,
});
export const CommentsCreateResponseBodySchema = z.object({
comment: CommentSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const CommentsUpdatePathParamsSchema = z.object({ id: z.string() });
export const CommentsUpdateRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export const CommentsUpdateRequestBodySchema = z.object({
comment: CommentUpdatePayloadSchema,
});
export const CommentsUpdateResponseBodySchema = z.object({
comment: CommentSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const CommentsDestroyPathParamsSchema = z.object({ id: z.string() });
export const CommentsDestroyRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export type CommentsIndexMethod = 'GET';
export type CommentsIndexPath = '/comments';
export interface CommentsIndexRequestQuery {
filter?: CommentFilter | CommentFilter[];
include?: CommentInclude;
page?: CommentPage;
sort?: CommentSort | CommentSort[];
}
export type CommentsIndexResponseBody = {
comments: Comment[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface CommentsIndexRequest {
query: CommentsIndexRequestQuery;
}
export interface CommentsIndexResponse {
body: CommentsIndexResponseBody;
}
export interface CommentsIndex {
method: CommentsIndexMethod;
path: CommentsIndexPath;
request: CommentsIndexRequest;
response: CommentsIndexResponse;
}
export type CommentsShowMethod = 'GET';
export type CommentsShowPath = '/comments/:id';
export interface CommentsShowPathParams {
id: string;
}
export interface CommentsShowRequestQuery {
include?: CommentInclude;
}
export type CommentsShowResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsShowRequest {
query: CommentsShowRequestQuery;
}
export interface CommentsShowResponse {
body: CommentsShowResponseBody;
}
export interface CommentsShow {
method: CommentsShowMethod;
path: CommentsShowPath;
pathParams: CommentsShowPathParams;
request: CommentsShowRequest;
response: CommentsShowResponse;
}
export type CommentsCreateMethod = 'POST';
export type CommentsCreatePath = '/comments';
export interface CommentsCreateRequestQuery {
include?: CommentInclude;
}
export interface CommentsCreateRequestBody {
comment: CommentCreatePayload;
}
export type CommentsCreateResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsCreateRequest {
body: CommentsCreateRequestBody;
query: CommentsCreateRequestQuery;
}
export interface CommentsCreateResponse {
body: CommentsCreateResponseBody;
}
export type CommentsCreateErrors = 422;
export interface CommentsCreate {
errors: CommentsCreateErrors;
method: CommentsCreateMethod;
path: CommentsCreatePath;
request: CommentsCreateRequest;
response: CommentsCreateResponse;
}
export type CommentsUpdateMethod = 'PATCH';
export type CommentsUpdatePath = '/comments/:id';
export interface CommentsUpdatePathParams {
id: string;
}
export interface CommentsUpdateRequestQuery {
include?: CommentInclude;
}
export interface CommentsUpdateRequestBody {
comment: CommentUpdatePayload;
}
export type CommentsUpdateResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsUpdateRequest {
body: CommentsUpdateRequestBody;
query: CommentsUpdateRequestQuery;
}
export interface CommentsUpdateResponse {
body: CommentsUpdateResponseBody;
}
export type CommentsUpdateErrors = 422;
export interface CommentsUpdate {
errors: CommentsUpdateErrors;
method: CommentsUpdateMethod;
path: CommentsUpdatePath;
pathParams: CommentsUpdatePathParams;
request: CommentsUpdateRequest;
response: CommentsUpdateResponse;
}
export type CommentsDestroyMethod = 'DELETE';
export type CommentsDestroyPath = '/comments/:id';
export interface CommentsDestroyPathParams {
id: string;
}
export interface CommentsDestroyRequestQuery {
include?: CommentInclude;
}
export interface CommentsDestroyRequest {
query: CommentsDestroyRequestQuery;
}
export interface CommentsDestroy {
method: CommentsDestroyMethod;
path: CommentsDestroyPath;
pathParams: CommentsDestroyPathParams;
request: CommentsDestroyRequest;
}ts
export * from './comments';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 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 Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import type { CommentsOperationTree } from './endpoints';
import { createClientFactory } from 'sorbus';
import { contract } from './contract';
export interface Client {
comments: CommentsOperationTree;
}
export const createClient = createClientFactory<Client>(contract);ts
import { ErrorSchema } from './api';
import { comments } from './endpoints';
export const contract = {
endpoints: {
comments,
},
error: ErrorSchema,
} as const;ts
import type { SortDirection } from '../api';
import type { Post } from './post';
import type { Video } from './video';
import * as z from 'zod';
import { SortDirectionSchema } from '../api';
import { PostSchema } from './post';
import { VideoSchema } from './video';
export const CommentCommentableTypeSchema = z.enum(['post', 'video']);
export const CommentCommentableSchema: z.ZodType<CommentCommentable> = z.lazy(
() =>
z.discriminatedUnion('commentableType', [
PostSchema.extend({ commentableType: z.literal('post') }),
VideoSchema.extend({ commentableType: z.literal('video') }),
]),
);
export const CommentFilterSchema: z.ZodType<CommentFilter> = z.lazy(() =>
z.object({
AND: z.array(CommentFilterSchema).optional(),
commentableType: CommentCommentableTypeFilterSchema.optional(),
NOT: CommentFilterSchema.optional(),
OR: z.array(CommentFilterSchema).optional(),
}),
);
export const CommentSchema = z.object({
authorName: z.string().nullable(),
body: z.string(),
commentable: CommentCommentableSchema.optional(),
commentableId: z.string(),
commentableType: z.string(),
createdAt: z.iso.datetime(),
id: z.string(),
updatedAt: z.iso.datetime(),
});
export const CommentCommentableTypeFilterSchema = z.union([
CommentCommentableTypeSchema,
z
.object({
eq: CommentCommentableTypeSchema,
in: z.array(CommentCommentableTypeSchema),
})
.partial(),
]);
export const CommentCreatePayloadSchema = z.object({
authorName: z.string().nullable().default(null),
body: z.string(),
commentableId: z.string(),
commentableType: ["post", "video"]Schema,
});
export const CommentIncludeSchema = z.object({
commentable: z.boolean().optional(),
});
export const CommentPageSchema = z.object({
number: z.number().int().min(1).optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const CommentSortSchema = z.object({
createdAt: SortDirectionSchema.optional(),
updatedAt: SortDirectionSchema.optional(),
});
export const CommentUpdatePayloadSchema = z.object({
authorName: z.string().nullable().optional(),
body: z.string().optional(),
commentableId: z.string().optional(),
commentableType: ["post", "video"]Schema.optional(),
});
export type CommentCommentableType = 'post' | 'video';
export type CommentCommentable =
| (Post & { commentableType: 'post' })
| (Video & { commentableType: 'video' });
export interface CommentFilter {
AND?: CommentFilter[];
commentableType?: CommentCommentableTypeFilter;
NOT?: CommentFilter;
OR?: CommentFilter[];
}
export interface Comment {
authorName: string | null;
body: string;
commentable?: CommentCommentable;
commentableId: string;
commentableType: string;
createdAt: string;
id: string;
updatedAt: string;
}
export type CommentCommentableTypeFilter =
| CommentCommentableType
| { eq?: CommentCommentableType; in?: CommentCommentableType[] };
export interface CommentCreatePayload {
authorName?: string | null;
body: string;
commentableId: string;
commentableType: ['post', 'video'];
}
export interface CommentInclude {
commentable?: boolean;
}
export interface CommentPage {
number?: number;
size?: number;
}
export interface CommentSort {
createdAt?: SortDirection;
updatedAt?: SortDirection;
}
export interface CommentUpdatePayload {
authorName?: string | null;
body?: string;
commentableId?: string;
commentableType?: ['post', 'video'];
}ts
export * from './comment';
export * from './post';
export * from './video';ts
import type { Comment } from './comment';
import * as z from 'zod';
import { CommentSchema } from './comment';
export const PostSchema = z.object({
body: z.string().nullable(),
comments: z.array(CommentSchema).optional(),
createdAt: z.iso.datetime(),
id: z.string(),
title: z.string(),
updatedAt: z.iso.datetime(),
});
export interface Post {
body: string | null;
comments?: Comment[];
createdAt: string;
id: string;
title: string;
updatedAt: string;
}ts
import type { Comment } from './comment';
import * as z from 'zod';
import { CommentSchema } from './comment';
export const VideoSchema = z.object({
comments: z.array(CommentSchema).optional(),
createdAt: z.iso.datetime(),
duration: z.number().int().nullable(),
id: z.string(),
title: z.string(),
updatedAt: z.iso.datetime(),
url: z.string(),
});
export interface Video {
comments?: Comment[];
createdAt: string;
duration: number | null;
id: string;
title: string;
updatedAt: string;
url: string;
}ts
import type { Operation } from 'sorbus';
import type { OffsetPagination } from '../api';
import type {
Comment,
CommentCreatePayload,
CommentFilter,
CommentInclude,
CommentPage,
CommentSort,
CommentUpdatePayload,
} from '../domains/comment';
import * as z from 'zod';
import { OffsetPaginationSchema } from '../api';
import {
CommentCreatePayloadSchema,
CommentFilterSchema,
CommentIncludeSchema,
CommentPageSchema,
CommentSchema,
CommentSortSchema,
CommentUpdatePayloadSchema,
} from '../domains/comment';
export const CommentsIndexRequestQuerySchema = z.object({
filter: z
.union([CommentFilterSchema, z.array(CommentFilterSchema)])
.optional(),
include: CommentIncludeSchema.optional(),
page: CommentPageSchema.optional(),
sort: z.union([CommentSortSchema, z.array(CommentSortSchema)]).optional(),
});
export const CommentsIndexResponseBodySchema = z.object({
comments: z.array(CommentSchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: OffsetPaginationSchema,
});
export const CommentsShowPathParamsSchema = z.object({ id: z.string() });
export const CommentsShowRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export const CommentsShowResponseBodySchema = z.object({
comment: CommentSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const CommentsCreateRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export const CommentsCreateRequestBodySchema = z.object({
comment: CommentCreatePayloadSchema,
});
export const CommentsCreateResponseBodySchema = z.object({
comment: CommentSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const CommentsUpdatePathParamsSchema = z.object({ id: z.string() });
export const CommentsUpdateRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export const CommentsUpdateRequestBodySchema = z.object({
comment: CommentUpdatePayloadSchema,
});
export const CommentsUpdateResponseBodySchema = z.object({
comment: CommentSchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const CommentsDestroyPathParamsSchema = z.object({ id: z.string() });
export const CommentsDestroyRequestQuerySchema = z.object({
include: CommentIncludeSchema.optional(),
});
export type CommentsIndexMethod = 'GET';
export type CommentsIndexPath = '/comments';
export interface CommentsIndexRequestQuery {
filter?: CommentFilter | CommentFilter[];
include?: CommentInclude;
page?: CommentPage;
sort?: CommentSort | CommentSort[];
}
export type CommentsIndexResponseBody = {
comments: Comment[];
meta?: Record<string, unknown>;
pagination: OffsetPagination;
};
export interface CommentsIndexRequest {
query: CommentsIndexRequestQuery;
}
export interface CommentsIndexResponse {
body: CommentsIndexResponseBody;
}
export interface CommentsIndex {
method: CommentsIndexMethod;
path: CommentsIndexPath;
request: CommentsIndexRequest;
response: CommentsIndexResponse;
}
export type CommentsShowMethod = 'GET';
export type CommentsShowPath = '/comments/:id';
export interface CommentsShowPathParams {
id: string;
}
export interface CommentsShowRequestQuery {
include?: CommentInclude;
}
export type CommentsShowResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsShowRequest {
query: CommentsShowRequestQuery;
}
export interface CommentsShowResponse {
body: CommentsShowResponseBody;
}
export interface CommentsShow {
method: CommentsShowMethod;
path: CommentsShowPath;
pathParams: CommentsShowPathParams;
request: CommentsShowRequest;
response: CommentsShowResponse;
}
export type CommentsCreateMethod = 'POST';
export type CommentsCreatePath = '/comments';
export interface CommentsCreateRequestQuery {
include?: CommentInclude;
}
export interface CommentsCreateRequestBody {
comment: CommentCreatePayload;
}
export type CommentsCreateResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsCreateRequest {
body: CommentsCreateRequestBody;
query: CommentsCreateRequestQuery;
}
export interface CommentsCreateResponse {
body: CommentsCreateResponseBody;
}
export type CommentsCreateErrors = 422;
export interface CommentsCreate {
errors: CommentsCreateErrors;
method: CommentsCreateMethod;
path: CommentsCreatePath;
request: CommentsCreateRequest;
response: CommentsCreateResponse;
}
export type CommentsUpdateMethod = 'PATCH';
export type CommentsUpdatePath = '/comments/:id';
export interface CommentsUpdatePathParams {
id: string;
}
export interface CommentsUpdateRequestQuery {
include?: CommentInclude;
}
export interface CommentsUpdateRequestBody {
comment: CommentUpdatePayload;
}
export type CommentsUpdateResponseBody = {
comment: Comment;
meta?: Record<string, unknown>;
};
export interface CommentsUpdateRequest {
body: CommentsUpdateRequestBody;
query: CommentsUpdateRequestQuery;
}
export interface CommentsUpdateResponse {
body: CommentsUpdateResponseBody;
}
export type CommentsUpdateErrors = 422;
export interface CommentsUpdate {
errors: CommentsUpdateErrors;
method: CommentsUpdateMethod;
path: CommentsUpdatePath;
pathParams: CommentsUpdatePathParams;
request: CommentsUpdateRequest;
response: CommentsUpdateResponse;
}
export type CommentsDestroyMethod = 'DELETE';
export type CommentsDestroyPath = '/comments/:id';
export interface CommentsDestroyPathParams {
id: string;
}
export interface CommentsDestroyRequestQuery {
include?: CommentInclude;
}
export interface CommentsDestroyRequest {
query: CommentsDestroyRequestQuery;
}
export interface CommentsDestroy {
method: CommentsDestroyMethod;
path: CommentsDestroyPath;
pathParams: CommentsDestroyPathParams;
request: CommentsDestroyRequest;
}
export const comments = {
create: {
errors: [422],
method: 'POST',
path: '/comments',
request: {
body: CommentsCreateRequestBodySchema,
query: CommentsCreateRequestQuerySchema,
},
response: {
body: CommentsCreateResponseBodySchema,
},
},
destroy: {
method: 'DELETE',
path: '/comments/:id',
pathParams: CommentsDestroyPathParamsSchema,
request: {
query: CommentsDestroyRequestQuerySchema,
},
},
index: {
method: 'GET',
path: '/comments',
request: {
query: CommentsIndexRequestQuerySchema,
},
response: {
body: CommentsIndexResponseBodySchema,
},
},
show: {
method: 'GET',
path: '/comments/:id',
pathParams: CommentsShowPathParamsSchema,
request: {
query: CommentsShowRequestQuerySchema,
},
response: {
body: CommentsShowResponseBodySchema,
},
},
update: {
errors: [422],
method: 'PATCH',
path: '/comments/:id',
pathParams: CommentsUpdatePathParamsSchema,
request: {
body: CommentsUpdateRequestBodySchema,
query: CommentsUpdateRequestQuerySchema,
},
response: {
body: CommentsUpdateResponseBodySchema,
},
},
} as const;
export interface CommentsOperationTree {
create: Operation<CommentsCreate>;
destroy: Operation<CommentsDestroy>;
index: Operation<CommentsIndex>;
show: Operation<CommentsShow>;
update: Operation<CommentsUpdate>;
}ts
export * from './comments';