agentleFS
mesherymeshery/schemas/.github/skills/create-openapi-schemas-from-golang-models/SKILL.md

create-openapi-schemas-from-golang-models

Skill85 starsChanged 30 days agoApache-2.0View on GitHub

Create OpenAPI schemas from Golang models in Meshery Cloud, generating schema artifacts in Meshery Schemas repository. Handles cross-repository schema creation workflow with proper naming conventions, Go code generation, and backwards compatibility validation.

The file

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 3 are distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read the meshery/schemas README.md and uphold all directives, including naming conventions

    line 25
  2. Distinctive:

    Read AGENTS.md in both repositories

    line 26
  3. Very specific:

    Treat the Golang models in layer5io/meshery-cloud as the source of truth

    line 27
  4. Very specific:

    If a property has x-oapi-codegen-extra-tags.db and that db value is snake_case, the schema property name and JSON tag must use that exact …

    line 51
  5. Very specific:

    Ensure generated models are in models/v1beta1/<construct_name>/

    line 224
  6. Very specific:

    If helpers are still needed for non-generated behavior (e.g., TableName(), custom business logic), create helpers.go manually.

    line 241
  7. Distinctive:

    Run full test suite:

    line 274
  8. Distinctive:

    Check router for all endpoints: - Don't miss any routes

    line 420

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Create OpenAPI Schemas from Golang Models

Canonical naming contract - see docs/identifier-naming-contributor-guide.md in meshery/schemas (https://github.com/meshery/schemas/blob/master/docs/identifier-naming-contributor-guide.md) for the full directory (26-row naming table with before/after and do/don't examples). The inline rules below remain the skill's authority for its workflow scope; the guide is the reader-friendly cross-repo reference.

Overview

This skill guides you through creating OpenAPI schemas from existing Golang models in Meshery Cloud (layer5io/meshery-cloud), with the generated schemas stored in Meshery Schemas (meshery/schemas). This cross-repository workflow ensures consistent API contracts between the two projects.

Prerequisites

Required Repositories

Both repositories must be locally cloned and available:

Required Knowledge

  1. Read the meshery/schemas README.md and uphold all directives, including naming conventions
  2. Read AGENTS.md in both repositories
  3. Treat the Golang models in layer5io/meshery-cloud as the source of truth
  4. Ignore any existing schemas in meshery/schemas for constructs being worked on
  5. Ignore any existing tests in layer5io/meshery-cloud for constructs being worked on

Naming Conventions

OpenAPI Schema Naming

ElementConventionExample
Property namesPreserve published wire-format casing: DB-backed fields use exact snake_case db names; new non-DB fields use camelCasefirst_name, organization_id, schemaVersion
Identifier fieldscamelCase + "Id" suffixroleId, userId, keychainId
Enumslowercaseadmin, user, enabled
Object namessingular nounsrole, keychain, user
Schema componentsPascalCaseRole, RolesPage, RoleHolderRequest
Files/folderslowercaserole.yaml, api.yml

Endpoint Naming

ElementConventionExample
Paths/api + kebab-case plurals/api/identity/orgs/{orgId}/roles
OperationscamelCase VerbNounGetAllRoles, UpsertRoles

If a property has x-oapi-codegen-extra-tags.db and that db value is snake_case, the schema property name and JSON tag must use that exact snake_case name. Do not camelize DB-backed fields in-place within an existing API version. Pagination envelopes must use page, page_size, and total_count. | Non-CRUD actions | append verb | .../keychains/{keychainId} |

Response descriptions and response message text must not include the word successfully. Use neutral wording such as Role deleted, Webhook processed, or Roles response.

Workflow

Task 1: Create Schema Artifacts

In meshery/schemas, create these files for the target construct:

Dual-Schema Pattern (required): Every entity needs two schemas - <construct>.yaml (full response entity) and {Construct}Payload in api.yml (write request body). Never use the entity schema as a POST/PUT requestBody. See AGENTS.md § "The Dual-Schema Pattern" for full rules.

schemas/constructs/v1beta1/<construct_name>/
├── <construct_name>.yaml          # Schema definition
├── api.yml                        # Endpoint definitions + page/payload schemas
└── templates/
    └── <construct_name>_template.json   # Example usage

Schema Definition (<construct_name>.yaml)

This is the response schema - the full persisted object as the API returns it. It must:

  • Have additionalProperties: false at the top level
  • Include all server-generated fields (id, created_at, updated_at, deleted_at) in properties and required

Map Golang struct fields to OpenAPI properties:

# Example: role.yaml
type: object
additionalProperties: false
required:
  - id
  - roleName
  - created_at
  - updated_at
properties:
  id:
    type: string
    format: uuid
    x-oapi-codegen-extra-tags:
      json: "id,omitempty"
      yaml: "id,omitempty"
      db: "id"
  roleName:
    type: string
    x-oapi-codegen-extra-tags:
      json: "role_name,omitempty"
      yaml: "role_name,omitempty"
      db: "role_name"
  description:
    type: string
  createdAt:
    type: string
    format: date-time
  updatedAt:
    type: string
    format: date-time
  deletedAt:
    type: string
    format: date-time
    nullable: true

API Definition (api.yml)

Define endpoints matching the Meshery Cloud router. All POST/PUT operations must use a {Construct}Payload request body - never the full entity schema.

openapi: 3.0.3
info:
  title: Role API
  version: v1beta1

paths:
  /api/identity/orgs/{orgId}/roles:
    get:
      operationId: getAllRoles
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: page
          in: query
          schema:
            type: integer
        - name: pagesize
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: List of roles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RolesPage'
    post:
      operationId: upsertRole
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RolePayload'  # ← Payload, NOT Role
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Role'       # ← full entity in response

components:
  schemas:
    Role:
      $ref: './role.yaml'                               # full response entity
    RolePayload:
      type: object                                      # write request body
      description: Payload for creating or updating a role.
      required:
        - roleName
      properties:
        id:
          type: string
          format: uuid
          x-oapi-codegen-extra-tags:
            json: "id,omitempty"                        # optional - for upsert
        roleName:
          type: string
        description:
          type: string
    RolesPage:
      type: object
      properties:
        page:
          type: integer
        roles:
          type: array
          items:
            $ref: '#/components/schemas/Role'
        totalCount:
          type: integer
        pageSize:
          type: integer

Go Type Hints

For cross-schema references, use x-go-type to avoid redundant structs:

registrant:
  x-go-type: "RegistrantReference"
  x-go-import-path: "github.com/meshery/schemas/models/v1beta1/core"
  $ref: "#/components/schemas/RegistrantReference"

Task 2: Generate Go Models

In the meshery/schemas repository:

cd /Users/l/code/schemas
make generate-golang

Verification Steps:

  1. Ensure generated models are in models/v1beta1/<construct_name>/
  2. If a schema type within the construct is stored as a JSON blob in a database column and has a dedicated schema definition with explicit properties, add x-generate-db-helpers: true at the schema component level in api.yml. This instructs the generator to produce Scan() and Value() SQL driver methods automatically in zz_generated.helpers.go - no manual helpers.go is needed for that type.

    # In api.yml, under components/schemas:
    Quiz:
      x-generate-db-helpers: true   # ← schema-level, not per-property
      type: object
      properties:
        id:
          $ref: "../../v1alpha1/core/api.yml#/components/schemas/uuid"
        title:
          type: string

    For amorphous JSON blob fields that lack a fixed schema definition (e.g., a freeform metadata map), use x-go-type: "core.Map" on the property instead - do not use x-generate-db-helpers for those.

  3. If helpers are still needed for non-generated behavior (e.g., TableName(), custom business logic), create helpers.go manually. When implementing Scan/Value, follow these rules (see docs/schema-authoring-reference.md § "SQL Driver (Scan/Value) Implementation Rules"):
    • Value() must always marshal - never return (nil, nil). A nil map produces JSON "null", not SQL NULL.
    • Scan() must zero the receiver (*m = nil) when src is nil, not silently return.
// helpers.go - NOT autogenerated
package role

import (
    "database/sql/driver"
    "encoding/json"
)

func (r Role) TableName() string {
    return "roles"
}

// Value implements driver.Valuer. Always marshals - never returns SQL NULL.
func (r Role) Value() (driver.Value, error) {
    b, err := json.Marshal(r)
    if err != nil {
        return nil, err
    }
    return string(b), nil
}
  1. Create/update tests:
go test ./models/v1beta1/<construct_name>/...
  1. Run full test suite:
go test ./...

Task 3: Validate Backwards Compatibility

Compare generated models with Meshery Cloud models:

Source: layer5io/meshery-cloud/server/models/<construct>.go Generated: meshery/schemas/models/v1beta1/<construct_name>/<construct_name>.go

Validation Checklist:

  • All fields present with matching types
  • JSON tags match the schema property name exactly; do not treat snake_case and camelCase as interchangeable for DB-backed fields
  • omitempty behavior preserved
  • Nullable fields handled correctly
  • Array/slice types match

Common Patterns

Pagination Response

<Construct>Page:
  type: object
  properties:
    page:
      type: integer
    data:
      type: array
      items:
        $ref: '#/components/schemas/<Construct>'
    totalCount:
      type: integer
    pageSize:
      type: integer

UUID Fields

id:
  type: string
  format: uuid
  x-oapi-codegen-extra-tags:
    json: "id,omitempty"
    db: "id"

Nullable Time Fields

deletedAt:
  type: string
  format: date-time
  nullable: true
  x-go-type: "sql.NullTime"
  x-go-import-path: "database/sql"

String Arrays (pq.StringArray)

roleNames:
  type: array
  items:
    type: string
  x-go-type: "pq.StringArray"
  x-go-import-path: "github.com/lib/pq"

Exemplar: Academy Construct

The Academy construct in meshery/schemas serves as the primary exemplar for this workflow. Study these files:

Reference Files

FileLocationPurpose
api.ymlassets/academy-api.ymlComplete OpenAPI schema with paths and components
helpers.goassets/academy-helpers.goCustom driver methods (NOT auto-generated)
Documentationreferences/meshery-schemas-academy.mdDetailed patterns and explanations

Key Patterns from Academy

  1. Cross-schema references using $ref with external files
  2. Custom Go type mappings with x-go-type and x-go-type-import
  3. Nullable fields using core.NullTime
  4. Enum definitions with proper Go type hints
  5. SQL Scanner/Valuer implementations in helpers.go

Source Repository


Example: Role Construct

Source Files in Meshery Cloud

  • Model: server/models/roles.go
  • Handler: server/handlers/roles.go
  • DAO: server/dao/roles_dao.go
  • Routes: server/router/router.go (search for "roles")

Key Endpoints

MethodPathHandler
GET/api/identity/orgs/:orgID/rolesGetAllRoles
POST/api/identity/orgs/:orgID/rolesUpsertRoles
GET/api/identity/orgs/:orgID/roles/:roleID/keychainsGetKeychainsByRoleID
POST/api/identity/orgs/:orgID/roles/:roleID/keychains/:keychainIDAssignKeychainToRole
DELETE/api/identity/orgs/:orgID/roles/:roleID/keychains/:keychainIDUnAssignKeychainFromRole

Key Models to Schema-ify

  • Role - Core role entity
  • RolesPage - Paginated response
  • UserWithRole - User with role assignments
  • RoleHolderRequest - Request payload for role assignment

Validation Commands

# Run schema validation
cd /Users/l/code/schemas
make validate-schemas

# Full build (validates + generates)
make build

# Test generated Go code
go test ./models/v1beta1/<construct>/...

Tips

  1. Start with the Golang struct - It's the source of truth
  2. Match JSON tags exactly - Critical for API compatibility
  3. Use x-oapi-codegen-extra-tags - Preserves db and yaml tags
  4. Check router for all endpoints - Don't miss any routes
  5. Run make build - Validates everything at once

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.