Cursor Pagination
Cursor-based pagination for navigating large datasets
API Definition
config/apis/grumpy_panda.rb
rb
# frozen_string_literal: true
Apiwork::API.define '/grumpy_panda' do
key_format :camel
export :openapi
export :apiwork
resources :activities
endModels
app/models/grumpy_panda/activity.rb
rb
# frozen_string_literal: true
module GrumpyPanda
class Activity < ApplicationRecord
validates :action, presence: true
end
endDatabase Table
| Column | Type | Nullable | Default |
|---|---|---|---|
| id | string | ||
| action | string | ||
| created_at | datetime | ||
| occurred_at | datetime | ✓ | |
| updated_at | datetime |
Representations
app/representations/grumpy_panda/activity_representation.rb
rb
# frozen_string_literal: true
module GrumpyPanda
class ActivityRepresentation < Apiwork::Representation::Base
adapter do
pagination do
strategy :cursor
default_size 3
end
end
attribute :id
attribute :action, writable: true
attribute :occurred_at, writable: true
attribute :created_at
attribute :updated_at
end
endContracts
app/contracts/grumpy_panda/activity_contract.rb
rb
# frozen_string_literal: true
module GrumpyPanda
class ActivityContract < Apiwork::Contract::Base
representation ActivityRepresentation
end
endControllers
app/controllers/grumpy_panda/activities_controller.rb
rb
# frozen_string_literal: true
module GrumpyPanda
class ActivitiesController < ApplicationController
before_action :set_activity, only: %i[show update destroy]
def index
activities = Activity.all
expose activities
end
def show
expose activity
end
def create
activity = Activity.create(contract.body[:activity])
expose activity
end
def update
activity.update(contract.body[:activity])
expose activity
end
def destroy
activity.destroy
expose activity
end
private
attr_reader :activity
def set_activity
@activity = Activity.find(params[:id])
end
end
endRequest Examples
First page
Request
http
GET /grumpy_panda/activitiesResponse 200
json
{
"activities": [
{
"id": "05601e34-4e5e-5293-a94c-d7f265d247b4",
"action": "user.logout",
"occurredAt": "2024-01-01T11:00:00.000Z",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
},
{
"id": "28fab1f1-3992-5d5c-9d68-d136bc923c6e",
"action": "user.login",
"occurredAt": "2024-01-01T10:00:00.000Z",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
},
{
"id": "4c39275f-ec14-5a37-858d-84eb6899b55d",
"action": "post.delete",
"occurredAt": "2024-01-01T14:00:00.000Z",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
],
"pagination": {
"next": "eyJpZCI6IjRjMzkyNzVmLWVjMTQtNWEzNy04NThkLTg0ZWI2ODk5YjU1ZCJ9",
"prev": null
}
}Next page
Request
http
GET /grumpy_panda/activities?page[after]=eyJpZCI6IjRjMzkyNzVmLWVjMTQtNWEzNy04NThkLTg0ZWI2ODk5YjU1ZCJ9Response 200
json
{
"activities": [
{
"id": "941cf71e-f960-5659-8955-cdd06fb62148",
"action": "post.create",
"occurredAt": "2024-01-01T12:00:00.000Z",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
},
{
"id": "d7d2baf6-242e-5a3b-8e5f-258913655538",
"action": "post.update",
"occurredAt": "2024-01-01T13:00:00.000Z",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-04-01T12:00:00.000Z"
}
],
"pagination": {
"next": null,
"prev": "eyJpZCI6Ijk0MWNmNzFlLWY5NjAtNTY1OS04OTU1LWNkZDA2ZmI2MjE0OCJ9"
}
}Exports
OpenAPI
yml
---
openapi: 3.1.0
info:
title: "/grumpy_panda"
version: 1.0.0
paths:
"/activities":
get:
operationId: activitiesIndex
parameters:
- in: query
name: page
required: false
schema:
"$ref": "#/components/schemas/activityPage"
responses:
'200':
content:
application/json:
schema:
properties:
activities:
items:
"$ref": "#/components/schemas/activity"
type: array
meta:
properties: {}
type: object
pagination:
"$ref": "#/components/schemas/cursorPagination"
type: object
required:
- activities
- pagination
description: ''
post:
operationId: activitiesCreate
requestBody:
content:
application/json:
schema:
properties:
activity:
"$ref": "#/components/schemas/activityCreatePayload"
type: object
required:
- activity
required: true
responses:
'200':
content:
application/json:
schema:
properties:
activity:
"$ref": "#/components/schemas/activity"
meta:
properties: {}
type: object
type: object
required:
- activity
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
"/activities/{id}":
get:
operationId: activitiesShow
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
content:
application/json:
schema:
properties:
activity:
"$ref": "#/components/schemas/activity"
meta:
properties: {}
type: object
type: object
required:
- activity
description: ''
patch:
operationId: activitiesUpdate
parameters:
- in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
properties:
activity:
"$ref": "#/components/schemas/activityUpdatePayload"
type: object
required:
- activity
required: true
responses:
'200':
content:
application/json:
schema:
properties:
activity:
"$ref": "#/components/schemas/activity"
meta:
properties: {}
type: object
type: object
required:
- activity
description: ''
'422':
description: Unprocessable Entity
content:
application/json:
schema:
properties:
issues:
items:
"$ref": "#/components/schemas/error"
type: array
required:
- issues
type: object
delete:
operationId: activitiesDestroy
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'204':
description: ''
components:
schemas:
activity:
properties:
action:
type: string
createdAt:
type: string
format: date-time
id:
type: string
occurredAt:
type:
- string
- 'null'
format: date-time
updatedAt:
type: string
format: date-time
type: object
required:
- action
- createdAt
- id
- occurredAt
- updatedAt
activityCreatePayload:
properties:
action:
type: string
occurredAt:
type:
- string
- 'null'
format: date-time
default:
type: object
required:
- action
activityPage:
properties:
after:
type: string
before:
type: string
size:
type: integer
minimum: 1
maximum: 100
type: object
activityUpdatePayload:
properties:
action:
type: string
occurredAt:
type:
- string
- 'null'
format: date-time
type: object
cursorPagination:
properties:
next:
type:
- string
- 'null'
prev:
type:
- string
- 'null'
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
- pointerApiwork
json
{
"base_path": "/grumpy_panda",
"enums": [
{
"deprecated": false,
"description": null,
"example": null,
"name": "error_layer",
"scope": null,
"values": [
"http",
"contract",
"domain"
]
}
],
"error_codes": [
{
"description": "Unprocessable Entity",
"name": "unprocessable_entity",
"status": 422
}
],
"fingerprint": "227366cb547e9a7f",
"info": null,
"locales": [],
"resources": [
{
"actions": [
{
"name": "activities.index",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/activities",
"raises": [],
"request": {
"body": [],
"description": null,
"query": [
{
"name": "page",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "reference",
"reference": "activity_page"
}
]
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "activities",
"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": "activity"
}
},
{
"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": "cursor_pagination"
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "activities.show",
"deprecated": false,
"description": null,
"method": "get",
"operation_id": null,
"path": "/activities/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "activity",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "activity"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "activities.create",
"deprecated": false,
"description": null,
"method": "post",
"operation_id": null,
"path": "/activities",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "activity",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "activity_create_payload"
}
],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "activity",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "activity"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "activities.update",
"deprecated": false,
"description": null,
"method": "patch",
"operation_id": null,
"path": "/activities/:id",
"raises": [
"unprocessable_entity"
],
"request": {
"body": [
{
"name": "activity",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "activity_update_payload"
}
],
"description": null,
"query": []
},
"response": {
"body": {
"deprecated": null,
"description": null,
"nullable": null,
"optional": null,
"type": "object",
"partial": null,
"shape": [
{
"name": "activity",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "activity"
},
{
"name": "meta",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "object",
"partial": false,
"shape": []
}
]
},
"description": null,
"no_content": false
},
"summary": null,
"tags": []
},
{
"name": "activities.destroy",
"deprecated": false,
"description": null,
"method": "delete",
"operation_id": null,
"path": "/activities/:id",
"raises": [],
"request": {
"body": [],
"description": null,
"query": []
},
"response": {
"body": null,
"description": null,
"no_content": true
},
"summary": null,
"tags": []
}
],
"identifier": "activities",
"name": "activities",
"parent_identifiers": [],
"path": "activities",
"resources": [],
"scope": "activity"
}
],
"types": [
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "activity",
"scope": "activity",
"type": "object",
"extends": [],
"shape": [
{
"name": "action",
"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": "occurredAt",
"deprecated": false,
"description": null,
"nullable": true,
"optional": false,
"type": "datetime",
"example": null
},
{
"name": "updatedAt",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "datetime",
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "activity_create_payload",
"scope": "activity",
"type": "object",
"extends": [],
"shape": [
{
"name": "action",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "occurredAt",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "datetime",
"default": null,
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "activity_page",
"scope": "activity",
"type": "object",
"extends": [],
"shape": [
{
"name": "after",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "before",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"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": "activity_update_payload",
"scope": "activity",
"type": "object",
"extends": [],
"shape": [
{
"name": "action",
"deprecated": false,
"description": null,
"nullable": false,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "occurredAt",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "datetime",
"example": null
}
]
},
{
"recursive": false,
"deprecated": false,
"description": null,
"example": null,
"name": "cursor_pagination",
"scope": null,
"type": "object",
"extends": [],
"shape": [
{
"name": "next",
"deprecated": false,
"description": null,
"nullable": true,
"optional": true,
"type": "string",
"example": null,
"format": null,
"max": null,
"min": null
},
{
"name": "prev",
"deprecated": false,
"description": null,
"nullable": true,
"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": "error",
"scope": null,
"type": "object",
"extends": [],
"shape": [
{
"name": "issues",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "array",
"example": null,
"max": null,
"min": null,
"of": {
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "error_issue"
}
},
{
"name": "layer",
"deprecated": false,
"description": null,
"nullable": false,
"optional": false,
"type": "reference",
"reference": "error_layer"
}
]
}
]
}Codegen
TypeScript
ts
export type ErrorLayer = 'http' | 'contract' | 'domain';
export interface CursorPagination {
next?: string | null;
prev?: string | null;
}
export interface ErrorIssue {
code: string;
detail: string;
meta: Record<string, unknown>;
path: string | number[];
pointer: string;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
export interface Activity {
action: string;
createdAt: string;
id: string;
occurredAt: string | null;
updatedAt: string;
}
export interface ActivityCreatePayload {
action: string;
occurredAt?: string | null;
}
export interface ActivityPage {
after?: string;
before?: string;
size?: number;
}
export interface ActivityUpdatePayload {
action?: string;
occurredAt?: string | null;
}ts
export * from './activity';ts
import type { CursorPagination } from '../api';
import type {
Activity,
ActivityCreatePayload,
ActivityPage,
ActivityUpdatePayload,
} from '../domains/activity';
export type ActivitiesIndexMethod = 'GET';
export type ActivitiesIndexPath = '/activities';
export interface ActivitiesIndexRequestQuery {
page?: ActivityPage;
}
export type ActivitiesIndexResponseBody = {
activities: Activity[];
meta?: Record<string, unknown>;
pagination: CursorPagination;
};
export interface ActivitiesIndexRequest {
query: ActivitiesIndexRequestQuery;
}
export interface ActivitiesIndexResponse {
body: ActivitiesIndexResponseBody;
}
export interface ActivitiesIndex {
method: ActivitiesIndexMethod;
path: ActivitiesIndexPath;
request: ActivitiesIndexRequest;
response: ActivitiesIndexResponse;
}
export type ActivitiesShowMethod = 'GET';
export type ActivitiesShowPath = '/activities/:id';
export interface ActivitiesShowPathParams {
id: string;
}
export type ActivitiesShowResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesShowResponse {
body: ActivitiesShowResponseBody;
}
export interface ActivitiesShow {
method: ActivitiesShowMethod;
path: ActivitiesShowPath;
pathParams: ActivitiesShowPathParams;
response: ActivitiesShowResponse;
}
export type ActivitiesCreateMethod = 'POST';
export type ActivitiesCreatePath = '/activities';
export interface ActivitiesCreateRequestBody {
activity: ActivityCreatePayload;
}
export type ActivitiesCreateResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesCreateRequest {
body: ActivitiesCreateRequestBody;
}
export interface ActivitiesCreateResponse {
body: ActivitiesCreateResponseBody;
}
export type ActivitiesCreateErrors = 422;
export interface ActivitiesCreate {
errors: ActivitiesCreateErrors;
method: ActivitiesCreateMethod;
path: ActivitiesCreatePath;
request: ActivitiesCreateRequest;
response: ActivitiesCreateResponse;
}
export type ActivitiesUpdateMethod = 'PATCH';
export type ActivitiesUpdatePath = '/activities/:id';
export interface ActivitiesUpdatePathParams {
id: string;
}
export interface ActivitiesUpdateRequestBody {
activity: ActivityUpdatePayload;
}
export type ActivitiesUpdateResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesUpdateRequest {
body: ActivitiesUpdateRequestBody;
}
export interface ActivitiesUpdateResponse {
body: ActivitiesUpdateResponseBody;
}
export type ActivitiesUpdateErrors = 422;
export interface ActivitiesUpdate {
errors: ActivitiesUpdateErrors;
method: ActivitiesUpdateMethod;
path: ActivitiesUpdatePath;
pathParams: ActivitiesUpdatePathParams;
request: ActivitiesUpdateRequest;
response: ActivitiesUpdateResponse;
}
export type ActivitiesDestroyMethod = 'DELETE';
export type ActivitiesDestroyPath = '/activities/:id';
export interface ActivitiesDestroyPathParams {
id: string;
}
export interface ActivitiesDestroy {
method: ActivitiesDestroyMethod;
path: ActivitiesDestroyPath;
pathParams: ActivitiesDestroyPathParams;
}ts
export * from './activities';Zod
ts
import * as z from 'zod';
export const ErrorLayerSchema = z.enum(['contract', 'domain', 'http']);
export const CursorPaginationSchema = z.object({
next: z.string().nullable().optional(),
prev: z.string().nullable().optional(),
});
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 ErrorSchema = z.object({
issues: z.array(ErrorIssueSchema),
layer: ErrorLayerSchema,
});
export type ErrorLayer = 'contract' | 'domain' | 'http';
export interface CursorPagination {
next?: string | null;
prev?: string | null;
}
export interface ErrorIssue {
code: string;
detail: string;
meta: Record<string, unknown>;
path: string | number[];
pointer: string;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import * as z from 'zod';
export const ActivitySchema = z.object({
action: z.string(),
createdAt: z.iso.datetime(),
id: z.string(),
occurredAt: z.iso.datetime().nullable(),
updatedAt: z.iso.datetime(),
});
export const ActivityCreatePayloadSchema = z.object({
action: z.string(),
occurredAt: z.iso.datetime().nullable().default(null),
});
export const ActivityPageSchema = z.object({
after: z.string().optional(),
before: z.string().optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const ActivityUpdatePayloadSchema = z.object({
action: z.string().optional(),
occurredAt: z.iso.datetime().nullable().optional(),
});
export interface Activity {
action: string;
createdAt: string;
id: string;
occurredAt: string | null;
updatedAt: string;
}
export interface ActivityCreatePayload {
action: string;
occurredAt?: string | null;
}
export interface ActivityPage {
after?: string;
before?: string;
size?: number;
}
export interface ActivityUpdatePayload {
action?: string;
occurredAt?: string | null;
}ts
export * from './activity';ts
import type { CursorPagination } from '../api';
import type {
Activity,
ActivityCreatePayload,
ActivityPage,
ActivityUpdatePayload,
} from '../domains/activity';
import * as z from 'zod';
import { CursorPaginationSchema } from '../api';
import {
ActivityCreatePayloadSchema,
ActivityPageSchema,
ActivitySchema,
ActivityUpdatePayloadSchema,
} from '../domains/activity';
export const ActivitiesIndexRequestQuerySchema = z.object({
page: ActivityPageSchema.optional(),
});
export const ActivitiesIndexResponseBodySchema = z.object({
activities: z.array(ActivitySchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: CursorPaginationSchema,
});
export const ActivitiesShowPathParamsSchema = z.object({ id: z.string() });
export const ActivitiesShowResponseBodySchema = z.object({
activity: ActivitySchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const ActivitiesCreateRequestBodySchema = z.object({
activity: ActivityCreatePayloadSchema,
});
export const ActivitiesCreateResponseBodySchema = z.object({
activity: ActivitySchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const ActivitiesUpdatePathParamsSchema = z.object({ id: z.string() });
export const ActivitiesUpdateRequestBodySchema = z.object({
activity: ActivityUpdatePayloadSchema,
});
export const ActivitiesUpdateResponseBodySchema = z.object({
activity: ActivitySchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const ActivitiesDestroyPathParamsSchema = z.object({ id: z.string() });
export type ActivitiesIndexMethod = 'GET';
export type ActivitiesIndexPath = '/activities';
export interface ActivitiesIndexRequestQuery {
page?: ActivityPage;
}
export type ActivitiesIndexResponseBody = {
activities: Activity[];
meta?: Record<string, unknown>;
pagination: CursorPagination;
};
export interface ActivitiesIndexRequest {
query: ActivitiesIndexRequestQuery;
}
export interface ActivitiesIndexResponse {
body: ActivitiesIndexResponseBody;
}
export interface ActivitiesIndex {
method: ActivitiesIndexMethod;
path: ActivitiesIndexPath;
request: ActivitiesIndexRequest;
response: ActivitiesIndexResponse;
}
export type ActivitiesShowMethod = 'GET';
export type ActivitiesShowPath = '/activities/:id';
export interface ActivitiesShowPathParams {
id: string;
}
export type ActivitiesShowResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesShowResponse {
body: ActivitiesShowResponseBody;
}
export interface ActivitiesShow {
method: ActivitiesShowMethod;
path: ActivitiesShowPath;
pathParams: ActivitiesShowPathParams;
response: ActivitiesShowResponse;
}
export type ActivitiesCreateMethod = 'POST';
export type ActivitiesCreatePath = '/activities';
export interface ActivitiesCreateRequestBody {
activity: ActivityCreatePayload;
}
export type ActivitiesCreateResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesCreateRequest {
body: ActivitiesCreateRequestBody;
}
export interface ActivitiesCreateResponse {
body: ActivitiesCreateResponseBody;
}
export type ActivitiesCreateErrors = 422;
export interface ActivitiesCreate {
errors: ActivitiesCreateErrors;
method: ActivitiesCreateMethod;
path: ActivitiesCreatePath;
request: ActivitiesCreateRequest;
response: ActivitiesCreateResponse;
}
export type ActivitiesUpdateMethod = 'PATCH';
export type ActivitiesUpdatePath = '/activities/:id';
export interface ActivitiesUpdatePathParams {
id: string;
}
export interface ActivitiesUpdateRequestBody {
activity: ActivityUpdatePayload;
}
export type ActivitiesUpdateResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesUpdateRequest {
body: ActivitiesUpdateRequestBody;
}
export interface ActivitiesUpdateResponse {
body: ActivitiesUpdateResponseBody;
}
export type ActivitiesUpdateErrors = 422;
export interface ActivitiesUpdate {
errors: ActivitiesUpdateErrors;
method: ActivitiesUpdateMethod;
path: ActivitiesUpdatePath;
pathParams: ActivitiesUpdatePathParams;
request: ActivitiesUpdateRequest;
response: ActivitiesUpdateResponse;
}
export type ActivitiesDestroyMethod = 'DELETE';
export type ActivitiesDestroyPath = '/activities/:id';
export interface ActivitiesDestroyPathParams {
id: string;
}
export interface ActivitiesDestroy {
method: ActivitiesDestroyMethod;
path: ActivitiesDestroyPath;
pathParams: ActivitiesDestroyPathParams;
}ts
export * from './activities';Sorbus
ts
import * as z from 'zod';
export const ErrorLayerSchema = z.enum(['contract', 'domain', 'http']);
export const CursorPaginationSchema = z.object({
next: z.string().nullable().optional(),
prev: z.string().nullable().optional(),
});
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 ErrorSchema = z.object({
issues: z.array(ErrorIssueSchema),
layer: ErrorLayerSchema,
});
export type ErrorLayer = 'contract' | 'domain' | 'http';
export interface CursorPagination {
next?: string | null;
prev?: string | null;
}
export interface ErrorIssue {
code: string;
detail: string;
meta: Record<string, unknown>;
path: string | number[];
pointer: string;
}
export interface Error {
issues: ErrorIssue[];
layer: ErrorLayer;
}ts
import type { ActivitiesOperationTree } from './endpoints';
import { createClientFactory } from 'sorbus';
import { contract } from './contract';
export interface Client {
activities: ActivitiesOperationTree;
}
export const createClient = createClientFactory<Client>(contract);ts
import { ErrorSchema } from './api';
import { activities } from './endpoints';
export const contract = {
endpoints: {
activities,
},
error: ErrorSchema,
} as const;ts
import * as z from 'zod';
export const ActivitySchema = z.object({
action: z.string(),
createdAt: z.iso.datetime(),
id: z.string(),
occurredAt: z.iso.datetime().nullable(),
updatedAt: z.iso.datetime(),
});
export const ActivityCreatePayloadSchema = z.object({
action: z.string(),
occurredAt: z.iso.datetime().nullable().default(null),
});
export const ActivityPageSchema = z.object({
after: z.string().optional(),
before: z.string().optional(),
size: z.number().int().min(1).max(100).optional(),
});
export const ActivityUpdatePayloadSchema = z.object({
action: z.string().optional(),
occurredAt: z.iso.datetime().nullable().optional(),
});
export interface Activity {
action: string;
createdAt: string;
id: string;
occurredAt: string | null;
updatedAt: string;
}
export interface ActivityCreatePayload {
action: string;
occurredAt?: string | null;
}
export interface ActivityPage {
after?: string;
before?: string;
size?: number;
}
export interface ActivityUpdatePayload {
action?: string;
occurredAt?: string | null;
}ts
export * from './activity';ts
import type { Operation } from 'sorbus';
import type { CursorPagination } from '../api';
import type {
Activity,
ActivityCreatePayload,
ActivityPage,
ActivityUpdatePayload,
} from '../domains/activity';
import * as z from 'zod';
import { CursorPaginationSchema } from '../api';
import {
ActivityCreatePayloadSchema,
ActivityPageSchema,
ActivitySchema,
ActivityUpdatePayloadSchema,
} from '../domains/activity';
export const ActivitiesIndexRequestQuerySchema = z.object({
page: ActivityPageSchema.optional(),
});
export const ActivitiesIndexResponseBodySchema = z.object({
activities: z.array(ActivitySchema),
meta: z.record(z.string(), z.unknown()).optional(),
pagination: CursorPaginationSchema,
});
export const ActivitiesShowPathParamsSchema = z.object({ id: z.string() });
export const ActivitiesShowResponseBodySchema = z.object({
activity: ActivitySchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const ActivitiesCreateRequestBodySchema = z.object({
activity: ActivityCreatePayloadSchema,
});
export const ActivitiesCreateResponseBodySchema = z.object({
activity: ActivitySchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const ActivitiesUpdatePathParamsSchema = z.object({ id: z.string() });
export const ActivitiesUpdateRequestBodySchema = z.object({
activity: ActivityUpdatePayloadSchema,
});
export const ActivitiesUpdateResponseBodySchema = z.object({
activity: ActivitySchema,
meta: z.record(z.string(), z.unknown()).optional(),
});
export const ActivitiesDestroyPathParamsSchema = z.object({ id: z.string() });
export type ActivitiesIndexMethod = 'GET';
export type ActivitiesIndexPath = '/activities';
export interface ActivitiesIndexRequestQuery {
page?: ActivityPage;
}
export type ActivitiesIndexResponseBody = {
activities: Activity[];
meta?: Record<string, unknown>;
pagination: CursorPagination;
};
export interface ActivitiesIndexRequest {
query: ActivitiesIndexRequestQuery;
}
export interface ActivitiesIndexResponse {
body: ActivitiesIndexResponseBody;
}
export interface ActivitiesIndex {
method: ActivitiesIndexMethod;
path: ActivitiesIndexPath;
request: ActivitiesIndexRequest;
response: ActivitiesIndexResponse;
}
export type ActivitiesShowMethod = 'GET';
export type ActivitiesShowPath = '/activities/:id';
export interface ActivitiesShowPathParams {
id: string;
}
export type ActivitiesShowResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesShowResponse {
body: ActivitiesShowResponseBody;
}
export interface ActivitiesShow {
method: ActivitiesShowMethod;
path: ActivitiesShowPath;
pathParams: ActivitiesShowPathParams;
response: ActivitiesShowResponse;
}
export type ActivitiesCreateMethod = 'POST';
export type ActivitiesCreatePath = '/activities';
export interface ActivitiesCreateRequestBody {
activity: ActivityCreatePayload;
}
export type ActivitiesCreateResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesCreateRequest {
body: ActivitiesCreateRequestBody;
}
export interface ActivitiesCreateResponse {
body: ActivitiesCreateResponseBody;
}
export type ActivitiesCreateErrors = 422;
export interface ActivitiesCreate {
errors: ActivitiesCreateErrors;
method: ActivitiesCreateMethod;
path: ActivitiesCreatePath;
request: ActivitiesCreateRequest;
response: ActivitiesCreateResponse;
}
export type ActivitiesUpdateMethod = 'PATCH';
export type ActivitiesUpdatePath = '/activities/:id';
export interface ActivitiesUpdatePathParams {
id: string;
}
export interface ActivitiesUpdateRequestBody {
activity: ActivityUpdatePayload;
}
export type ActivitiesUpdateResponseBody = {
activity: Activity;
meta?: Record<string, unknown>;
};
export interface ActivitiesUpdateRequest {
body: ActivitiesUpdateRequestBody;
}
export interface ActivitiesUpdateResponse {
body: ActivitiesUpdateResponseBody;
}
export type ActivitiesUpdateErrors = 422;
export interface ActivitiesUpdate {
errors: ActivitiesUpdateErrors;
method: ActivitiesUpdateMethod;
path: ActivitiesUpdatePath;
pathParams: ActivitiesUpdatePathParams;
request: ActivitiesUpdateRequest;
response: ActivitiesUpdateResponse;
}
export type ActivitiesDestroyMethod = 'DELETE';
export type ActivitiesDestroyPath = '/activities/:id';
export interface ActivitiesDestroyPathParams {
id: string;
}
export interface ActivitiesDestroy {
method: ActivitiesDestroyMethod;
path: ActivitiesDestroyPath;
pathParams: ActivitiesDestroyPathParams;
}
export const activities = {
create: {
errors: [422],
method: 'POST',
path: '/activities',
request: {
body: ActivitiesCreateRequestBodySchema,
},
response: {
body: ActivitiesCreateResponseBodySchema,
},
},
destroy: {
method: 'DELETE',
path: '/activities/:id',
pathParams: ActivitiesDestroyPathParamsSchema,
},
index: {
method: 'GET',
path: '/activities',
request: {
query: ActivitiesIndexRequestQuerySchema,
},
response: {
body: ActivitiesIndexResponseBodySchema,
},
},
show: {
method: 'GET',
path: '/activities/:id',
pathParams: ActivitiesShowPathParamsSchema,
response: {
body: ActivitiesShowResponseBodySchema,
},
},
update: {
errors: [422],
method: 'PATCH',
path: '/activities/:id',
pathParams: ActivitiesUpdatePathParamsSchema,
request: {
body: ActivitiesUpdateRequestBodySchema,
},
response: {
body: ActivitiesUpdateResponseBodySchema,
},
},
} as const;
export interface ActivitiesOperationTree {
create: Operation<ActivitiesCreate>;
destroy: Operation<ActivitiesDestroy>;
index: Operation<ActivitiesIndex>;
show: Operation<ActivitiesShow>;
update: Operation<ActivitiesUpdate>;
}ts
export * from './activities';