Add Validation Once, Enforce it Everywhere
Here's the beautiful part about firestone's validation: you write it once, in your resource schema, and it works across all four outputs.
No duplicate validation logic. No keeping multiple validation rules in sync. Just JSON Schema validation keywords (reference), and firestone handles the rest.
How It Works
Add validation to your resource:
properties:
username:
type: string
minLength: 3
maxLength: 20
pattern: "^[a-z0-9_]+$"
description: "Lowercase letters, numbers, and underscores only"
email:
type: string
format: email
age:
type: integer
minimum: 18
maximum: 120
role:
type: string
enum: [user, admin, moderator]
required: [username, email]
Now that validation automatically works in:
1. OpenAPI Spec The generated spec includes all validation rules. API gateways and server frameworks use these to reject invalid requests:
# In generated OpenAPI spec
username:
type: string
minLength: 3
maxLength: 20
pattern: "^[a-z0-9_]+$"
2. CLI Generated Click commands validate before sending requests:
$ python cli.py users create --username "AB" --email "invalid" --age 15
Error: username must be at least 3 characters
Error: email is not a valid email address
Error: age must be at least 18
3. Streamlit UI Form inputs enforce validation rules automatically:
- Text inputs show min/max length
- Number inputs enforce min/max values
- Dropdowns are populated from enums
- Forms won't submit with invalid data
4. Generated Client SDKs When you generate client libraries from the OpenAPI spec, they include validation, catching errors before making network requests.
Common Validation Keywords
Strings
type: string
minLength: 3 # Minimum characters
maxLength: 100 # Maximum characters
pattern: "^[A-Z]" # Regex match
format: email # Built-in formats: email, uri, date, time, etc.
Numbers
type: integer # or "number" for floats
minimum: 0 # >= 0
maximum: 100 # <= 100
multipleOf: 5 # Must be multiple of 5
Enums (Choices)
type: string
enum: [pending, approved, rejected]
Arrays
type: array
minItems: 1 # At least one item
maxItems: 10 # At most 10 items
uniqueItems: true # No duplicates
Objects
type: object
required: [name, email] # These fields are mandatory
additionalProperties: false # Only allow defined properties
Real-World Example
Let's create a user resource with comprehensive validation:
kind: user
schema:
type: array
key:
name: user_id
items:
type: object
properties:
username:
type: string
minLength: 3
maxLength: 20
pattern: "^[a-z0-9_]+$"
description: "Alphanumeric and underscore, lowercase only"
email:
type: string
format: email
description: "Valid email address required"
age:
type: integer
minimum: 18
maximum: 120
description: "Must be 18 or older"
bio:
type: string
maxLength: 500
description: "Short biography, max 500 characters"
role:
type: string
enum: [user, moderator, admin]
default: user
description: "User permission level"
tags:
type: array
items:
type: string
maxItems: 10
uniqueItems: true
description: "Up to 10 unique tags"
required: [username, email, age]
Generate everything:
# OpenAPI spec with all validation
firestone generate --resources user.yaml openapi > openapi.yaml
# CLI that validates inputs
firestone generate --resources user.yaml cli > cli.py
# Streamlit UI with validated forms
firestone generate --resources user.yaml streamlit > app.py
Now try to create an invalid user:
- Via CLI: Validation fails before request is sent
- Via Streamlit: Form shows errors, won't submit
- Via API: Server returns 400/422 with detailed error messages
- Via Generated Client: Client-side validation catches errors
All from the same validation rules in your resource schema.
The Single Source of Truth
Need to change a validation rule? Update it once in your resource:
# Change minimum age from 18 to 21
age:
type: integer
minimum: 21 # Changed from 18
maximum: 120
Regenerate all four outputs:
firestone generate --resources user.yaml openapi > openapi.yaml
firestone generate --resources user.yaml cli > cli.py
firestone generate --resources user.yaml streamlit > app.py
# (and AsyncAPI if you use it)
Now the new minimum age of 21 is enforced in:
- OpenAPI spec (API validation)
- CLI (command validation)
- Streamlit (form validation)
- AsyncAPI (message validation)
One change, four updates, zero inconsistencies.
Best Practices
1. Validate at the Source Put validation in your resource schema, not scattered across application code.
2. Use Descriptive Messages
The description field helps users understand what's expected:
username:
type: string
pattern: "^[a-z0-9_]+$"
description: "Letters, numbers, and underscores only"
3. Fail Fast Let firestone's validation catch errors early - before they reach your database or business logic.
4. Test Your Validation Use the generated CLI to quickly test validation rules:
# Should fail
python cli.py users create --username "UPPERCASE" --email "bad" --age 10
# Should succeed
python cli.py users create --username "valid_user" --email "user@example.com" --age 25
Next Steps
Want to learn more about JSON Schema validation?
- JSON Schema Validation Reference - Complete validation keyword reference
Ready to see validation in action?
- Try it: Quickstart Tutorial - Build a resource with validation
- Go deeper: Schema Design - Best practices for designing schemas