Skip to content

Schema

tag Go Reference Go Report Card CI codecov License

Decode HTTP requests into Go structs with OpenAPI 3.0 compliance

A Go package for decoding HTTP requests into Go structs with full support for OpenAPI 3.0/3.1 parameter serialization. Handles query, path, header, cookie parameters and request bodies (JSON, XML, forms, multipart, files) in a single unified API.

Decouples business logic from HTTP request handling. Request types are defined with struct tags. The package extracts query parameters, path variables, headers, cookies, and request bodies, converting them into typed Go structs with OpenAPI 3.0 compliance.

Key Features

  • OpenAPI 3.0/3.1 Compliant - Full support for parameter serialization styles
  • All Parameter Locations - Query, path, header, cookie parameters
  • All Serialization Styles - form, simple, matrix, label, spaceDelimited, pipeDelimited, deepObject
  • Multiple Body Types - JSON, XML, URL-encoded forms, multipart forms, file uploads
  • Type Conversion - Automatic conversion between types with sensible defaults
  • Performance Optimized - Metadata caching for high-throughput applications
  • Thread-Safe - Safe for concurrent use across goroutines
  • Extensible - Custom decoders, unmarshalers, and tag parsers

Quick Start

package main

import (
    "net/http"
    "github.com/talav/schema"
)

type CreateUserRequest struct {
    // Query parameter
    Version string `schema:"version,location=query"`
    // Path parameter (from router)
    OrgID string `schema:"org_id,location=path"`
    // Header parameter
    APIKey string `schema:"X-Api-Key,location=header"`
    // Request body
    Body struct {
        Name  string `schema:"name"`
        Email string `schema:"email"`
    } `body:"structured"`
}

func handler(w http.ResponseWriter, r *http.Request) {
    codec := schema.NewDefaultCodec()

    // Router params from your router (chi, gorilla, etc.)
    routerParams := map[string]string{"org_id": "123"}

    var req CreateUserRequest
    if err := codec.DecodeRequest(r, routerParams, &req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Use req.Version, req.OrgID, req.APIKey, req.Body.Name, req.Body.Email
}

Getting Started

Quick Start

Install and build your first request handler

Quick Start →

Core Concepts

Understand the architecture and how it works

Learn Concepts →

Documentation

User Guides

Learn about parameters, bodies, and serialization

Browse Guides →

Advanced Topics

Extensibility and customization

Advanced Docs →

API Documentation

Complete API documentation with types, methods, and interfaces is available at pkg.go.dev/github.com/talav/schema.

Architecture

The package consists of four main components:

  1. Codec - High-level API that orchestrates the decoding pipeline
  2. Metadata - Parses and caches struct tag metadata for performance
  3. Decoder - Extracts parameters from HTTP requests into maps
  4. Unmarshaler - Converts maps to typed structs (default: mapstructure)

All components are thread-safe and designed for reuse across concurrent requests.

Why Schema?

Traditional HTTP handlers mix request parsing with business logic:

// Traditional approach - mixed concerns
func handler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    ageStr := r.URL.Query().Get("age")
    age, _ := strconv.Atoi(ageStr)
    apiKey := r.Header.Get("X-API-Key")

    var body RequestBody
    json.NewDecoder(r.Body).Decode(&body)

    // Finally, business logic...
}

With Schema, define your request structure once:

// With schema - clean separation
type UserRequest struct {
    Name   string `schema:"name"`
    Age    int    `schema:"age"`
    APIKey string `schema:"X-API-Key,location=header"`
    Body   struct {
        Email string `schema:"email"`
    } `body:"structured"`
}

func handler(w http.ResponseWriter, r *http.Request) {
    var req UserRequest
    codec.DecodeRequest(r, nil, &req)
    // Business logic with strongly-typed req
}

What's Next?