OpenAPI Generation

Generate comprehensive OpenAPI v3.1 specifications from protobuf definitions

The protoc-gen-openapiv3 plugin automatically creates detailed OpenAPI specifications from your protobuf service definitions, ensuring your API documentation stays perfectly synchronized with your implementation.

Table of Contents#

Overview#

The OpenAPI generation plugin bridges the gap between protobuf service definitions and modern API documentation standards. It creates complete OpenAPI v3.1 specifications that include:

  • Complete Schema Definitions - All protobuf messages converted to JSON schemas
  • Service Endpoints - RPC methods mapped to HTTP operations
  • Header Parameters - HTTP headers from service and method annotations included as parameters
  • Field Examples - Example values from protobuf field annotations included in OpenAPI
  • Type Safety - Accurate type information including enums, arrays, and nested objects
  • Documentation - Comments from protobuf definitions preserved as descriptions
  • Validation Rules - Both buf.validate constraints and header validation rules reflected in OpenAPI

Key Benefits#

  • Always Up-to-Date - Documentation generated directly from source of truth
  • Multi-Format Output - JSON and YAML formats supported
  • Client Generation - Use with OpenAPI client generators for any language
  • API Testing - Import into testing tools like Postman, Insomnia
  • Developer Experience - Rich documentation for API consumers

Installation#

go install github.com/SebastienMelki/sebuf/cmd/protoc-gen-openapiv3@latest

Verify installation:

protoc-gen-openapiv3 --version

Quick Start#

1. Define Your Service#

Create user_api.proto:

syntax = "proto3";
package userapi.v1;
 
option go_package = "github.com/yourorg/userapi/v1;userapi";
 
// User represents a system user
message User {
  // Unique user identifier
  string id = 1 [(sebuf.http.field_examples) = {
    values: [
      "550e8400-e29b-41d4-a716-446655440000",
      "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "123e4567-e89b-12d3-a456-426614174000"
    ]
  }];
  
  // User's email address
  string email = 2 [(sebuf.http.field_examples) = {
    values: [
      "alice.johnson@example.com",
      "bob.smith@example.com",
      "charlie.davis@example.com"
    ]
  }];
  
  // Full name of the user
  string name = 3 [(sebuf.http.field_examples) = {
    values: ["Alice Johnson", "Bob Smith", "Charlie Davis", "Diana Wilson"]
  }];
  
  // Age in years
  int32 age = 4 [(sebuf.http.field_examples) = {
    values: ["25", "34", "42", "28"]
  }];
  
  // Tags associated with the user
  repeated string tags = 5 [(sebuf.http.field_examples) = {
    values: ["engineer", "manager", "designer"]
  }];
  
  // Additional metadata
  map<string, string> metadata = 6;
  
  // Current user status
  UserStatus status = 7;
  
  // User's profile settings
  UserProfile profile = 8;
}
 
// UserProfile contains user profile information
message UserProfile {
  // Profile picture URL
  string avatar_url = 1;
  
  // User's bio or description
  string bio = 2;
  
  // Preferred language code (ISO 639-1)
  string language = 3;
  
  // Timezone identifier
  string timezone = 4;
}
 
// UserStatus represents the user's current status
enum UserStatus {
  USER_STATUS_UNSPECIFIED = 0;
  USER_STATUS_ACTIVE = 1;
  USER_STATUS_INACTIVE = 2;
  USER_STATUS_SUSPENDED = 3;
}
 
// Request to create a new user
message CreateUserRequest {
  // The user to create
  User user = 1;
  
  // Whether to send welcome email
  bool send_welcome_email = 2;
}
 
// Response containing the created user
message CreateUserResponse {
  // The created user with generated ID
  User user = 1;
  
  // Timestamp when user was created
  int64 created_at = 2;
}
 
// Request to get a user by ID
message GetUserRequest {
  // User ID to retrieve
  string id = 1;
}
 
// Response containing the requested user
message GetUserResponse {
  // The requested user
  User user = 1;
}
 
// Request to list users with filtering
message ListUsersRequest {
  // Maximum number of users to return
  int32 page_size = 1;
  
  // Token for pagination
  string page_token = 2;
  
  // Filter by user status
  UserStatus status_filter = 3;
  
  // Search query for name or email
  string search_query = 4;
}
 
// Response containing list of users
message ListUsersResponse {
  // List of users
  repeated User users = 1;
  
  // Token for next page
  string next_page_token = 2;
  
  // Total number of users (for pagination info)
  int32 total_count = 3;
}
 
// UserService manages users in the system
service UserService {
  // CreateUser creates a new user in the system
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  
  // GetUser retrieves an existing user by ID
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  
  // ListUsers returns a paginated list of users
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}

2. Generate OpenAPI Specification#

Create buf.yaml:

version: v2
# Add sebuf dependency if using HTTP annotations
deps:
  - buf.build/sebmelki/sebuf  # Optional, only if using HTTP annotations

Create buf.gen.yaml:

version: v2
plugins:
  - remote: buf.build/protocolbuffers/go
    out: .
    opt: paths=source_relative
  - local: protoc-gen-openapiv3
    out: ./docs
    # For JSON format instead of YAML:
    # opt: format=json
    # For custom filename:
    # opt: filename=my_api.yaml

Generate:

buf generate

Using protoc#

# Generate YAML format (default)
protoc --openapiv3_out=./docs user_api.proto
 
# Generate JSON format
protoc --openapiv3_out=./docs --openapiv3_opt=format=json user_api.proto
 
# Custom output filename
protoc --openapiv3_out=./docs --openapiv3_opt=filename=user_api.yaml user_api.proto

3. View Generated Specification#

The generated user_api.yaml will contain:

openapi: 3.1.0
info:
  title: userapi.v1 API
  version: 1.0.0
  
paths:
  /userapi/create_user:
    post:
      summary: CreateUser creates a new user in the system
      operationId: CreateUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateUserResponse'
                
  /userapi/get_user:
    post:
      summary: GetUser retrieves an existing user by ID
      operationId: GetUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GetUserRequest'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetUserResponse'
 
components:
  schemas:
    User:
      type: object
      description: User represents a system user
      properties:
        id:
          type: string
          description: Unique user identifier
          examples:
            - "550e8400-e29b-41d4-a716-446655440000"
            - "f47ac10b-58cc-4372-a567-0e02b2c3d479"
            - "123e4567-e89b-12d3-a456-426614174000"
        email:
          type: string
          description: User's email address
          examples:
            - "alice.johnson@example.com"
            - "bob.smith@example.com"
            - "charlie.davis@example.com"
        name:
          type: string
          description: Full name of the user
          examples:
            - "Alice Johnson"
            - "Bob Smith"
            - "Charlie Davis"
            - "Diana Wilson"
        age:
          type: integer
          format: int32
          description: Age in years
          examples:
            - 25
            - 34
            - 42
            - 28
        tags:
          type: array
          description: Tags associated with the user
          items:
            type: string
          examples:
            - ["engineer"]
            - ["manager"]
            - ["designer"]
        metadata:
          type: object
          description: Additional metadata
          additionalProperties:
            type: string
        status:
          $ref: '#/components/schemas/UserStatus'
        profile:
          $ref: '#/components/schemas/UserProfile'
    
    UserStatus:
      type: string
      description: UserStatus represents the user's current status
      enum:
        - USER_STATUS_UNSPECIFIED
        - USER_STATUS_ACTIVE
        - USER_STATUS_INACTIVE
        - USER_STATUS_SUSPENDED
    
    UserProfile:
      type: object
      description: UserProfile contains user profile information
      properties:
        avatarUrl:
          type: string
          description: Profile picture URL
        bio:
          type: string
          description: User's bio or description
        language:
          type: string
          description: Preferred language code (ISO 639-1)
        timezone:
          type: string
          description: Timezone identifier

4. Use the Specification#

Import into API tools:

# Postman
curl -X POST https://api.getpostman.com/import \
  -H "X-Api-Key: YOUR_API_KEY" \
  -F "file=@docs/user_api.yaml"
 
# Swagger UI (local)
docker run -p 8080:8080 -v $(pwd)/docs:/app swaggerapi/swagger-ui

Generate client code:

# Generate TypeScript client
openapi-generator-cli generate \
  -i docs/user_api.yaml \
  -g typescript-fetch \
  -o clients/typescript
 
# Generate Python client  
openapi-generator-cli generate \
  -i docs/user_api.yaml \
  -g python \
  -o clients/python

Generated Specification Structure#

Document Information#

openapi: 3.1.0
info:
  title: "{package_name} API"      # Derived from protobuf package
  version: "1.0.0"                 # Default version
  description: ""                  # Can be customized

Header Parameters#

When using header annotations, the OpenAPI specification automatically includes header parameters:

paths:
  /endpoint:
    post:
      parameters:
        - name: X-API-Key
          in: header
          required: true
          description: API authentication key
          schema:
            type: string
            format: uuid
            example: "123e4567-e89b-12d3-a456-426614174000"
        - name: X-Tenant-ID
          in: header
          required: false
          description: Tenant identifier
          schema:
            type: integer
            minimum: 1
      requestBody:
        # ...

Paths#

Each protobuf service method becomes an OpenAPI path:

paths:
  /{package}/{method_name}:        # Default path pattern
    post:                          # All methods use POST
      summary: "{method_comment}"   # From protobuf comments
      operationId: "{method_name}"  # RPC method name
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/{RequestType}'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/{ResponseType}'

Components/Schemas#

All protobuf messages become reusable schemas:

components:
  schemas:
    MessageName:
      type: object
      description: "{message_comment}"
      properties:
        field_name:
          type: string
          description: "{field_comment}"

Type Mapping#

The plugin provides comprehensive mapping between protobuf types and OpenAPI schemas:

Scalar Types#

Protobuf Type OpenAPI Type Format Notes
string string - UTF-8 encoded
bytes string byte Base64 encoded
bool boolean -
int32, sint32, sfixed32 integer int32
int64, sint64, sfixed64 integer int64
uint32, fixed32 integer int32 minimum: 0
uint64, fixed64 integer int64 minimum: 0
float number float
double number double

Complex Types#

Repeated Fields (Arrays):

repeated string tags = 1;
tags:
  type: array
  items:
    type: string

Map Fields (Objects):

map<string, string> metadata = 1;
metadata:
  type: object
  additionalProperties:
    type: string

Map Fields with Unwrap (Array Values):

When map values use the unwrap annotation, the OpenAPI schema reflects the unwrapped structure:

message BarList {
  repeated Bar bars = 1 [(sebuf.http.unwrap) = true];
}
 
message Response {
  map<string, BarList> bars = 1;
}
bars:
  type: object
  additionalProperties:
    type: array
    items:
      $ref: '#/components/schemas/Bar'

Root-Level Unwrap (Map or Array at Root):

When a message has a single field with unwrap, the entire schema becomes that field's type:

// Root map unwrap - response is a plain object
message UsersResponse {
  map<string, User> users = 1 [(sebuf.http.unwrap) = true];
}
UsersResponse:
  type: object
  additionalProperties:
    $ref: '#/components/schemas/User'
// Root repeated unwrap - response is a plain array
message UserList {
  repeated User users = 1 [(sebuf.http.unwrap) = true];
}
UserList:
  type: array
  items:
    $ref: '#/components/schemas/User'
// Combined unwrap - root map + value unwrap
message BarsResponse {
  map<string, BarList> data = 1 [(sebuf.http.unwrap) = true];
}
BarsResponse:
  type: object
  additionalProperties:
    type: array
    items:
      $ref: '#/components/schemas/Bar'

See JSON/Protobuf Compatibility for details.

Enums:

enum Status {
  ACTIVE = 0;
  INACTIVE = 1;
}
Status:
  type: string
  enum:
    - ACTIVE
    - INACTIVE

Message References:

UserProfile profile = 1;
profile:
  $ref: '#/components/schemas/UserProfile'

Field Examples:

string email = 1 [(sebuf.http.field_examples) = {
  values: ["alice@example.com", "bob@example.com", "charlie@example.com"]
}];
email:
  type: string
  examples:
    - "alice@example.com"
    - "bob@example.com"
    - "charlie@example.com"

Optional Fields (Proto3):

optional string middle_name = 1;
middleName:
  type: string
  # Handled according to OpenAPI 3.1 nullable semantics

Advanced Examples#

Nested Messages#

message Company {
  string name = 1;
  Address headquarters = 2;
  repeated Department departments = 3;
}
 
message Address {
  string street = 1;
  string city = 2;
  string country = 3;
  optional string postal_code = 4;
}
 
message Department {
  string name = 1;
  repeated Employee employees = 2;
  Employee manager = 3;
}
 
message Employee {
  string id = 1;
  string name = 2;
  string email = 3;
  repeated string skills = 4;
  map<string, string> certifications = 5;
}

Generates comprehensive schemas with proper references:

components:
  schemas:
    Company:
      type: object
      properties:
        name:
          type: string
        headquarters:
          $ref: '#/components/schemas/Address'
        departments:
          type: array
          items:
            $ref: '#/components/schemas/Department'
    
    Address:
      type: object
      properties:
        street:
          type: string
        city:
          type: string
        country:
          type: string
        postalCode:
          type: string
    
    Department:
      type: object
      properties:
        name:
          type: string
        employees:
          type: array
          items:
            $ref: '#/components/schemas/Employee'
        manager:
          $ref: '#/components/schemas/Employee'

Complex Enums#

// Priority levels for task management
enum Priority {
  PRIORITY_UNSPECIFIED = 0;  // Default priority
  PRIORITY_LOW = 1;          // Low priority tasks
  PRIORITY_MEDIUM = 2;       // Medium priority tasks  
  PRIORITY_HIGH = 3;         // High priority tasks
  PRIORITY_URGENT = 4;       // Urgent tasks requiring immediate attention
}
Priority:
  type: string
  description: Priority levels for task management
  enum:
    - PRIORITY_UNSPECIFIED  # Default priority
    - PRIORITY_LOW          # Low priority tasks
    - PRIORITY_MEDIUM       # Medium priority tasks
    - PRIORITY_HIGH         # High priority tasks
    - PRIORITY_URGENT       # Urgent tasks requiring immediate attention

API with Multiple Services#

// Authentication service
service AuthService {
  rpc Login(LoginRequest) returns (LoginResponse);
  rpc Logout(LogoutRequest) returns (LogoutResponse);
  rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
}
 
// User management service
service UserService {
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
  rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
}
 
// Notification service
service NotificationService {
  rpc SendEmail(SendEmailRequest) returns (SendEmailResponse);
  rpc SendPush(SendPushRequest) returns (SendPushResponse);
}

Each service generates its own OpenAPI specification file:

  • AuthService.openapi.yaml - Contains authentication endpoints
  • UserService.openapi.yaml - Contains user management endpoints
  • NotificationService.openapi.yaml - Contains notification endpoints

This per-service approach provides:

  • Better organization - Each service has its own API documentation
  • Independent versioning - Services can evolve separately
  • Easier deployment - Deploy only the API specs you need
  • Team ownership - Different teams can manage their service docs

Example UserService.openapi.yaml:

openapi: 3.1.0
info:
  title: UserService API
  version: 1.0.0
paths:
  /user/create_user:
    post: { ... }
  /user/update_user:
    post: { ... }
  /user/delete_user:
    post: { ... }

Output Formats#

YAML Format (Default)#

protoc --openapiv3_out=./docs api.proto
# Generates: ServiceName.openapi.yaml for each service
# Example: UserService.openapi.yaml, AdminService.openapi.yaml

Advantages:

  • Human-readable and editable
  • Supports comments (preserved from protobuf)
  • Standard for OpenAPI documentation
  • Direct import into most tools

JSON Format#

protoc --openapiv3_out=./docs --openapiv3_opt=format=json api.proto
# Generates: ServiceName.openapi.json for each service
# Example: UserService.openapi.json, AdminService.openapi.json

Advantages:

  • Programmatic processing
  • Faster parsing
  • Smaller file size
  • Direct use in JavaScript applications

File Naming Convention#

The plugin automatically generates one file per service with the naming pattern:

  • YAML: {ServiceName}.openapi.yaml
  • JSON: {ServiceName}.openapi.json

This ensures:

  • No file conflicts when multiple services exist
  • Clear association between service and its documentation
  • Easy to manage and deploy individual service specs

Integration with HTTP Generation#

When used together with protoc-gen-go-http, the OpenAPI specification will accurately reflect your actual HTTP endpoints:

Using Buf#

# buf.gen.yaml
version: v2
plugins:
  - remote: buf.build/protocolbuffers/go
    out: .
    opt: module=github.com/yourorg/api
  - local: protoc-gen-go-http
    out: .
  - local: protoc-gen-openapiv3
    out: ./docs
buf generate

Using protoc#

# Clone sebuf for HTTP annotations
git clone https://github.com/SebastienMelki/sebuf.git
 
# Generate both HTTP handlers and OpenAPI spec
protoc --go_out=. --go_opt=module=github.com/yourorg/api \
       --go-http_out=. \
       --openapiv3_out=./docs \
       --proto_path=. \
       --proto_path=./sebuf/proto \
       api.proto

Benefits of combined generation:

  • Accurate paths - OpenAPI paths match actual HTTP routes
  • Consistent behavior - Same type handling in both systems
  • Single source of truth - One protobuf definition drives everything
  • Testing alignment - Test against the same spec that documents the API

With HTTP Annotations and Headers#

import "sebuf/http/annotations.proto";
import "sebuf/http/headers.proto";
 
service UserService {
  option (sebuf.http.service_config) = {
    base_path: "/api/v1"
  };
  
  option (sebuf.http.service_headers) = {
    required_headers: [
      {
        name: "X-API-Key"
        description: "API authentication key"
        type: "string"
        format: "uuid"
        required: true
        example: "123e4567-e89b-12d3-a456-426614174000"
      }
    ]
  };
  
  rpc CreateUser(CreateUserRequest) returns (User) {
    option (sebuf.http.config) = {
      path: "/users"
    };
    option (sebuf.http.method_headers) = {
      required_headers: [
        {
          name: "X-Request-ID"
          description: "Unique request identifier"
          type: "string"
          format: "uuid"
          required: true
        }
      ]
    };
  }
  
  rpc GetUser(GetUserRequest) returns (User) {
    option (sebuf.http.config) = {
      path: "/users/get"
    };
  }
}

The OpenAPI spec will reflect the actual HTTP paths and header parameters:

paths:
  /api/v1/users:
    post:
      summary: CreateUser
      parameters:
        - name: X-API-Key
          in: header
          description: API authentication key
          required: true
          schema:
            type: string
            format: uuid
            example: "123e4567-e89b-12d3-a456-426614174000"
        - name: X-Request-ID
          in: header
          description: Unique request identifier
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        # ...
      responses:
        # ...
  /api/v1/users/get:
    post:
      summary: GetUser
      parameters:
        - name: X-API-Key
          in: header
          description: API authentication key
          required: true
          schema:
            type: string
            format: uuid
            example: "123e4567-e89b-12d3-a456-426614174000"
      requestBody:
        # ...

Customization Options#

Document Information#

Customize the generated OpenAPI document info:

# Set custom title and version
protoc --openapiv3_out=./docs \
       --openapiv3_opt=title="My Amazing API" \
       --openapiv3_opt=version="2.1.0" \
       api.proto

Output Directory Structure#

# Organize by version
protoc --openapiv3_out=./docs/v1 api/v1/*.proto
protoc --openapiv3_out=./docs/v2 api/v2/*.proto
 
# Separate files by service
protoc --openapiv3_out=./docs/auth auth_service.proto
protoc --openapiv3_out=./docs/users user_service.proto

Multiple Formats#

Generate both formats simultaneously:

# YAML for documentation
protoc --openapiv3_out=./docs api.proto
 
# JSON for tooling
protoc --openapiv3_out=./docs --openapiv3_opt=format=json api.proto

Best Practices#

1. Rich Documentation#

Add comprehensive comments to your protobuf definitions:

// UserService provides comprehensive user management capabilities
// including CRUD operations, authentication, and profile management.
service UserService {
  // CreateUser registers a new user in the system.
  //
  // This method validates the user input, ensures email uniqueness,
  // and sends a welcome email if requested. The created user will
  // have a system-generated ID and creation timestamp.
  //
  // Returns the created user with all fields populated.
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}
 
// User represents a registered user in the system
message User {
  // Unique identifier for the user (read-only)
  // Format: UUID v4
  // Example: "123e4567-e89b-12d3-a456-426614174000"
  string id = 1;
  
  // User's email address (must be unique)
  // Must be a valid email format
  // Example: "user@example.com"
  string email = 2;
  
  // Full display name of the user
  // Maximum length: 100 characters
  // Example: "John Doe"
  string name = 3;
}

2. Consistent Naming#

Use consistent naming conventions:

// Good: Consistent service and method naming
service UserService {
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
  rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}
 
// Good: Clear request/response message naming
message CreateUserRequest {
  User user = 1;
  bool send_welcome_email = 2;
}
 
message CreateUserResponse {
  User user = 1;
  int64 created_at = 2;
}

3. Organize by Domain#

Structure your protobuf files by business domain:

api/
├── auth/
│   ├── auth_service.proto
│   └── auth_types.proto
├── users/
│   ├── user_service.proto
│   └── user_types.proto
├── orders/
│   ├── order_service.proto
│   └── order_types.proto
└── common/
    ├── pagination.proto
    └── errors.proto

4. Version Management#

Include version information in package names:

syntax = "proto3";
package mycompany.users.v1;
 
option go_package = "github.com/mycompany/api/users/v1;usersv1";
 
// Clear versioning in OpenAPI output

5. Error Handling Documentation#

Document error scenarios in service comments:

service UserService {
  // GetUser retrieves a user by ID
  //
  // Errors:
  // - INVALID_ARGUMENT: User ID is empty or invalid format
  // - NOT_FOUND: User with the specified ID does not exist  
  // - PERMISSION_DENIED: Caller lacks permission to view this user
  // - INTERNAL: Database or system error occurred
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
}

Troubleshooting#

Common Issues#

1. Plugin Not Found#

protoc-gen-openapiv3: program not found or is not executable

Solution:

# Ensure plugin is in PATH
export PATH=$PATH:$(go env GOPATH)/bin
 
# Reinstall plugin
go install github.com/SebastienMelki/sebuf/cmd/protoc-gen-openapiv3@latest

2. No Output Generated#

If no OpenAPI file is generated, check:

  • Service definitions: Plugin only processes files with service definitions
  • Output directory: Ensure the output directory exists and is writable
  • File permissions: Check write permissions for the target directory
# Create output directory
mkdir -p docs
 
# Check permissions
ls -la docs/
 
# Generate with verbose output
protoc -v --openapiv3_out=./docs api.proto

3. Missing Message Schemas#

If message schemas are missing from the output:

  • Message usage: Only messages used in service methods are included
  • Import statements: Ensure all required messages are imported
  • Package structure: Verify correct package and import paths

4. Incorrect Type Mapping#

If protobuf types aren't mapped correctly:

// Ensure proper type usage
message Example {
  string text = 1;        // ✅ Becomes string
  int32 number = 2;       // ✅ Becomes integer/int32
  repeated string list = 3; // ✅ Becomes array of strings
  map<string, string> meta = 4; // ✅ Becomes object with string values
}

Validation and Testing#

1. Validate Generated Spec#

# Install OpenAPI validator
npm install -g @apidevtools/swagger-cli
 
# Validate the generated spec
swagger-cli validate docs/api.yaml
 
# Bundle and dereference
swagger-cli bundle docs/api.yaml --outfile docs/api-bundled.yaml

2. Test with Tools#

# Test with Swagger UI
docker run -p 8080:8080 -v $(pwd)/docs:/app swaggerapi/swagger-ui
 
# Test with Postman
curl -X POST https://api.getpostman.com/import \
  -H "X-Api-Key: YOUR_API_KEY" \
  -F "file=@docs/api.yaml"
 
# Test with Insomnia
# Import docs/api.yaml directly in the Insomnia app

Getting Help#

  • Demo: Try the simple tutorial
  • Test cases: Review internal/openapiv3/ test files
  • Issues: File a GitHub issue with your proto definition and expected output
  • Community: Join GitHub Discussions for questions and tips

Real-World Usage Examples#

API Gateway Integration#

# Generated OpenAPI can be imported into API gateways
# Kong, AWS API Gateway, Azure API Management, etc.
 
# Kong example
curl -X POST http://kong:8001/services \
  -d name=user-service \
  -d url=http://user-service:8080
 
curl -X POST http://kong:8001/services/user-service/plugins \
  -d name=openapi-validator \
  -d config.spec=@docs/user_api.yaml

Client Generation Pipeline#

#!/bin/bash
# generate-clients.sh
 
# Generate OpenAPI spec
protoc --openapiv3_out=./docs api/*.proto
 
# Generate TypeScript client
openapi-generator-cli generate \
  -i docs/api.yaml \
  -g typescript-fetch \
  -o clients/typescript \
  --additional-properties=npmName=@mycompany/api-client
 
# Generate Python client
openapi-generator-cli generate \
  -i docs/api.yaml \
  -g python \
  -o clients/python \
  --additional-properties=packageName=mycompany_api_client
 
# Generate Go client (alternative to protobuf)
openapi-generator-cli generate \
  -i docs/api.yaml \
  -g go \
  -o clients/go
 
echo "All clients generated successfully!"

Documentation Website#

# Generate static documentation site
npx redoc-cli bundle docs/api.yaml --output docs/index.html
 
# Or use GitBook, Docusaurus, etc.
# Most documentation platforms support OpenAPI imports

Next: Follow the complete workflow in our Getting Started Guide

See also: