Validation
Parameter validation is critical for security, data integrity, and API reliability. flAPI ships with seven built-in validator types — int, string, enum, email, uuid, date, and time.
When a field carries a typed validator and is referenced via double-brace {{ params.X }} at top level of the template, flAPI rewrites the reference to a DuckDB ? placeholder and binds the value via duckdb_bind_* — the value travels as a primitive, never as SQL text. This is the hard boundary; the legacy keyword regex is a fallback for non-bindable sites (triple-brace {{{ }}} and untyped fields).
Why Validation Matters
Without validation: unclear 400s, weak typing, regex relies on keyword heuristics.
With validation: strict type parsing (1; DROP TABLE cannot slip through as 1), DuckDB prepared-statement binding for typed fields, clear error messages, automatic OpenAPI docs.
Basic Validation
Add one or more validators to any request field:
request:
- field-name: customer_id
field-in: query
required: true
validators:
- type: int
min: 1
max: 999999
preventSqlInjection: true
preventSqlInjection defaults to true. For numeric/temporal bindable types (int, double, boolean, date, time) the SQL-keyword regex is now demoted to a debug log — the prepared-statement bind is the real defense, and the regex's notorious false positives (e.g. latitude=1.111) are gone. Varchar-bindable types (string, uuid, email, enum) keep the regex because flAPI templates routinely embed them via triple-brace {{{ }}} for LIKE patterns. Set preventSqlInjection: false only when you have a deliberate reason (e.g. a field that holds an opaque token).
Validator Types
flAPI supports exactly seven validator types. Anything else is silently ignored by src/request_validator.cpp.
Integer (int)
For whole numbers. Range bounds default to INT_MIN/INT_MAX.
validators:
- type: int
min: 1
max: 1000
preventSqlInjection: true
String (string)
Length is controlled with min and max (in characters), and pattern matching uses regex (full match required).
validators:
- type: string
min: 3
max: 50
regex: '^[A-Za-z0-9_-]+$'
preventSqlInjection: true
Use a separate enum validator if the field should be one of a fixed set of values — string does not accept an enum: key.
Enum (enum)
Whitelist of allowed values.
validators:
- type: enum
allowedValues: [draft, published, archived]
Email (email)
Built-in regex: [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}.
validators:
- type: email
UUID (uuid)
Standard 36-character form (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
validators:
- type: uuid
Date (date)
Format YYYY-MM-DD. Optional min / max are strings in the same format.
validators:
- type: date
min: "2000-01-01"
max: "2025-12-31"
Time (time)
Format HH:MM:SS. Optional min / max are strings in the same format.
validators:
- type: time
min: "08:00:00"
max: "18:00:00"
SQL Injection Prevention
When preventSqlInjection is true (the default), flAPI rejects values that contain SQL keywords (SELECT, INSERT, UPDATE, DELETE, DROP, UNION, ...), comment markers (--, /*, */), or common injection patterns (' OR 1=1, '; ...).
Only set it to false for fields where you intentionally accept SQL-looking content (very rare).
Multiple Validators on One Field
Stack validators when you need multiple checks:
- field-name: username
field-in: query
required: true
validators:
- type: string
min: 3
max: 20
regex: '^[a-zA-Z0-9_]+$'
All declared validators must pass for the parameter to be accepted.
Common Validation Patterns
Email Address
- field-name: email
validators:
- type: email
Phone Number (E.164)
- field-name: phone
validators:
- type: string
regex: '^\+?[1-9]\d{1,14}$'
Country Code
- field-name: country
validators:
- type: string
regex: '^[A-Z]{2}$'
Status Enum
- field-name: status
validators:
- type: enum
allowedValues: [active, inactive, pending]
Customer UUID
- field-name: customer_id
validators:
- type: uuid
Date Range
- field-name: registration_date
validators:
- type: date
min: "2000-01-01"
max: "2025-12-31"
Strict Mode: Reject Unknown Parameters
By default flAPI accepts any extra query parameter and silently ignores it. Set request-fields-validation: true on the endpoint to reject requests that include parameters not declared under request:.
url-path: /customers/
method: GET
request-fields-validation: true # default: false
request:
- field-name: id
field-in: query
validators:
- type: int
min: 1
A request containing ?id=1&unknown=foo returns a 400 with the offending field name. The pagination parameters offset and limit are always allowed.
Error Responses
Validation errors come back as HTTP 400 with one entry per failed field:
{
"errors": [
{ "field": "segment", "message": "Invalid enum value" },
{ "field": "id", "message": "Integer is less than the minimum allowed value" }
]
}
The exact messages come from src/request_validator.cpp:
| Validator | Message |
|---|---|
int (out of range) | Integer is less than/greater than the minimum/maximum allowed value |
int (non-numeric) | Invalid integer value |
string (too short/long) | String is shorter/longer than the minimum/maximum allowed length |
string (regex mismatch) | Invalid string format |
enum | Invalid enum value |
email | Invalid email format |
uuid | Invalid UUID format |
date / time | Invalid date/time format, Date/Time is before/after the minimum/maximum allowed date/time |
| any (SQL pattern) | Potential SQL injection detected |
| missing required | Required field is missing |
| unknown (strict mode) | Unknown parameter not defined in endpoint configuration |
Security Best Practices
1. Always validate user input
validators:
- type: enum
allowedValues: [active, inactive]
2. Whitelist with enum where possible
validators:
- type: enum
allowedValues: [name, email, created_at]
3. Limit string length and tighten regex
validators:
- type: string
min: 1
max: 100
regex: '^[A-Za-z0-9_ \-]+$'
4. Restrict numeric ranges
validators:
- type: int
min: 1
max: 1000
5. Enable strict field mode for sensitive endpoints
request-fields-validation: true
Real-World Examples
Product Search
url-path: /products/search/
method: GET
request:
- field-name: query
field-in: query
required: true
validators:
- type: string
min: 3
max: 100
- field-name: category
field-in: query
required: false
validators:
- type: enum
allowedValues: [electronics, clothing, books, home]
- field-name: min_price
field-in: query
required: false
validators:
- type: int
min: 0
max: 1000000
- field-name: limit
field-in: query
required: false
validators:
- type: int
min: 1
max: 100
Date Range Analytics
url-path: /analytics/revenue/
method: GET
request:
- field-name: start_date
field-in: query
required: true
validators:
- type: date
min: "2020-01-01"
- field-name: end_date
field-in: query
required: true
validators:
- type: date
- field-name: granularity
field-in: query
required: false
default: day
validators:
- type: enum
allowedValues: [day, week, month, quarter, year]
Customer Lookup by UUID
url-path: /customers/:customer_uuid
method: GET
request:
- field-name: customer_uuid
field-in: path
required: true
validators:
- type: uuid
Common Issues
Validators not firing
- Check indentation under
validators:— it must be a list of mappings, not a single mapping. - Make sure the type is one of the seven supported names (
int,string,enum,email,uuid,date,time).
Regex rejects valid input
- The pattern must match the whole value (anchored implicitly).
- Escape backslashes once for YAML, then once more if needed for the regex engine.
"Potential SQL injection detected" on a clean value
- The default scanner blocks SQL keywords as whole words and certain patterns. If the value is legitimate (e.g. a column name in a CMS search), set
preventSqlInjection: falseon that validator and rely on a regex / enum instead.
Next Steps
- Parameters — learn the four parameter sources
- SQL Templating — use validated params safely
- Endpoints Overview — full endpoint tutorial
- Authentication — secure your endpoints