kin-openapi

repository·master·Indexed 25 days ago

https://github.com/getkin/kin-openapi

A Go library for handling OpenAPI (Swagger) files, supporting versions 2.0 and 3.0, with upcoming support for 3.1. It provides tools for loading, validating, and filtering OpenAPI documents, as well as validating HTTP requests and responses against them. Includes a CLI tool for validating YAML or JSON specification files.

Tokens
4K
Snippets
11
Records
22
Agent score
84%

What's inside kin-openapi

  1. Validate HTTP requests and responses

    master

    To validate incoming HTTP requests and outgoing responses against an OpenAPI specification, use openapi3filter. The typical workflow involves:

    1. Loading the document with openapi3.Loader.
    2. Creating a router (e.g., using gorillamux.NewRouter) to match the request to an operation.
    3. Using openapi3filter.ValidateRequest with a RequestValidationInput.
    4. Using openapi3filter.ValidateResponse with a ResponseValidationInput.
    package main
    
    import (
    	"context"
    	"fmt"
    	"net/http"
    
    	"github.com/getkin/kin-openapi/openapi3"
    	"github.com/getkin/kin-openapi/openapi3filter"
    	"github.com/getkin/kin-openapi/routers/gorillamux"
    )
    
    func main() {
    	ctx := context.Background()
    	loader := &openapi3.Loader{Context: ctx, IsExternalRefsAllowed: true}
    	doc, _ := loader.LoadFromFile(".../My-OpenAPIv3-API.yml")
    	// Validate document
    	_ = doc.Validate(ctx)
    	router, _ := gorillamux.NewRouter(doc)
    	httpReq, _ := http.NewRequest(http.MethodGet, "/items", nil)
    
    	// Find route
    	route, pathParams, _ := router.FindRoute(httpReq)
    
    	// Validate request
    	requestValidationInput := &openapi3filter.RequestValidationInput{
    		Request:    httpReq,
    		PathParams: pathParams,
    		Route:      route,
    	}
    	_ = openapi3filter.ValidateRequest(ctx, requestValidationInput)
    
    	// Handle that request
    	// --> YOUR CODE GOES HERE <--
    	responseHeaders := http.Header{"Content-Type": []string{"application/json"}}
    	responseCode := 200
    	responseBody := []byte(`{}`)
    
    	// Validate response
    	responseValidationInput := &openapi3filter.ResponseValidationInput{
    		RequestValidationInput: requestValidationInput,
    		Status:                 responseCode,
    		Header:                 responseHeaders,
    	}
    	responseValidationInput.SetBodyBytes(responseBody)
    	_ = openapi3filter.ValidateResponse(ctx, responseValidationInput)
    }
  2. Disable detailed schema error messages

    master

    By default, schema validation errors include the error reason, the schema, and the input value. To prevent sensitive information (like secrets) from appearing in error messages, you can disable these extra details by setting the global openapi3.SchemaErrorDetailsDisabled option to true.

    func main() {
    	// ...
    
    	// Disable schema error detailed error messages
    	openapi3.SchemaErrorDetailsDisabled = true
    
    	// ... other validate codes
    }
  3. Identify validation errors by code

    master

    Validation errors carry stable, kebab-case codes (e.g., operation-responses-required) that are independent of the error message text. This allows tools to programmatically handle specific error types. You can find the full list of codes via openapi3.ValidationErrorCodes().

    err := doc.Validate(ctx, openapi3.EnableMultiError())
    for _, e := range err.(openapi3.MultiError) {
    	var coded openapi3.CodedError
    	if errors.As(e, &coded) {
    		fmt.Println(coded.Code(), e) // e.g. "operation-responses-required value of responses must be an object"
    	}
    }
  4. Register a custom array uniqueness checker

    master

    The library uses a default function (based on json.Marshal) to check if array items are unique. For better performance, you can register your own function using openapi3.RegisterArrayUniqueItemsChecker.

    func main() {
    	// ...
    
    	// Register a customized function used to check uniqueness of array.
    	openapi3.RegisterArrayUniqueItemsChecker(arrayUniqueItemsChecker)
    
    	// ... other validate codes
    }
    
    func arrayUniqueItemsChecker(items []any) bool {
    	// Check the uniqueness of the input slice
    }
  5. Provide custom schema error messages

    master

    For fine-grained control over error messages, you can provide a custom error function via openapi3filter.Options. By using WithCustomSchemaErrorFunc, you can define how openapi3.SchemaError objects are converted to strings. For example, returning only the Reason field ensures the original input value is never included in the error message.

    func validationOptions() *openapi3filter.Options {
    	options := &openapi3filter.Options{}
    	options.WithCustomSchemaErrorFunc(safeErrorMessage)
    	return options
    }
    
    func safeErrorMessage(err *openapi3.SchemaError) string {
    	return err.Reason
    }
  6. Register a custom body decoder for HTTP validation

    master

    By default, openapi3filter supports common content types like application/json and text/plain. To support other types (e.g., application/xml), register a custom decoder using openapi3filter.RegisterBodyDecoder.

    func main() {
    	// ...
    
    	// Register a body's decoder for content type "application/xml".
    	openapi3filter.RegisterBodyDecoder("application/xml", xmlBodyDecoder)
    
    	// Now you can validate HTTP request that contains a body with content type "application/xml".
    	requestValidationInput := &openapi3filter.RequestValidationInput{
    		Request:    httpReq,
    		PathParams: pathParams,
    		Route:      route,
    	}
    	if err := openapi3filter.ValidateRequest(ctx, requestValidationInput); err != nil {
    		panic(err)
    	}
    
    	// ...
    
    	// And you can validate HTTP response that contains a body with content type "application/xml".
    	if err := openapi3filter.ValidateResponse(ctx, responseValidationInput); err != nil {
    		panic(err)
    	}
    }
    
    func xmlBodyDecoder(body io.Reader, h http.Header, schema *openapi3.SchemaRef, encFn openapi3filter.EncodingFn) (decoded any, err error) {
    	// Decode body to a primitive, []any, or map[string]any.
    }
  7. Track source locations (Origin) in OpenAPI documents

    master

    By setting IncludeOrigin = true on an openapi3.Loader, the loader records the file, line, and column for every element in the document. This is useful for linters or diff tools.

    An Origin struct contains:

    • Key: The location of the object itself (file, line, column).
    • Fields: Locations of scalar fields within the object (e.g., origin.Fields["description"]).
    • Sequences: Locations of items in sequence-valued fields (e.g., origin.Sequences["enum"]).

    Note: Origin data is populated during post-processing and is excluded from serialization (it won't appear if you marshal the document back to JSON/YAML).

    loader := openapi3.NewLoader()
    loader.IncludeOrigin = true
    doc, err := loader.LoadFromFile("my-openapi-spec.json")
    
    // Each element has an Origin field with source location info
    fmt.Println(doc.Info.Origin.Key.File)   // "my-openapi-spec.json"
    fmt.Println(doc.Info.Origin.Key.Line)   // 2
    fmt.Println(doc.Info.Origin.Key.Column) // 1
  8. Load an OpenAPI 3 document

    master

    Use openapi3.NewLoader() to create a loader that resolves all references in your specification. You can load documents from files using LoadFromFile.

    loader := openapi3.NewLoader()
    doc, err := loader.LoadFromFile("my-openapi-spec.json")
  9. Reconcile component $ref types with ReferencesComponentInRootDocument

    master

    The ReferencesComponentInRootDocument function helps determine if a schema reference coincides with a reference in the root document's component objects. This is particularly useful for code generation tools to identify if two schema definitions share the same structure by checking if they point to the same component definition.

    doc, err = loader.LoadFromFile("openapi.yml")
    
    for _, path := range doc.Paths.InMatchingOrder() {
    	pathItem := doc.Paths.Find(path)
    
    	if pathItem.Get == nil || pathItem.Get.Responses.Status(200) {
    		continue
    	}
    
    	for _, s := range pathItem.Get.Responses.Status(200).Value.Content {
    		name, match := ReferencesComponentInRootDocument(doc, s.Schema)
    		fmt.Println(path, match, name) // /record true #/components/schemas/BookRecord
    	}
    }
  10. Breaking changes in v0.143.0

    master

    In version v0.143.0, the following type changes occurred:

    • Removed openapi3.StringMap[V] (internal helper).
    • openapi3.Discriminator.Mapping field type changed from StringMap[MappingRef] to map[string]MappingRef.
    • openapi3.OAuthFlow.Scopes field type changed from StringMap[string] to map[string]string.
  11. Breaking changes in v0.144.0

    master
    In version v0.144.0, openapi3filter.ValidationHandler.AuthenticationFunc no longer defaults to NoopAuthenticationFunc. Users must now explicitly set this field and implement their own AuthenticationFunc or manually use the noop implementation.
  12. Breaking changes in v0.136.0

    master

    In version v0.136.0, several schema field types were updated:

    • openapi3.Schema.ExclusiveMin and openapi3.Schema.ExclusiveMax changed from bool to ExclusiveBound (a union type holding *bool for OpenAPI 3.0 or *float64 for OpenAPI 3.1).
    • openapi3.Schema.PrefixItems changed from []*SchemaRef to SchemaRefs.
    • openapi3.Schema.UnevaluatedItems and openapi3.Schema.UnevaluatedProperties changed from *SchemaRef to BoolSchema (a union type accepting a boolean or a schema object).