Putnami
DocsGitHub

Licensed under FSL-1.1-MIT

Getting Started
Concepts
How To
Build A Web App
Build An Api Service
Share Code Between Projects
Configure Your App
Add Persistence
Add Authentication
Add Background Jobs
Principles
Tooling & Workspace
Workspace Overview
Cli
Jobs & Commands
SDK
Error Handling
Extensions
Typescript
Go
Python
Docker
Ci
Frameworks
Typescript
OverviewWebReact RoutingForms And ActionsStatic FilesApiErrors And ResponsesConfigurationLoggingHttp And MiddlewareDependency InjectionPlugins And LifecycleSessionsAuthPersistenceEventsStorageCachingWebsocketsTestingHealth ChecksTelemetryProto GrpcSmart Client
Go
OverviewHttpDependency InjectionPlugins And LifecycleConfigurationSecurityPersistenceErrorsEventsStorageCachingLoggingTelemetryGrpcService ClientsValidationOpenapiTesting
Platform
  1. DocsSeparator
  2. FrameworksSeparator
  3. GoSeparator
  4. Openapi

OpenAPI

go.putnami.dev/openapi generates OpenAPI 3.0.3 specifications from Go struct types and endpoint metadata.

Plugin setup

import (
    "go.putnami.dev/app"
    fhttp "go.putnami.dev/http"
    "go.putnami.dev/openapi"
)

server := fhttp.NewServerPlugin(fhttp.ServerConfig{Port: 3000})
oapiPlugin := openapi.NewPlugin(openapi.PluginOptions{})

// Register the plugin — serves the spec at /_/openapi.json
oapiPlugin.RegisterOn(server)

a := app.New("my-service")
a.Module.Use(server)
a.Module.Use(oapiPlugin)

The spec is served at GET /_/openapi.json.

Documenting endpoints

Register endpoints with the plugin using the endpoint builder:

import "reflect"

type CreateUserBody struct {
    Name  string `json:"name" validate:"required,minlen=2" description:"User's display name"`
    Email string `json:"email" validate:"required,email" description:"Email address"`
}

type UserResponse struct {
    ID    string `json:"id" description:"User ID"`
    Name  string `json:"name" description:"User's display name"`
    Email string `json:"email" description:"Email address"`
}

type UserParams struct {
    ID string `json:"id" validate:"required,uuid" description:"User ID"`
}

// Register endpoint with OpenAPI metadata
oapiPlugin.AddRoute(openapi.DiscoveredRoute{
    Method:      "POST",
    Path:        "/users",
    Description: "Create a new user",
    Tags:        []string{"Users"},
    Body:        reflect.TypeOf(CreateUserBody{}),
    Returns:     reflect.TypeOf(UserResponse{}),
    Throws: []openapi.ThrowsMeta{
        {Status: 409, Description: "Email already exists"},
        {Status: 422, Description: "Validation failed"},
    },
})

oapiPlugin.AddRoute(openapi.DiscoveredRoute{
    Method:      "GET",
    Path:        "/users/{id}",
    Description: "Get a user by ID",
    Tags:        []string{"Users"},
    Params:      reflect.TypeOf(UserParams{}),
    Returns:     reflect.TypeOf(UserResponse{}),
    Throws: []openapi.ThrowsMeta{
        {Status: 404, Description: "User not found"},
    },
})

DiscoveredRoute fields

Field Type Description
Method string HTTP method
Path string Route path (supports {param} and [param])
Description string Operation description
Tags []string Grouping tags
Params reflect.Type Path parameters struct type
Query reflect.Type Query parameters struct type
Body reflect.Type Request body struct type
Returns reflect.Type Success response struct type
Throws []ThrowsMeta Possible error responses
Security *SecurityMeta Security requirements

Schema generation

The plugin generates OpenAPI schemas by introspecting Go struct types using reflection and struct tags.

Type mapping

Go type OpenAPI type Format
string string —
int, int64 integer int64
float32 number float
float64 number double
bool boolean —
[]T array —
struct object —

Struct tags for OpenAPI

Tag OpenAPI mapping Example
json:"name" Property name json:"email"
description:"text" Field description description:"User email"
validate:"required" Added to required array validate:"required"
validate:"uuid" format: "uuid" validate:"uuid"
validate:"email" format: "email" validate:"email"

Example schema

type Product struct {
    ID          string   `json:"id" validate:"required,uuid" description:"Product ID"`
    Name        string   `json:"name" validate:"required" description:"Product name"`
    Price       float64  `json:"price" validate:"required,min=0" description:"Price in USD"`
    Tags        []string `json:"tags" description:"Product tags"`
    Description string   `json:"description" description:"Product description"`
}

Generates:

{
  "type": "object",
  "required": ["id", "name", "price"],
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "description": "Product ID"
    },
    "name": {
      "type": "string",
      "description": "Product name"
    },
    "price": {
      "type": "number",
      "format": "double",
      "minimum": 0,
      "description": "Price in USD"
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Product tags"
    },
    "description": {
      "type": "string",
      "description": "Product description"
    }
  }
}

Programmatic access

Generate the spec without the plugin:

doc := openapi.GenerateSpec(routes, openapi.Options{
    Title:   "My API",
    Version: "1.0.0",
})

specJSON, err := doc.JSON()

Security metadata

Document security requirements:

oapiPlugin.AddRoute(openapi.DiscoveredRoute{
    Method:      "DELETE",
    Path:        "/users/{id}",
    Description: "Delete a user",
    Security: &openapi.SecurityMeta{
        Roles:  []string{"admin"},
        Scopes: []string{"write"},
    },
})

Related guides

  • HTTP & Middleware — endpoint registration
  • Validation — validation tags
  • Security — authorization

On this page

  • OpenAPI
  • Plugin setup
  • Documenting endpoints
  • DiscoveredRoute fields
  • Schema generation
  • Type mapping
  • Struct tags for OpenAPI
  • Example schema
  • Programmatic access
  • Security metadata
  • Related guides