Skip to main content

ModelSerializers & Validation

Serializers control the representation of model data sent over HTTP and validate incoming request payloads before models are saved.


Defining a ModelSerializer​

package catalog

import (
"context"
"errors"
"github.com/forgego/forge/api"
"myapp/models"
)

type ProductSerializer struct {
api.ModelSerializer[models.Product]
}

// Fields controls which attributes are exposed in JSON
func (ProductSerializer) Fields() []string {
return []string{
"id",
"sku",
"name",
"price",
"tax_rate",
"price_with_tax",
"in_stock",
"created_at",
}
}

// ReadonlyFields prevents clients from overriding generated or audit fields
func (ProductSerializer) ReadonlyFields() []string {
return []string{"id", "price_with_tax", "created_at"}
}

// Custom field validation: Validate<FieldName>
func (s *ProductSerializer) ValidatePrice(price float64) error {
if price < 0.01 {
return errors.New("price must be at least $0.01")
}
return nil
}

// Cross-field object validation
func (s *ProductSerializer) Validate(ctx context.Context, p *models.Product) error {
if p.TaxRate < 0 || p.TaxRate > 1.0 {
return errors.New("tax rate must be between 0.0 and 1.0")
}
return nil
}

Serializer Configuration Options​

MethodReturn TypeDescription
Fields()[]stringWhitelist of field names to serialize.
Exclude()[]stringBlacklist of field names to omit.
ReadonlyFields()[]stringFields serialized in responses but ignored in POST/PUT/PATCH.
WriteOnlyFields()[]stringFields accepted in requests but never included in responses (e.g. passwords).

Handling Validation Errors​

When a client submits invalid JSON or fails validation constraints, Forge automatically formats an RFC 7807 compliant error payload:

{
"status": 422,
"error": "Unprocessable Entity",
"detail": "Validation failed for 2 fields",
"errors": {
"price": ["price must be at least $0.01"],
"tax_rate": ["tax rate must be between 0.0 and 1.0"]
}
}

Nested Relational Serialization​

Serialize related entities by embedding their serializers:

type OrderSerializer struct {
api.ModelSerializer[models.Order]
Customer CustomerSummarySerializer `json:"customer"`
OrderItems []OrderItemDetailSerializer `json:"items"`
}

Next Steps​

  • ViewSets: Hook serializers into HTTP endpoints.
  • Pagination: Paginate list responses.
  • OpenAPI: What the OpenAPI document contains today.