-
-
Couldn't load subscription status.
- Fork 229
Description
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:
- One source of truth – business rules are defined once, not in every transport layer
- Transport-agnostic validation – same logic applies for HTTP, gRPC, CLI, etc.
- Consistent error formats – users always get the same structure
- 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 explicitlyrequired:"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.