Skip to content

Validation

oapi-codegen generates Validate() methods for your types based on OpenAPI schema constraints. This uses the go-playground/validator library under the hood.

Configuration Options

generate:
  validation:
    skip: false      # Set to true to skip Validate() method generation
    simple: false    # Set to true to use simple validate.Struct() for all types
    response: false  # Set to true to generate Validate() for response types

skip

When true, no Validate() methods are generated. Use this if you don't need validation or want to implement your own.

simple

When true, all struct types use simple validate.Struct() validation instead of custom validation logic. This produces cleaner code but doesn't support advanced features like union type validation.

response

When true, generates Validate() methods for response types. Useful for contract testing to ensure API responses match the OpenAPI spec.

Supported OpenAPI Constraints

The following OpenAPI constraints are translated to validation tags:

OpenAPI Constraint Validation Tag Applies To
required required All types
nullable: true omitempty All types
minimum gte=N integers, numbers
maximum lte=N integers, numbers
exclusiveMinimum gt=N integers, numbers
exclusiveMaximum lt=N integers, numbers
minLength min=N strings, arrays
maxLength max=N strings, arrays
minItems min=N arrays
maxItems max=N arrays
enum custom switch string, integer enums

Generated Code Examples

Simple Struct Validation

For simple structs without unions or nested validators:

type DistanceBasedLocation struct {
    Distance float32 `json:"distance" validate:"required,gte=0"`
}

func (d DistanceBasedLocation) Validate() error {
    return runtime.ConvertValidatorError(typesValidator.Struct(d))
}

Enum Validation

Enum types get custom switch-based validation:

type Status string

const (
    ACTIVE   Status = "ACTIVE"
    INACTIVE Status = "INACTIVE"
    PENDING  Status = "PENDING"
)

// Validate checks if the Status value is valid
func (s Status) Validate() error {
    switch s {
    case ACTIVE, INACTIVE, PENDING:
        return nil
    default:
        return runtime.NewValidationErrorsFromString("Enum", fmt.Sprintf("must be a valid Status value, got: %v", s))
    }
}

Complex Validation with Nested Types

For structs with nested types that implement Validator:

type Response struct {
    Status Status `json:"status" validate:"required"`

    // Unit A numeric unit for an indicator - includes empty string
    Unit           IndicatorUnit   `json:"unit" validate:"required"`
    NullableStatus *NullableStatus `json:"nullableStatus,omitempty"`
}

func (r Response) Validate() error {
    var errors runtime.ValidationErrors
    if v, ok := any(r.Status).(runtime.Validator); ok {
        if err := v.Validate(); err != nil {
            errors = errors.Append("Status", err)
        }
    }
    if v, ok := any(r.Unit).(runtime.Validator); ok {
        if err := v.Validate(); err != nil {
            errors = errors.Append("Unit", err)
        }
    }
    if r.NullableStatus != nil {
        if v, ok := any(r.NullableStatus).(runtime.Validator); ok {
            if err := v.Validate(); err != nil {
                errors = errors.Append("NullableStatus", err)
            }
        }
    }
    if len(errors) == 0 {
        return nil
    }
    return errors
}

Runtime Helpers

Validator Interface

All generated types with validation implement this interface:

type Validator interface {
    Validate() error
}

ValidationErrors

A slice type that collects multiple validation errors with field paths:

var errors runtime.ValidationErrors
errors = errors.Append("FieldName", err)

ConvertValidatorError

Converts go-playground/validator errors to ValidationErrors:

return runtime.ConvertValidatorError(typesValidator.Struct(s))

Usage

Validating Request Bodies

func handleRequest(w http.ResponseWriter, r *http.Request) {
    var body CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    if err := body.Validate(); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Process valid request...
}

Contract Testing (Response Validation)

Enable response validation in config:

generate:
  validation:
    response: true

Then validate responses in tests:

func TestAPIResponse(t *testing.T) {
    resp, err := client.GetUser(ctx, userID)
    require.NoError(t, err)

    // Validate response matches OpenAPI spec
    if err := resp.Validate(); err != nil {
        t.Errorf("Response validation failed: %v", err)
    }
}