Contributing to Meshery Schemas
This comprehensive guide covers everything you need to know to contribute to the Meshery Schemas repository. Meshery follows Schema-Driven Development (SDD), where the structure of data is centrally defined using schemas that power consistency, validation, and code generation across the platform.
Table of Contents
- Overview
- Prerequisites
- Quick Start
- Schema Directory Structure
- Understanding
api.yml- The Construct Index File - Schema File Roles
- Naming Conventions
- Adding a New Schema
- Modifying Existing Schemas
- Code Generation
- Vendor Extensions (
x-*Annotations) - Go Helper Files
- TypeScript Integration
- Common Schema Patterns
- Template Files
- What NOT to Commit
- Testing Your Changes
- Common Mistakes to Avoid
- Checklist for Schema Changes
- Getting Help
Overview
Meshery schemas offer a powerful system designed for:
- Model-Driven Management: Meshery uses explicit models for describing infrastructure and applications.
- Dynamic Discovery: Process different kinds of relationships and styles for adaptive configurations.
- Lifecycle Management: Track status and lifecycle of resources via schema properties.
- Extensibility: Open-ended metadata and modular schema components enable expansion.
- Visual Representation: Properties for styling edges and nodes create user-friendly visuals.
- Automated Operations: Support validation, automated configuration, and patching.
Meshery uses the OpenAPI v3 specification with a modular, versioned, and extensible schema strategy:
- ✅ Versioned schemas for backward compatibility
- 🧩 Modular constructs for maintainability and reuse
- 🧪 Schemas are used for validation, API documentation, and automatic code generation
Prerequisites
Before contributing, ensure you have the following installed:
1. Go (v1.24.0+)
# Verify installation
go version
2. oapi-codegen
Essential for generating Go code from OpenAPI specifications:
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
export PATH="${GOPATH:-$HOME/go}/bin:$PATH"
3. Node.js & npm
Required for TypeScript generation and build process:
# Verify installation
node --version
npm --version
4. make
The repository uses Makefiles for automation:
# Verify installation
make --version
Quick Start
# Clone the repository
git clone https://github.com/meshery/schemas.git
cd schemas
# Install dependencies
make setup
npm install
# Generate all code (Go, TypeScript, RTK Query)
make build
# Build TypeScript distribution
npm run build
Schema Directory Structure
All schemas are located in the schemas/constructs/ directory:
schemas/
├── constructs/
│ ├── <schema-version>/ # e.g., v1alpha1, v1beta1
│ │ └── <construct>/ # e.g., model, component, design
│ │ ├── api.yml # Index file: refs subschemas + defines API endpoints
│ │ ├── <construct>.yaml # Subschema: data model definition
│ │ ├── <construct>_core.yml # Subschema: core/shared types (optional)
│ │ └── templates/ # Manually defined template files
│ │ ├── <construct>_template.json
│ │ └── <construct>_template.yaml
│ │
│ ├── v1alpha1/
│ │ ├── core/
│ │ │ └── api.yml # Core schema definitions (timestamps, UUIDs, etc.)
│ │ └── capability/
│ │ └── api.yml
│ │
│ ├── v1alpha3/
│ │ └── relationship/
│ │ ├── api.yml
│ │ ├── relationship_core.yml
│ │ └── templates/
│ │
│ └── v1beta1/
│ ├── model/
│ │ ├── api.yml
│ │ ├── model.yaml
│ │ ├── model_core.yml
│ │ └── templates/
│ ├── component/
│ │ ├── api.yml
│ │ ├── component.yaml
│ │ └── templates/
│ ├── design/
│ ├── environment/
│ ├── workspace/
│ └── ...
│
├── models/ # Auto-generated Go code (do NOT commit)
│ └── <version>/<package>/<package>.go
│
├── typescript/
│ ├── index.ts # Manually maintained - public API surface
│ ├── generated/ # Auto-generated TypeScript (do NOT commit)
│ │ └── <version>/<package>/
│ │ ├── <Package>.d.ts # Type definitions
│ │ └── <Package>Schema.ts # Schema as JS object
│ └── rtk/ # RTK Query client configurations
│
├── dist/ # Built distribution (do NOT commit)
│ ├── index.js, index.d.ts
│ ├── cloudApi.js, mesheryApi.js
│ └── constructs/<version>/<package>/<Package>Schema.js
│
└── _openapi_build/ # Bundled OpenAPI specs (do NOT commit)
├── merged_openapi.yml
├── cloud_openapi.yml
└── meshery_openapi.yml
Understanding api.yml - The Construct Index File
Each construct directory contains an api.yml file that serves as the index file for that construct. This is the entry point for code generation tools.
The Three Roles of api.yml
- References all subschemas - Aggregates and references all related schema definitions via
$ref - Defines API endpoints - Contains all REST operations (GET, POST, PUT, DELETE) for the construct
- Acts as the entry point - Used by code generators (oapi-codegen, openapi-typescript)
Example api.yml Structure
openapi: 3.0.0
info:
title: Model API
version: v1beta1
paths:
/api/models:
get:
operationId: getModels
summary: Get all models
responses:
"200":
description: Success
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ModelDefinition"
post:
operationId: createModel
summary: Create a new model
# ...
components:
schemas:
ModelDefinition:
$ref: "./model.yaml#/ModelDefinition" # Reference to subschema
ModelReference:
$ref: "./model_core.yml#/ModelReference" # Reference to another subschema
Schema File Roles
| File | Purpose |
|---|---|
api.yml | Index file - aggregates all subschemas via $ref and defines API endpoints for the construct |
<construct>.yaml | Subschema - defines the main data model (noun) for the construct |
<other-subschemas>.yml | Subschema - defines core/shared types used by the main schema |
templates/*.json | Templates - example instances with default values |
Naming Conventions
Property Names
- Use camelCase for property fields:
schemaVersion,displayName,componentsCount - Identifier fields use lowerCamelCase with “Id” suffix:
modelId,registrantId,categoryId - Enums use lowercase words:
enabled,ignored,duplicate
OpenAPI Schema Names
- PascalCase nouns under
components/schemas:Model,Component,Design - Files/folders are lowercase:
api.yml,model.yaml,component.yaml - Template files:
templates/<construct>_template.json
Endpoints and Operations
- Paths under
/apiwith kebab-case, plural nouns:/api/workspaces,/api/environments - Path params are camelCase:
{subscriptionId},{connectionId} - Non-CRUD actions append a verb segment:
.../register,.../export,.../cancel operationIdis camelCase VerbNoun:getModels,createDesign,registerMeshmodels
Versioning
schemaVersionuses group/version:models.meshery.io/v1beta1,components.meshery.io/v1beta1- Version strings follow k8s-style:
v1,v1alpha1,v1beta1 - Semver fields use standard SemVer:
1.0.0,2.3.1
DB-Mirrored Fields
DB-mirrored fields such as created_at, updated_at, and user_id intentionally remain snake_case to mirror existing database columns. Do not rename these to camelCase.
Adding a New Schema
Step 1: Create the Directory Structure
mkdir -p schemas/constructs/v1beta1/mypackage/templates
Step 2: Create the Index File (api.yml)
Create schemas/constructs/v1beta1/mypackage/api.yml:
openapi: 3.0.0
info:
title: MyPackage API
version: v1beta1
description: API for managing MyPackage resources
paths:
/api/mypackages:
get:
operationId: getMyPackages
summary: Get all mypackages
tags:
- MyPackage
responses:
"200":
description: Success
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/MyPackage"
post:
operationId: createMyPackage
summary: Create a new mypackage
tags:
- MyPackage
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/MyPackage"
responses:
"201":
description: Created
components:
schemas:
MyPackage:
$ref: "./mypackage.yaml#/MyPackage"
Step 3: Create Subschema Files (Optional)
Create schemas/constructs/v1beta1/mypackage/mypackage.yaml:
MyPackage:
type: object
required:
- id
- name
properties:
id:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/uuid
x-order: 1
name:
type: string
description: Name of the package
minLength: 1
maxLength: 100
x-order: 2
description:
type: string
description: Description of the package
x-order: 3
created_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/created_at
x-order: 10
updated_at:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/updated_at
x-order: 11
Step 4: Create Template Files
Create schemas/constructs/v1beta1/mypackage/templates/mypackage_template.json:
{
"id": "00000000-0000-0000-0000-000000000000",
"name": "Example Package",
"description": "An example package instance",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z"
}
Step 5: Build and Test
# Run the build - your schema will be automatically discovered
make build
npm run build
# Verify Go code was generated
ls models/v1beta1/mypackage/
# Verify TypeScript was generated
ls typescript/generated/v1beta1/mypackage/
Modifying Existing Schemas
Adding a New Field
- Update the schema / yml file (e.g.,
model.yaml or api.yml):
properties:
# ... existing properties
newField:
type: string
description: Description of the new field
x-order: 20
x-oapi-codegen-extra-tags:
yaml: "newField"
json: "newField"
- Update template files with default values:
{
"newField": "default value"
}
- Run the build:
make build
npm run build
Adding a New API Endpoint
Edit the api.yml file to add new paths:
paths:
# ... existing paths
/api/models/{id}/export:
post:
operationId: exportModel
summary: Export a model
parameters:
- $ref: "../../v1alpha1/core/api.yml#/components/parameters/id"
responses:
"200":
description: Model exported successfully
Code Generation
The build system generates code from your schemas automatically.
Build Pipeline
schemas/constructs/**/*.yml (you write these)
│
▼
bundle-openapi.js (bundles + dereferences schemas)
│
├──▶ generate-golang.js → models/**/*.go (Go structs via oapi-codegen)
├──▶ generate-typescript.js → typescript/generated/ (TypeScript types)
└──▶ generate-rtk.js → typescript/rtk/ (RTK Query hooks)
What Gets Generated
| Output | Location | Description |
|---|---|---|
| Go structs | models/<version>/<package>/ | Strongly-typed models for backend |
| TypeScript types | typescript/generated/<version>/<package>/<Package>.d.ts | Interface definitions |
| TypeScript schemas | typescript/generated/<version>/<package>/<Package>Schema.ts | OpenAPI schema as const JS object |
| RTK Query clients | typescript/rtk/ | Auto-generated API hooks for Redux |
| Bundled OpenAPI | _openapi_build/ | Merged API specifications |
Build Commands
| Command | Description |
|---|---|
make build | Full build: bundles OpenAPI + generates Go/TypeScript |
make bundle-openapi | Bundle and merge OpenAPI specs only |
make generate-golang | Generate Go code (requires bundled specs) |
make generate-ts | Generate TypeScript types and schemas |
npm run build | Build TypeScript distribution with tsup |
Dynamic Schema Discovery
Schemas are discovered automatically by scanning schemas/constructs/ for directories containing an api.yml file. No manual configuration needed!
Vendor Extensions (x-* Annotations)
OpenAPI vendor extensions provide metadata that the Meshery build pipeline uses to control code generation behavior.
x-oapi-codegen-extra-tags
Added to individual properties to inject custom Go struct tags into generated code. Commonly used for JSON, YAML, and database column mappings.
roleName:
type: string
x-oapi-codegen-extra-tags:
json: "role_name,omitempty"
yaml: "role_name,omitempty"
db: "role_name"
Do not add this to properties that already inherit it through a $ref to a core schema — the tags are already defined in the referenced definition.
x-go-type and x-go-type-import
Tells oapi-codegen to use a specific Go type for a property instead of generating a new struct. Use this for cross-package references and for complex field types like core.Map.
metadata:
type: object
additionalProperties: true
x-go-type: "core.Map"
x-go-type-skip-optional-pointer: true
x-oapi-codegen-extra-tags:
db: "metadata"
For cross-package references:
plan:
$ref: "../plan/api.yml#/components/schemas/Plan"
x-go-type: "planv1beta1.Plan"
x-go-type-import:
path: "github.com/meshery/schemas/models/v1beta1/plan"
name: planv1beta1
x-internal
Applied to individual API operations (get, post, etc.) to scope them to a specific deployment target. The build pipeline uses this tag to split the merged OpenAPI spec into cloud_openapi.yml (for Meshery Cloud) and meshery_openapi.yml (for Meshery OSS).
paths:
/api/entitlement/plans:
get:
x-internal: ["cloud"] # Only included in the cloud bundle
operationId: getPlans
Operations without x-internal are included in all bundles.
x-generate-db-helpers
x-generate-db-helpers: true is an optional, schema-level annotation placed on a named component under components/schemas (not on individual properties). It directs the Go generator to automatically produce Scan() and Value() SQL driver methods for that type in the auto-generated file zz_generated.helpers.go.
These methods implement Go’s sql.Scanner and driver.Valuer interfaces, allowing the struct to be transparently serialized as JSON when reading from or writing to a database column.
When to use it
Use x-generate-db-helpers: true when both of the following are true:
- The schema type has a dedicated OpenAPI schema component with explicit, named properties.
- The type is persisted as a JSON blob in a single database column, not spread across a dedicated relational table with one column per field.
When NOT to use it
Do not use x-generate-db-helpers for:
- Amorphous types with no fixed property set (e.g., a freeform
metadataobject). Usex-go-type: "core.Map"for those fields instead. - Types that map to a full database table with individual columns for each property — these are handled by normal DB tag generation on each field.
Example
components:
schemas:
Quiz:
x-generate-db-helpers: true # schema-level annotation
type: object
required:
- id
- title
properties:
id:
$ref: "../../v1alpha1/core/api.yml#/components/schemas/uuid"
title:
type: string
# ... additional properties
The generator produces the following in zz_generated.helpers.go:
func (value *Quiz) Scan(src interface{}) error {
if src == nil {
*value = Quiz{}
return nil
}
mapVal := core.Map{}
if err := mapVal.Scan(src); err != nil {
return err
}
return core.MapToStruct(mapVal, value)
}
func (value Quiz) Value() (driver.Value, error) {
mapVal, err := core.StructToMap(value)
if err != nil {
return nil, err
}
return core.Map(mapVal).Value()
}
Counter-example — metadata
A metadata field is also stored as a JSON blob in the database, but it is amorphous — it has no fixed property list. For this reason it uses x-go-type: "core.Map" rather than x-generate-db-helpers:
metadata:
type: object
additionalProperties: true
x-go-type: "core.Map"
x-go-type-skip-optional-pointer: true
x-oapi-codegen-extra-tags:
db: "metadata"
Go Helper Files
While Go structs are auto-generated from schemas, you often need to add custom methods to make these structs compatible with databases, implement interfaces, or add utility functions. This is done through manually created helper files.
When to Create Helper Files
Create a helper file (*_helper.go or helpers.go) in the generated package when you need:
| Use Case | Description |
|---|---|
| SQL Driver Compatibility | Implement database/sql/driver.Scanner and driver.Valuer interfaces (consider using x-generate-db-helpers instead for types with fixed schemas stored as JSON blobs) |
| Entity Interface | Implement the entity.Entity interface for database CRUD operations |
| GORM Table Names | Define custom table names via TableName() method |
| Utility Methods | Add helper functions for serialization, validation, or business logic |
| Type Conversions | Add methods to convert between related types |
Helper File Location
models/
├── core/
│ ├── core.go # Auto-generated (do NOT edit)
│ ├── helpers.go # Manual: utility functions
│ ├── datatype_map.go # Manual: Map type with SQL driver methods
│ └── datatype_null_time.go # Manual: NullTime with SQL driver methods
├── v1beta1/
│ ├── model/
│ │ ├── model.go # Auto-generated (do NOT edit)
│ │ └── model_helper.go # Manual: Entity interface, TableName, etc.
│ ├── component/
│ │ ├── component.go # Auto-generated (do NOT edit)
│ │ └── component_helper.go # Manual: Entity interface, TableName, etc.
│ └── category/
│ ├── category.go # Auto-generated (do NOT edit)
│ └── category_helper.go # Manual: Entity interface, TableName, etc.
SQL Driver Interface Implementation
Tip: For types with a fixed schema that are stored as a JSON blob in a single database column, prefer the
x-generate-db-helpers: trueannotation on the schema component. This auto-generates theScanandValuemethods for you. Use manual helper files only when the auto-generated methods are insufficient (e.g., custom serialization logic is needed).
To store complex types (structs, maps, slices) in SQL databases, implement Scan and Value methods:
// helpers.go - This is NOT autogenerated
package mypackage
import (
"database/sql/driver"
"encoding/json"
"github.com/meshery/schemas/models/core"
)
// Scan implements sql.Scanner interface for reading from database
func (m *MyComplexType) Scan(value interface{}) error {
mapVal := core.Map{}
err := mapVal.Scan(value)
if err != nil {
return err
}
return core.MapToStruct(mapVal, m)
}
// Value implements driver.Valuer interface for writing to database
func (m MyComplexType) Value() (driver.Value, error) {
mapVal, err := core.StructToMap(m)
if err != nil {
return nil, err
}
return core.Map(mapVal).Value()
}
Entity Interface Implementation
For structs that need database CRUD operations, implement the entity.Entity interface:
// component_helper.go - This is NOT autogenerated
package component
import (
"fmt"
"sync"
"github.com/gofrs/uuid"
"github.com/meshery/meshkit/database"
"github.com/meshery/meshkit/models/meshmodel/entity"
"gorm.io/gorm/clause"
)
// TableName returns the database table name for GORM
func (c ComponentDefinition) TableName() string {
return "component_definition_dbs"
}
// Type returns the entity type identifier
func (c ComponentDefinition) Type() entity.EntityType {
return entity.ComponentDefinition
}
// GenerateID generates a new UUID for the entity
func (c *ComponentDefinition) GenerateID() (uuid.UUID, error) {
return uuid.NewV4()
}
// GetID returns the entity's ID
func (c ComponentDefinition) GetID() uuid.UUID {
return c.Id
}
// GetEntityDetail returns a human-readable description
func (c *ComponentDefinition) GetEntityDetail() string {
return fmt.Sprintf("type: %s, name: %s, model: %s",
c.Type(), c.DisplayName, c.Model.Name)
}
// Create inserts the entity into the database
func (c *ComponentDefinition) Create(db *database.Handler, hostID uuid.UUID) (uuid.UUID, error) {
c.Id, _ = c.GenerateID()
err := db.Omit(clause.Associations).Create(&c).Error
return c.Id, err
}
// UpdateStatus updates the entity's status in the database
func (c *ComponentDefinition) UpdateStatus(db *database.Handler, status entity.EntityStatus) error {
return nil
}
Deterministic ID Generation
For entities that need consistent IDs based on their content (to prevent duplicates):
// model_helper.go
package model
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"github.com/gofrs/uuid"
)
func (m *ModelDefinition) GenerateID() (uuid.UUID, error) {
// Create identifier from unique fields
modelIdentifier := ModelDefinition{
Registrant: m.Registrant,
Version: m.Version,
SchemaVersion: m.SchemaVersion,
Name: m.Name,
Model: Model{
Version: m.Model.Version,
},
}
byt, err := json.Marshal(modelIdentifier)
if err != nil {
return uuid.UUID{}, err
}
hash := md5.Sum(byt)
return uuid.FromString(hex.EncodeToString(hash[:]))
}
Type Alias and Conversion Helpers
Add type aliases and conversion methods for convenience:
// model_helper.go
package model
// Type alias for cleaner code
type Styles = ComponentDefinition_Styles
// ToReference converts full definition to a lightweight reference
func (m ModelDefinition) ToReference() ModelReference {
return ModelReference{
Name: m.Name,
Version: m.Version,
DisplayName: m.DisplayName,
Model: m.Model,
Registrant: RegistrantReference{
Kind: m.Registrant.Kind,
},
}
}
Core Utility Types
The models/core/ package provides reusable types with built-in SQL compatibility:
| Type | Purpose | Use Case |
|---|---|---|
core.Map | map[string]any with SQL support | Storing JSON objects in database |
core.NullTime | Nullable time with JSON/YAML support | Optional timestamp fields (e.g., deleted_at) |
core.Time | Time wrapper with custom formatting | Required timestamp fields |
Using Core Types:
package mypackage
import "github.com/meshery/schemas/models/core"
// For nullable timestamps (e.g., deleted_at)
type MyStruct struct {
DeletedAt core.NullTime `json:"deleted_at" gorm:"column:deleted_at"`
}
// For JSON metadata stored as blob
type MyStruct struct {
Metadata core.Map `json:"metadata" gorm:"type:bytes;serializer:json"`
}
Important Notes for Helper Files
- File Header Comment: Always add
// This is not autogenerated.at the top - Same Package: Helper files must be in the same package as the generated code
- DO Commit Helper Files: Unlike generated
.gofiles, helper files ARE committed to the repository - Naming Convention: Use
<package>_helper.goorhelpers.go - Pointer vs Value Receivers: Use pointer receivers for methods that modify the struct
TypeScript Integration
Using Generated Types
import { v1beta1, v1alpha1 } from "@meshery/schemas";
const component: v1beta1.Component = { /* ... */ };
const model: v1beta1.Model = { /* ... */ };
const design: v1beta1.Design = { /* ... */ };
Using Schema Exports
// From main index
import {
ModelDefinitionV1Beta1OpenApiSchema,
ComponentDefinitionV1Beta1OpenApiSchema,
} from "@meshery/schemas";
// Direct import
import ModelSchema from "@meshery/schemas/dist/constructs/v1beta1/model/ModelSchema";
Maintaining typescript/index.ts
The typescript/index.ts file is manually maintained and defines the public API surface. When adding new constructs:
- Import components from the generated
.d.tsfile - Import the schema from the generated
*Schema.tsfile - Add type exports to the appropriate namespace
// Type imports (no .d.ts extension)
import { components as MyPackageComponents } from "./generated/v1beta1/mypackage/MyPackage";
// Schema imports
import MyPackageV1Beta1Schema from "./generated/v1beta1/mypackage/MyPackageSchema";
// Export in namespace
export namespace v1beta1 {
export type MyPackage = MyPackageComponents["schemas"]["MyPackage"];
}
// Export schema
export { MyPackageV1Beta1Schema };
Common Schema Patterns
Core Schema References
Always use the non-deprecated references from v1alpha1/core/api.yml:
| Type | Reference |
|---|---|
| UUID | ../../v1alpha1/core/api.yml#/components/schemas/uuid |
| Timestamp (created) | ../../v1alpha1/core/api.yml#/components/schemas/created_at |
| Timestamp (updated) | ../../v1alpha1/core/api.yml#/components/schemas/updated_at |
| Version String | ../../v1alpha1/core/api.yml#/components/schemas/versionString |
| Semver String | ../../v1alpha1/core/api.yml#/components/schemas/semverString |
| Input String | ../../v1alpha1/core/api.yml#/components/schemas/inputString |
Timestamp Fields Pattern
properties:
createdAt:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/created_at
x-order: 14
updatedAt:
$ref: ../../v1alpha1/core/api.yml#/components/schemas/updated_at
x-order: 15
Preserving Field Order with x-order
Use the x-order tag to ensure fields appear in a specific order in generated code:
properties:
id:
type: string
x-order: 1
name:
type: string
x-order: 2
description:
type: string
x-order: 3
Referencing Other Constructs
When referencing models or other constructs, add x-go-type and x-go-import-path to avoid generating redundant Go structs:
model:
$ref: ../model/api.yml#/components/schemas/ModelReference
x-go-type: model.ModelReference
x-go-type-import:
path: github.com/meshery/schemas/models/v1beta1/model
description: Reference to the model
Annotating API Paths for Filtering
Use x-internal to control which bundled output includes the path:
paths:
/api/entitlement/plans:
get:
x-internal: ["cloud"] # Only included in cloud_openapi.yml
operationId: getPlans
# ...
- With
x-internal: Included only in the specified clients - Without
x-internal: Included in all clients
Template Files
Templates are manually defined files in the templates/ subdirectory. They provide example instances with default values.
Template Structure
constructs/v1beta1/model/templates/
├── model_template.json # Default JSON template
├── model_template.yaml # Default YAML template
├── model_minimal_template.json # Minimal variant (optional)
└── model_full_template.yaml # Full variant (optional)
Template Content Example
{
"id": "00000000-0000-0000-0000-000000000000",
"schemaVersion": "models.meshery.io/v1beta1",
"version": "1.0.0",
"name": "example-model",
"displayName": "Example Model",
"description": "An example model template",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z"
}
When to Update Templates
- When adding new required fields to a schema
- When changing default values
- When adding new optional fields that should have example values
What NOT to Commit
CRITICAL: Do not commit generated files. Only commit source schema files.
❌ Do NOT Commit
| Directory/File | Description |
|---|---|
models/<version>/<package>/<package>.go | Auto-generated Go structs |
typescript/generated/ | Generated TypeScript types and schemas |
dist/ | Built distribution files |
_openapi_build/ | Bundled OpenAPI specs |
merged_openapi.yml | Generated merged spec |
cloud_openapi.yml | Generated cloud spec |
meshery_openapi.yml | Generated meshery spec |
✅ DO Commit
| Directory/File | Description |
|---|---|
constructs/<version>/<package>/api.yml | Index file for each construct |
constructs/<version>/<package>/*.yaml | Subschema files |
constructs/<version>/<package>/*.json | Schema files in JSON format |
constructs/<version>/<package>/templates/ | Template files |
typescript/index.ts | Manually maintained public API |
models/<version>/<package>/*_helper.go | Manual Go helper files (SQL drivers, Entity interface) |
models/<version>/<package>/helpers.go | Manual Go utility functions |
models/core/datatype_*.go | Manual Core data type definitions |
Note: The
models/directory contains both auto-generated files (e.g.,model.go) and manually created helper files (e.g.,model_helper.go). Only the auto-generated struct files should NOT be committed. Helper files that implement interfaces, SQL drivers, and utility methods ARE committed and maintained manually.
Testing Your Changes
Run Full Build
make build
npm run build
Run Go Tests
go test ./...
Verify Generated Code
# Check Go code compiles
go build ./...
# Check TypeScript compiles
npm run build
Common Mistakes to Avoid
- ❌ Committing auto-generated Go structs (
models/<pkg>/<pkg>.go) - but DO commit helper files - ❌ Using deprecated
core.jsonreferences instead ofv1alpha1/core/api.yml - ❌ Adding redundant
x-oapi-codegen-extra-tagswhen using core schema references - ❌ Forgetting to update template files when adding new fields
- ❌ Not testing the build after schema changes
- ❌ Placing template files outside the
templates/subdirectory - ❌ Using
.d.tsextension in TypeScript import paths - ❌ Assuming schema property names are PascalCase (check actual generated files)
- ❌ Not adding new schemas to
typescript/index.tsfor public API exposure - ❌ Forgetting
x-go-typewhen referencing other constructs - ❌ Editing auto-generated
.gofiles instead of creating helper files - ❌ Forgetting
// This is not autogenerated.comment in helper files - ❌ Missing
TableName()method in helper files for GORM entities - ❌ Not implementing
Scan()/Value()for complex types stored in SQL - ❌ Writing manual
Scan()/Value()methods whenx-generate-db-helpers: trueon the schema component would auto-generate them - ❌ Using
x-generate-db-helperson amorphous types (usex-go-type: "core.Map"instead)
Checklist for Schema Changes
Before submitting a PR, verify:
- Modified only schema YAML/JSON files (not auto-generated code)
- Created/updated
api.ymlas the index file if adding new construct - Referenced all subschemas from
api.yml - Used non-deprecated
v1alpha1/core/api.ymlreferences - Updated corresponding template files with default values
- Removed redundant
x-oapi-codegen-extra-tagswhen using core refs - Used
x-generate-db-helpers: truefor types with fixed schemas stored as JSON blobs - Created helper files (
*_helper.go) only for cases not covered by auto-generation - Added
// This is not autogenerated.comment to helper files - Implemented
TableName(),Scan(),Value()as needed in helper files - Used
sync.Mutexfor thread-safeCreate()methods - Added
x-ordertags for consistent field ordering - Ran
make buildsuccessfully - Ran
go test ./...successfully - Ran
npm run buildsuccessfully - Updated
typescript/index.tsif adding new public types - Verified only source schema files are in the commit
1. Mesheryctl Contributor Flow
a. Add a new schema on a new command
Example: You want to add a mesheryctl model build command.
Steps:
- Add the new verb in
openapi.yamlunder the appropriate construct (e.g.,model/) - Update
<construct>.jsonif new properties are needed - Run:
make generate-types
make golang-generate
- Implement the CLI logic
- Add tests (Check existing unit tests for format)
b. Add an existing schema on an existing command
Example: You detect a part of existing code that is not following the schema driven development principle (model is a struct created in mesheryctl command), you have two options:
- If you know how to implement, update the existing code to use a proper struct generated from the
meshery/schemasrepository- Update the CLI logic
- Add/Adjust tests if needed
- If you don’t know how to implement it, open an issue on Github using either a mesheryctl issue template (feature, bug)
c. Add a new schema on an existing command
Steps:
- Add the new verb in
openapi.yamlunder the appropriate construct (e.g.,model/) - Update
<construct>.jsonif new properties are needed - Run:
make generate-types
make golang-generate
- Update the CLI logic
- Add/Adjust tests if needed
Why it matters: This reduces drift between backend logic and API contract, enforces consistency between Meshery’s components (Server, UI, CLI) and , resulting in higher quality code.
2. Meshery Server Contributor Flow
Example: Add a new status field to component.
Steps:
- Add the new property in
component.json - Run:
make validate-schemas
make golang-generate
- The generated Go structs (from
oapi-codegen) are used in the backend. - If the backend uses GORM with auto-migration enabled, these structs may be used to update the DB schema.
- Avoid manually editing the generated models, as they will be overwritten when schemas are regenerated.
Why it matters: This reduces drift between backend logic and API contract, enforces consistency between Meshery’s components (Server, UI, CLI) and , resulting in higher quality code.
3. Meshery UI Contributor Flow
Example: Show the new version field on the Model dashboard.
Steps:
- Check
openapi.yamlto verify the new field exists - Wait for the backend to regenerate and expose the property
- Use RTK + TypeScript types to access and render data
Note:
make generate-typesnow generates only TypeScript types and schema-related objects._template.json/_template.yamlfiles are no longer auto-generated.
Why it matters: UI stays in sync with the backend - fewer bugs, fewer mismatches, easier onboarding.
4. Meshery Docs Contributor Flow
Example: You are writing a guide! Steps:
- Read the schema structure and workflows
- Walk through the scenarios above
- Write a guide that’s accurate, actionable, and friendly
Why it matters: Docs are often the first impression contributors get. Schema-driven clarity starts here.
Getting Help
- GitHub Issues - Report bugs or request features
- Community Slack - Real-time discussions with maintainers
- Weekly Meetings - Join our community calls
Further Reading
- meshery/schemas README - Full reference for schema authoring
- AGENTS.md - Contributor checklist
- Core schema definitions - Reusable building blocks
- Academy construct - Exemplar for advanced patterns including
x-generate-db-helpers
Community Resources For more contribution guidelines, see the Meshery Contributing Guide.