Skip to content

Make “Required by Default” Struct Field Behavior Configurable #901

@bcastillo-2022474

Description

@bcastillo-2022474

Feature Request: Config Option for Default Field Required Behavior

Summary

It would be great to have a configuration option to make struct fields optional by default — so they’d only be required if explicitly tagged with required:"true".
This would flip the current default (where everything is required unless told otherwise).

Why This Matters

Use Case: Clean Architecture + Domain-Level Validation

In many clean architecture setups, validation belongs in the use case or domain layer, not the transport layer. The HTTP layer should really just handle:

  • JSON decoding
  • Type checking (string vs number, etc.)
  • Basic structure validation

All the real business logic (required fields, length rules, formats, cross-field constraints, etc.) lives in the domain. This ensures:

  1. One source of truth – business rules are defined once, not in every transport layer
  2. Transport-agnostic validation – same logic applies for HTTP, gRPC, CLI, etc.
  3. Consistent error formats – users always get the same structure
  4. Proper layering – keeps the domain clean and independent

The Current Issue

Right now, Huma requires fields by default, which can lead to inconsistent validation behavior.

Example:
If a field is missing entirely, Huma’s built-in validation triggers.
If it’s present but invalid, your domain validation kicks in.
Result: two different error responses for what is, functionally, the same rule.

# Missing field -> Huma validation
{ "code": "VALIDATION_ERROR", "context": { "details": ["expected required property email"] } }

# Invalid field -> Domain validation
{ "code": "VALIDATION_ERROR", "context": { "email": "Email is required and must be valid" } }

Current Workaround

You can make fields optional by adding required:"false" everywhere…
but that quickly gets tedious and error-prone:

type CreateUserRequest struct {
    Body struct {
        Email     string `json:"email" required:"false"`
        FirstName string `json:"first_name" required:"false"`
        LastName  string `json:"last_name" required:"false"`
        // ...and so on for every field
    }
}

This adds noise, clutters the code, and is easy to forget on new fields.

Proposed Solution

Add a new field to huma.Config:

type Config struct {
    // ...
    DefaultFieldRequired bool
}

Behavior:

  • DefaultFieldRequired = true → current behavior (fields required unless marked otherwise)
  • DefaultFieldRequired = false → fields optional unless explicitly required:"true"

Default remains true to keep backward compatibility.

Example Usage

humaConfig := huma.DefaultConfig("My API", "1.0.0")
humaConfig.DefaultFieldRequired = false
api := humagin.New(router, humaConfig)

type CreateUserRequest struct {
    Body struct {
        Email     string `json:"email"`               // Optional
        Age       int    `json:"age" required:"true"` // Required
    }
}

It keeps the default safe for existing users but makes life much easier for those of us pushing validation deeper into the domain layer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions