Add a Construct
This guide walks you through adding a new entity schema to meshery/schemas. By the end, you’ll have a fully conformant construct with entity schema, payload schema, API endpoints, and template files.
Prerequisites
- Node.js and Go installed
- Repository cloned and dependencies installed (
make setup) - Familiarity with Schema-Driven Development
Step 1: Create the Directory Structure
Every construct lives under schemas/constructs/<version>/<construct>/:
schemas/constructs/v1beta1/keychain/
├── api.yml # OpenAPI spec: endpoints + schemas
├── keychain.yaml # Entity (response) schema
└── templates/
├── keychain_template.json # Example instance (JSON)
└── keychain_template.yaml # Example instance (YAML)
Step 2: Define the Entity Schema
The <construct>.yaml file represents the full server-side object as returned in API responses.
Required properties of every entity .yaml:
type: objectat the rootadditionalProperties: falseat the root- All server-generated fields in
properties:id,created_at,updated_at,deleted_at - Server-generated fields that are always present in
required
# keychain.yaml
type: object
additionalProperties: false
required:
- id
- name
- owner
- created_at
- updated_at
properties:
id:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
description: Unique identifier for the keychain.
name:
type: string
description: Human-readable name of the keychain.
minLength: 1
maxLength: 500
description:
type: string
description: Optional description of the keychain purpose.
maxLength: 2000
owner:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
description: ID of the user who owns this keychain.
created_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/created_at
description: Timestamp when the keychain was created.
updated_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/updated_at
description: Timestamp when the keychain was last updated.
deleted_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/nullTime
description: Soft-delete timestamp; null if not deleted.
Step 3: Define the Payload Schema in api.yml
For any entity with POST or PUT operations, define a {Construct}Payload in api.yml that contains only client-settable fields:
components:
schemas:
KeychainPayload:
type: object
description: Payload for creating or updating a keychain.
required:
- name
properties:
id:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
description: Existing keychain ID for updates; omit on create.
x-oapi-codegen-extra-tags:
json: "id,omitempty"
name:
type: string
description: Human-readable name of the keychain.
minLength: 1
maxLength: 500
description:
type: string
description: Optional description.
maxLength: 2000
owner:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
description: ID of the owning user.
x-oapi-codegen-extra-tags:
json: "owner,omitempty"
POST/PUT requestBody. This forces clients to supply server-generated fields like created_at. Always reference *Payload.
Step 4: Define API Endpoints
In the same api.yml, define paths, parameters, and operations:
openapi: 3.0.0
info:
title: Keychain API
version: v1beta1
security:
- jwt: []
tags:
- name: Keychains
description: Operations for managing keychains.
paths:
/api/auth/keychains:
get:
tags: [Keychains]
operationId: getKeychains
summary: List all keychains
parameters:
- $ref: "../core/api.yml#/components/parameters/page"
- $ref: "../core/api.yml#/components/parameters/pagesize"
- $ref: "../core/api.yml#/components/parameters/search"
- $ref: "../core/api.yml#/components/parameters/order"
responses:
"200":
description: Keychains list
content:
application/json:
schema:
$ref: "#/components/schemas/KeychainPage"
post:
tags: [Keychains]
operationId: createKeychain
summary: Create a new keychain
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/KeychainPayload"
responses:
"201":
description: Keychain created
content:
application/json:
schema:
$ref: "#/components/schemas/Keychain"
/api/auth/keychains/{keychainId}:
get:
tags: [Keychains]
operationId: getKeychainById
parameters:
- $ref: "#/components/parameters/keychainId"
responses:
"200":
description: Keychain details
content:
application/json:
schema:
$ref: "#/components/schemas/Keychain"
put:
tags: [Keychains]
operationId: updateKeychain
parameters:
- $ref: "#/components/parameters/keychainId"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/KeychainPayload"
responses:
"200":
description: Keychain updated
content:
application/json:
schema:
$ref: "#/components/schemas/Keychain"
delete:
tags: [Keychains]
operationId: deleteKeychain
parameters:
- $ref: "#/components/parameters/keychainId"
responses:
"204":
description: Keychain deleted
Key Conventions
| Element | Rule | Example |
|---|---|---|
operationId |
lower camelCase verbNoun | createKeychain |
| Path segments | kebab-case, plural | /api/auth/keychains |
| Path parameters | camelCase + Id |
{keychainId} |
| POST (create only) | Returns 201 |
Not 200 |
| DELETE (single) | Returns 204, no body |
|
| Bulk delete | POST .../delete |
Not DELETE with body |
| Every operation | At least one tags entry |
[Keychains] |
Step 5: Create Template Files
Add example instances in templates/. These are embedded in documentation and used for testing.
// templates/keychain_template.json
{
"id": "00000000-0000-0000-0000-000000000000",
"name": "My Keychain",
"description": "",
"owner": "00000000-0000-0000-0000-000000000000",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"deleted_at": null
}
type: array, use [] not {}. If type: string, use "" not {}.
Step 6: Verify
Run the full build and validation:
make build # Generate Go structs + TS types + RTK clients
make validate-schemas # Run schema validation rules
go test ./... # Go tests
npm run build # TypeScript distribution
PR Checklist
Before opening your pull request, verify:
<construct>.yamlhasadditionalProperties: false<construct>.yamllists all server-generated fields inpropertiesandrequiredapi.ymldefines{Construct}Payloadwith only client-settable fields- All
POST/PUTrequestBodyentries reference{Construct}Payload GETresponses reference the full{Construct}entity schemaoperationIdis lower camelCase verbNoun- Path parameters are camelCase with
Idsuffix - No
DELETEoperation has arequestBody POSTfor creation only returns201- Every operation has at least one
tagsentry - String properties have
description,maxLength - Numeric properties have
minimum,maximum - ID properties have
format: uuidorx-id-format: external - Template files are in
templates/with correct value types make buildpassesmake validate-schemaspasses- Only schema YAML files are in the commit (not generated code)
Canonical References
When uncertain, model new schemas on these constructs:
connection/—connection.yaml+ConnectionPayloadinapi.ymlkey/—key.yaml+KeyPayloadinapi.ymlteam/—team.yaml+teamPayload/teamUpdatePayloadinapi.ymlenvironment/—environment.yaml+environmentPayloadinapi.yml