guides/04_runtime_schemas.md

# Runtime Schemas

Exdantic runtime schemas let you build validation contracts dynamically from data or configuration.

## Runtime Schema Types

Exdantic runtime layer includes:

- `Exdantic.Runtime.DynamicSchema` for field-level runtime schemas
- `Exdantic.Runtime.EnhancedSchema` for runtime model validators and computed fields
- `Exdantic.Runtime.Validator` as a unified interface for both

## Create a Dynamic Schema

```elixir
fields = [
  {:name, :string, [required: true, min_length: 2]},
  {:age, :integer, [optional: true, gteq: 0]},
  {:tags, {:array, :string}, [optional: true, min_items: 1]}
]

schema = Exdantic.Runtime.create_schema(fields,
  name: "UserRuntime",
  title: "Runtime User",
  description: "Generated at runtime",
  strict: true
)
```

Validate:

```elixir
{:ok, validated} = Exdantic.Runtime.validate(%{name: "Jane", age: 31}, schema)
```

Generate JSON Schema:

```elixir
json = Exdantic.Runtime.to_json_schema(schema)
```

## Dynamic Field Definition Format

Accepted tuples:

- `{field_name, type}`
- `{field_name, type, opts}`

Common field opts:

- `required`, `optional`, `default`
- `description`, `example`, `examples`
- standard constraints (`min_length`, `gteq`, etc.)

## DynamicSchema Utility APIs

`Exdantic.Runtime.DynamicSchema` includes helpers:

- `new/4`
- `get_field/2`
- `field_names/1`
- `required_fields/1`
- `optional_fields/1`
- `strict?/1`
- `update_config/2`
- `add_field/3`
- `remove_field/2`
- `summary/1`

## Enhanced Runtime Schema

Use `create_enhanced_schema/2` to add runtime model validators and computed fields:

```elixir
schema =
  Exdantic.Runtime.create_enhanced_schema(
    [
      {:name, :string, [required: true]},
      {:email, :string, [required: true]}
    ],
    model_validators: [
      fn data -> {:ok, %{data | name: String.trim(data.name)}} end
    ],
    computed_fields: [
      {:email_domain, :string, fn data -> {:ok, data.email |> String.split("@") |> List.last()} end}
    ],
    strict: true
  )

{:ok, result} = Exdantic.Runtime.validate_enhanced(%{name: " Jane ", email: "jane@x.com"}, schema)
```

Enhanced runtime schemas keep runtime functions in an internal function registry when anonymous functions are used.

## EnhancedSchema Utility APIs

`Exdantic.Runtime.EnhancedSchema` supports:

- `create/2`
- `validate/3`
- `to_json_schema/2`
- `info/1`
- `add_model_validator/2`
- `add_computed_field/4`
- `process_model_validators/1`
- `process_computed_fields/2`

## Unified Runtime Validator

Use `Exdantic.Runtime.Validator` when callers should not care if schema is dynamic or enhanced:

```elixir
{:ok, validated} = Exdantic.Runtime.Validator.validate(input, runtime_schema)
json = Exdantic.Runtime.Validator.to_json_schema(runtime_schema)
info = Exdantic.Runtime.Validator.schema_info(runtime_schema)
```

## Phase 6 Runtime Helpers

Runtime module also includes advanced helpers:

- `create_enhanced_schema_v6/2`
- `validate_enhanced_v6/3`

These can add provider compatibility testing, performance metadata, and JSON Schema checks in one call.

## Error Semantics

Runtime validation returns the same structured error shape as compile-time schemas (`Exdantic.Error`).

Strict mode behaves similarly: unknown keys produce `:additional_properties` errors.

## When Runtime Schemas Are the Right Tool

Choose runtime schemas when:

- Fields are generated from external metadata
- Inputs differ per tenant, pipeline stage, or task
- You need map-based validation contracts without compile-time module generation

Choose compile-time schemas when contracts are stable and shared.

## Next Guides

- `guides/05_type_adapter_wrapper_root_schema.md`
- `guides/06_json_schema_and_resolvers.md`