Validation Modes
What are Validation Modes?
When you add CVT validation middleware to your API server, you need to decide what happens when a request or response doesn't match your OpenAPI schema. Should invalid requests be rejected? Should violations be logged? Or should validation run silently in the background?
Validation modes control this behavior. They let you start with zero-risk observability and gradually move toward full contract enforcement as you gain confidence.
Adding contract validation to a production API can be risky. You might discover that:
- Your OpenAPI spec doesn't match your actual implementation
- Some clients send requests that don't match the documented format
- Edge cases in your responses weren't captured in the schema
Validation modes let you deploy safely by starting in observation mode, fixing issues, then enabling enforcement—without breaking existing clients.
Mode Reference
| Mode | Request Violation | Response Violation | Metrics | Use Case |
|---|---|---|---|---|
| strict | Reject with 400 Bad Request | Log error (response already sent) | Recorded | Production enforcement after rollout |
| warn | Log warning, continue processing | Log warning, continue | Recorded | Gradual rollout, monitoring impact |
| shadow | Silent (no logging) | Silent (no logging) | Recorded | Initial deployment, metrics-only analysis |
Mode Behavior Details
Strict Mode
Request → Validate → Invalid? → Return 400 (handler never executes)
→ Valid? → Execute handler → Validate response → Log if invalid
- Request violations: Immediately rejected with
400 Bad Requestand error details - Response violations: Logged as errors but response is still sent (can't unsend)
- Best for: Production APIs after you've validated with warn/shadow modes
Warn Mode
Request → Validate → Log if invalid → Execute handler → Validate response → Log if invalid
- Request violations: Logged as warnings, request continues to handler
- Response violations: Logged as warnings, response is sent normally
- Best for: Transitioning from shadow to strict, identifying issues without breaking clients
Shadow Mode
Request → Validate → Execute handler → Validate response
↓ ↓
Record metrics Record metrics
(no logging) (no logging)
- Request violations: Only recorded in metrics, no logs, no impact
- Response violations: Only recorded in metrics, no logs, no impact
- Best for: Initial deployment to measure contract compliance without any risk
Recommended Rollout Strategy
┌─────────────────────────────────────────────────────────────────────────────┐
│ Production Rollout Phases │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Phase 1 Phase 2 Phase 3 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ SHADOW │ ──────► │ WARN │ ──────► │ STRICT │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ • Deploy middleware • Enable logging • Full enforcement │
│ • Monitor metrics • Review violations • Reject invalid requests │
│ • Zero risk • Fix issues found • Contract is enforced │
│ • Measure baseline • No client impact • Clients must comply │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Step-by-step:
-
Deploy with
shadow— Add middleware to production with zero risk. Monitor thecvt_validation_errors_totalmetric to understand your baseline. -
Analyze metrics — Use Grafana dashboards to identify which endpoints have the most violations. Common issues: missing required fields, wrong types, undocumented endpoints.
-
Switch to
warn— Enable logging to get detailed error messages. Review logs to understand exactly what's failing and why. -
Fix violations — Either update your OpenAPI spec to match reality, or fix client/server code to match the spec. This is where you decide what the contract should be.
-
Switch to
strict— Full enforcement. Invalid requests are rejected. Your API contract is now enforced.
Mode Configuration by SDK
Each SDK uses language-appropriate naming conventions:
| SDK | Strict | Warn | Shadow |
|---|---|---|---|
| Node.js | mode: "strict" | mode: "warn" | mode: "shadow" |
| Python | ValidationMode.STRICT | ValidationMode.WARN | ValidationMode.SHADOW |
| Go | producer.ModeStrict | producer.ModeWarn | producer.ModeShadow |
| Java | ValidationMode.STRICT | ValidationMode.WARN | ValidationMode.SHADOW |
- Node.js
- Python
- Go
- Java
import { createExpressMiddleware } from "@sahina/cvt-sdk/producer";
app.use(
createExpressMiddleware({
schemaId: "petstore",
validator,
mode: "strict", // or "warn" or "shadow"
}),
);
from cvt_sdk.producer import ProducerConfig, ValidationMode
config = ProducerConfig(
schema_id="petstore",
validator=validator,
mode=ValidationMode.STRICT, # or WARN or SHADOW
)
config := producer.Config{
SchemaID: "petstore",
Validator: validator,
Mode: producer.ModeStrict, // or ModeWarn or ModeShadow
}
ProducerConfig config = ProducerConfig.builder()
.schemaId("petstore")
.validator(myValidator)
.mode(ValidationMode.STRICT) // or WARN or SHADOW
.build();
Metrics Emitted
All modes emit Prometheus metrics for monitoring:
| Metric | Description |
|---|---|
cvt_validations_total | Total validations by schema, method, result |
cvt_validation_errors_total | Validation failures by error category |
cvt_validation_duration_seconds | Validation latency histogram by schema and method |
Use these metrics to:
- Track contract compliance over time
- Identify problematic endpoints before enabling strict mode
- Monitor the impact of API changes
- Alert on sudden increases in validation failures
Response Validation Behavior
Important: Response validation can only log, never block.
Client Request
│
▼
Validate Request ─── Can reject (strict mode)
│
▼
Your Handler
│
▼
Send Response ─── Already sent to client!
│
▼
Validate Response ─── Can only log (too late to block)
This is by design: by the time we validate the response, it's already been sent to the client. Response validation helps you detect implementation drift (where your code diverges from your spec) but can't prevent invalid responses from reaching clients.
To prevent invalid responses: Validate your response data before sending it, or use typed response builders that enforce the schema at compile time.
Related Documentation
- Producer Testing Guide - Full producer testing workflow
- Observability Guide - Metrics and monitoring
- Configuration Reference - Environment variables