Schema-Driven Development
What Is Schema-Driven Development?
Meshery follows a schema-first approach: every API resource, database entity, and client-side type is derived from a single set of OpenAPI YAML files in this repository. When a schema changes here, the change automatically propagates into:
- Go structs consumed by
meshery/mesheryandlayer5io/meshery-cloud - TypeScript types published as
@meshery/schemason npm - RTK Query hooks used by the Meshery UI and Layer5 Cloud frontend
- OpenAPI documentation served at this site
This eliminates drift between backend and frontend, ensures validation is consistent, and makes the schema the authoritative contract for every Meshery integration.
How It Works
schemas/constructs/<version>/<construct>/
├── api.yml ← You write this
└── <construct>.yaml ← You write this
│
▼
make bundle-openapi → _openapi_build/merged_openapi.yml
│
┌─────┼──────────────────────────┐
▼ ▼ ▼
oapi-codegen openapi-typescript RTK codegen
│ │ │
▼ ▼ ▼
models/v1beta1/ typescript/generated/ typescript/rtk/
<construct>.go <construct>.d.ts cloudApi.ts
mesheryApi.ts
│
▼
npm publish @meshery/schemas
The Flow
- You define the schema — a
<construct>.yamlfile for the entity and anapi.ymlfile with endpoints, payloads, and pagination envelopes. make bundle-openapimerges all per-constructapi.ymlfiles into unified OpenAPI bundles (one for Meshery Server, one for Layer5 Cloud, using thex-internalannotation).- Go generation —
oapi-codegenreads the bundled spec and produces Go structs with JSON/YAML/DB tags inmodels/<version>/<construct>/. - TypeScript generation —
openapi-typescriptproduces.d.tstype definitions intypescript/generated/. - RTK generation — a custom codegen reads the bundled spec and produces
createApihooks intypescript/rtk/. - npm publish — the TypeScript distribution is built with
tsupand published as@meshery/schemas.
Source of Truth Rules
| Migration Stage | Source of Truth |
|---|---|
| Pre-migration — construct being moved from downstream repo | The downstream implementation is the reference for field names, types, and tags |
| Post-migration — schema defined here | This repository is the permanent authority. Downstream must conform. |
Once a construct lives here, do not weaken schemas or skip validation to accommodate legacy downstream code. If a downstream implementation diverges, the downstream code must be updated.
Key Design Principles
The Dual-Schema Pattern
Every writable entity has two schema definitions:
<construct>.yaml— the full server-side object as returned in API responses, including all server-generated fields (id,created_at,updated_at,deleted_at) andadditionalProperties: false.{Construct}Payload— defined inapi.yml, containing only client-settable fields. AllPOST/PUTrequest bodies reference the Payload, never the entity schema.
See Add a Construct for the step-by-step walkthrough.
Casing Is Schema-Determined
Field casing isn’t arbitrary — it’s determined by whether the field maps to a database column:
- DB-backed fields use the exact
snake_casecolumn name - Non-DB fields use
camelCase
See Naming Rules for the complete reference.
Versioning Is Explicit
Schema versions (v1alpha1, v1beta1, v1beta2) are directory-level. Partial casing migrations within a version are forbidden. If the wire format must change, introduce a new version.
See Versioning for the rules.
Before You Change a Schema
- Have you read the Naming Rules?
- Does your entity follow the dual-schema pattern?
- Have you run
make buildandmake validate-schemas? - Are you only modifying schema YAML files (not generated code)?
Downstream Propagation Map
meshery/schemas (this repo)
│
├──► meshery/meshery
│ Go structs (models/)
│ OpenAPI docs (docs/)
│
├──► layer5io/meshery-cloud
│ TypeScript types (typescript/generated/)
│ RTK Query hooks (typescript/rtk/)
│
└──► npm: @meshery/schemas
Published package consumed by any frontend
Changes here affect multiple repositories. Always verify with make build before opening a PR.