Skip to main content

Response Format

flAPI provides flexible response formatting with JSON by default, plus support for CSV and Parquet. This guide covers response structure, metadata, error handling, and format customization.

Default JSON Response

Basic Structure

{
"data": [
{"id": 1, "name": "Product A", "price": 29.99},
{"id": 2, "name": "Product B", "price": 39.99}
],
"metadata": {
"count": 2,
"execution_time_ms": 1.5,
"cached": true
}
}

Response Fields

FieldTypeDescription
dataArrayQuery results
metadataObjectExecution information
metadata.countIntegerNumber of rows returned
metadata.execution_time_msFloatQuery execution time
metadata.cachedBooleanWhether result came from cache

Response Configuration

Configure response format in your endpoint:

url-path: /products/

response:
format: json # json, csv, or parquet
include_metadata: true
pagination:
enabled: true
default_limit: 20
max_limit: 100

Response Formats

JSON (Default)

Content-Type: application/json

response:
format: json

Example:

$ curl http://localhost:8080/products/

{
"data": [
{"id": 1, "name": "Product A"}
]
}

CSV

Content-Type: text/csv

response:
format: csv

Example:

$ curl http://localhost:8080/products/?format=csv

id,name,price
1,Product A,29.99
2,Product B,39.99

Headers:

Content-Type: text/csv
Content-Disposition: attachment; filename=products.csv

Parquet

Content-Type: application/octet-stream

response:
format: parquet

Example:

$ curl http://localhost:8080/products/?format=parquet -o products.parquet

Perfect for:

  • Data exports
  • Analytics tools
  • Data pipelines
  • Large datasets

Pagination

Enable Pagination

response:
pagination:
enabled: true
default_limit: 20
max_limit: 100

Paginated Response

{
"data": [...],
"pagination": {
"limit": 20,
"offset": 0,
"total": 150,
"has_more": true
},
"links": {
"self": "/products?limit=20&offset=0",
"next": "/products?limit=20&offset=20",
"prev": null
}
}

Pagination Parameters

Add to your endpoint:

request:
- field-name: limit
field-in: query
required: false
validators:
- type: int
min: 1
max: 100

- field-name: offset
field-in: query
required: false
validators:
- type: int
min: 0

SQL template:

SELECT * FROM products
LIMIT {{{params.limit|20}}}
OFFSET {{{params.offset|0}}}
# Page 1
curl http://localhost:8080/products?limit=20&offset=0

# Page 2
curl http://localhost:8080/products?limit=20&offset=20

# Page 3
curl http://localhost:8080/products?limit=20&offset=40

Metadata

Standard Metadata

Included by default:

{
"metadata": {
"count": 25,
"execution_time_ms": 2.3,
"cached": true,
"cache_age_seconds": 120
}
}

Custom Metadata

Add custom metadata in SQL:

SELECT
*,
CURRENT_TIMESTAMP as query_timestamp,
VERSION() as database_version
FROM products

Disable Metadata

response:
include_metadata: false

Returns just the data:

[
{"id": 1, "name": "Product A"},
{"id": 2, "name": "Product B"}
]

Error Responses

Standard Error Format

{
"error": {
"message": "Parameter 'category' must be one of: electronics, clothing, books",
"code": "VALIDATION_ERROR",
"status": 400,
"details": {
"parameter": "category",
"provided": "invalid",
"expected": ["electronics", "clothing", "books"]
}
}
}

HTTP Status Codes

CodeMeaningExample
200SuccessQuery executed successfully
400Bad RequestInvalid parameter value
401UnauthorizedMissing or invalid authentication
403ForbiddenUser lacks required permissions
404Not FoundEndpoint doesn't exist
429Too Many RequestsRate limit exceeded
500Internal Server ErrorQuery execution failed

Error Types

Validation Error (400):

{
"error": {
"message": "Missing required parameter: customer_id",
"code": "VALIDATION_ERROR",
"status": 400
}
}

Authentication Error (401):

{
"error": {
"message": "Invalid or missing authentication token",
"code": "AUTHENTICATION_ERROR",
"status": 401
}
}

Query Error (500):

{
"error": {
"message": "Query execution failed",
"code": "QUERY_ERROR",
"status": 500,
"details": {
"sql_state": "42P01",
"message": "relation 'missing_table' does not exist"
}
}
}

Content Negotiation

Accept Header

Request specific format using Accept header:

# JSON (default)
curl -H "Accept: application/json" http://localhost:8080/products/

# CSV
curl -H "Accept: text/csv" http://localhost:8080/products/

# Parquet
curl -H "Accept: application/parquet" http://localhost:8080/products/

Query Parameter

Override format with query parameter:

# Force CSV
curl http://localhost:8080/products/?format=csv

# Force Parquet
curl http://localhost:8080/products/?format=parquet

Column Formatting

Date/Time Formatting

DuckDB returns timestamps in ISO 8601:

{
"created_at": "2024-01-15T10:30:00Z",
"date": "2024-01-15"
}

Format in SQL if needed:

SELECT
STRFTIME(created_at, '%Y-%m-%d') as date,
STRFTIME(created_at, '%H:%M:%S') as time
FROM orders

Number Formatting

Precise decimals preserved:

{
"price": 29.99,
"quantity": 150,
"weight_kg": 2.456
}

Round in SQL if needed:

SELECT
ROUND(price, 2) as price,
ROUND(weight_kg, 3) as weight_kg
FROM products

NULL Handling

NULL values returned as null:

{
"id": 123,
"optional_field": null
}

Convert in SQL if needed:

SELECT
COALESCE(optional_field, 'N/A') as optional_field
FROM table

Compression

Enable Compression

flAPI automatically compresses responses > 1KB:

response:
compression:
enabled: true
min_size_bytes: 1024
algorithms: ['gzip', 'deflate']

Response Headers

Content-Encoding: gzip
Vary: Accept-Encoding

Client Usage

Most HTTP clients handle decompression automatically:

# curl handles gzip automatically
curl http://localhost:8080/products/

# Disable compression
curl --compressed http://localhost:8080/products/

Streaming Responses

For large datasets, enable streaming:

response:
streaming:
enabled: true
chunk_size: 1000 # Rows per chunk

Benefits:

  • Lower memory usage
  • Faster time to first byte
  • Better for large datasets

Client receives data incrementally:

{"id": 1, "name": "Product A"}
{"id": 2, "name": "Product B"}
{"id": 3, "name": "Product C"}
...

CORS Configuration

Enable CORS for web applications:

response:
cors:
enabled: true
origins: ['https://example.com', 'https://app.example.com']
methods: ['GET', 'POST', 'PUT', 'DELETE']
headers: ['Content-Type', 'Authorization']
credentials: true

Response Headers:

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization

Caching Headers

flAPI sets appropriate cache headers:

Cache-Control: public, max-age=3600
ETag: "a1b2c3d4e5f6"
Last-Modified: Mon, 15 Jan 2024 10:30:00 GMT

Configure caching headers:

response:
cache_control:
max_age: 3600 # 1 hour
public: true

Best Practices

1. Always Include Metadata

# ✅ Good: Metadata helps debugging
response:
include_metadata: true

# ❌ Bad: No execution context
response:
include_metadata: false

2. Implement Pagination

# ✅ Good: Prevents large responses
response:
pagination:
enabled: true
max_limit: 100

# ❌ Bad: Unlimited results
response:
pagination:
enabled: false

3. Use Appropriate Formats

# For web apps: JSON
format: json

# For data exports: CSV or Parquet
format: csv

# For analytics: Parquet
format: parquet

4. Set Reasonable Limits

-- ✅ Good: Always limit results
SELECT * FROM large_table
LIMIT 1000

-- ❌ Bad: No limit
SELECT * FROM large_table

5. Return Clear Errors

# ✅ Good: Detailed error messages
error_message: "Invalid segment. Use AUTOMOTIVE, BUILDING, or FURNITURE"

# ❌ Bad: Vague errors
error_message: "Invalid input"

Testing Responses

Test JSON Response

$ curl http://localhost:8080/products/ | jq

{
"data": [...]
}

Test CSV Response

$ curl "http://localhost:8080/products/?format=csv"

id,name,price
1,Product A,29.99

Test Error Handling

$ curl "http://localhost:8080/products/?category=invalid"

{
"error": {
"message": "Parameter 'category' must be one of: ...",
"status": 400
}
}

Test Pagination

$ curl "http://localhost:8080/products/?limit=5" | jq '.pagination'

{
"limit": 5,
"offset": 0,
"total": 150,
"has_more": true
}

Next Steps

🍪 Cookie Settings