Skip to main content

YAML Syntax & Structure

flAPI extends standard YAML with section includes, environment-variable substitution, and a fixed set of top-level keys per endpoint. This guide covers the extended syntax, the supported endpoint shapes (REST + 3 MCP variants), and path resolution rules.

Extended YAML Features

flAPI's YAML parser supports standard YAML plus these extensions:

1. Section Includes

flAPI's include directive copies one named section out of another YAML file. The syntax is:

{{include:section_name from path/to/file.yaml}}

No quotes around the path. The path is resolved relative to the including file.

Includable sections (anything else is rejected):

  • request
  • auth
  • rate-limit
  • connection
  • template-source
  • cache
  • heartbeat

Example — a shared customer-common.yaml:

# common/customer-common.yaml
request:
- field-name: id
field-in: query
validators:
- type: int
min: 1

auth:
enabled: true
type: basic
users:
- username: admin
password: '${ADMIN_PASSWORD}'
roles: [admin, read]

rate-limit:
enabled: true
max: 100
interval: 60

connection:
- customers-parquet

template-source: customers.sql

cache:
enabled: true
table: customers_cache
schema: analytics
schedule: 5m

Endpoint that pulls them in:

# customers-rest.yaml
url-path: /customers/
method: GET

{{include:request from common/customer-common.yaml}}
{{include:auth from common/customer-common.yaml}}
{{include:rate-limit from common/customer-common.yaml}}
{{include:connection from common/customer-common.yaml}}
{{include:template-source from common/customer-common.yaml}}
{{include:cache from common/customer-common.yaml}}

with-pagination: true

You can also keep multiple variants of a section under different top-level keys in the source file (auth, auth-dev, auth-prod, ...) and choose which to include:

{{include:auth-prod from common/customer-common.yaml}}

This works because the include directive grabs the value of whatever top-level key you name.

2. Environment Variables

Reference environment variables anywhere a value is expected. Two equivalent forms exist:

auth:
jwt-secret: '${JWT_SECRET}'
jwt-issuer: '{{env.JWT_ISSUER}}'
  • ${VAR} — shell-style.
  • {{env.VAR}} — Mustache-style, also works inside SQL templates.

Environment variables must be whitelisted in flapi.yaml (template.environment-whitelist). Unknown / unwhitelisted variables cause a load-time error.

3. Template Variables

These context variables are available inside SQL templates (not in endpoint YAML):

  • params.* — validated request parameters
  • conn.* — properties of the first connection listed under connection:
  • auth.* — authenticated user (auth.username, auth.roles, auth.email, auth.type, auth.authenticated)
  • cache.* — cache metadata (in cache-refresh templates)
  • env.* — whitelisted environment variables

See SQL Templating for the full reference.

Endpoint YAML Structure

Each YAML file defines one endpoint of one of four kinds. flAPI decides which kind by inspecting the top-level keys:

Top-level key presentEndpoint kind
url-pathREST
mcp-toolMCP tool
mcp-resourceMCP resource
mcp-promptMCP prompt

url-path can also coexist with mcp-tool / mcp-resource when the same query should be exposed both ways.

Basic REST Endpoint

# sqls/customers.yaml

url-path: /customers/
method: GET

request:
- field-name: segment
field-in: query
description: Market segment to filter by
required: false
validators:
- type: enum
allowedValues: [AUTOMOBILE, BUILDING, FURNITURE, HOUSEHOLD, MACHINERY]

- field-name: min_balance
field-in: query
description: Minimum account balance
required: false
validators:
- type: int
min: 0
max: 1000000

template-source: customers.sql

connection:
- customers-parquet

with-pagination: true

cache:
enabled: true
table: customers_cache
schema: analytics
schedule: 5m
primary-key: [id]
# sqls/orders.yaml

url-path: /orders/:order_id
method: GET

request:
- field-name: order_id
field-in: path
description: Order ID
required: true
validators:
- type: int
min: 1

- field-name: status
field-in: query
description: Order status filter
required: false
default: "completed"
validators:
- type: enum
allowedValues: [pending, completed, cancelled, refunded]

- field-name: start_date
field-in: query
description: Start of the date range (YYYY-MM-DD)
required: false
validators:
- type: date
min: "2020-01-01"

- field-name: end_date
field-in: query
description: End of the date range (YYYY-MM-DD)
required: false
validators:
- type: date

template-source: orders/detail.sql
connection:
- bigquery-warehouse
- customers-parquet

with-pagination: true
request-fields-validation: true

auth:
enabled: true
type: bearer
jwt-secret: '${JWT_SECRET}'
jwt-issuer: 'my-auth-server'

rate-limit:
enabled: true
max: 1000
interval: 60

cache:
enabled: true
table: orders_cache
schema: analytics
schedule: 15m
primary-key: [order_id]
cursor:
column: updated_at
type: timestamp
retention:
keep-last-snapshots: 5
max-snapshot-age: 14d

Top-Level Keys Reference

KeyTypeDefaultNotes
url-pathstringRequired for REST endpoints. Path parameters use :name.
methodstringGETSingle HTTP method: GET, POST, PUT, PATCH, DELETE. Use one string, not a list.
template-sourcestringRequired for REST + MCP-tool + MCP-resource. Path to .sql file.
connectionlist[string]Required when a template is used. First entry exposes conn.* in templates.
requestlist[]Request parameter definitions.
with-paginationbooltrueWraps responses in {data, next, total_count} and accepts limit/offset.
request-fields-validationboolfalseReject requests containing parameters not declared in request:.
authmappinginherits globalPer-endpoint auth override — see Authentication.
rate-limitmappinginherits globalPer-endpoint rate-limit override.
cachemappingDuckLake cache configuration.
operationmappingautotype: Read / type: Write, plus returns-data, transaction, validate-before-write.
heartbeatmappingEndpoint heartbeat config.
mcp-toolmappingDeclares an MCP tool.
mcp-resourcemappingDeclares an MCP resource.
mcp-promptmappingDeclares an MCP prompt.

Note: method is a single string. There is no methods: [GET] list form.

MCP Endpoint Variants

flAPI exposes the same YAML configuration as three MCP shapes for AI agents.

MCP Tool

A callable tool that runs an SQL query. Only these three keys are recognised:

KeyTypeDefaultDescription
mcp-tool.namestringrequiredUnique tool name (alphanumeric / underscore)
mcp-tool.descriptionstringrequiredTool description for the LLM
mcp-tool.result-mime-typestringapplication/jsonMIME type sent back to the model
# sqls/customers-mcp.yaml
mcp-tool:
name: search_customers
description: |
Search and retrieve customer information with flexible filtering
by segment, balance, name, or registration date.
result-mime-type: application/json

request:
- field-name: segment
field-in: query
required: false
validators:
- type: enum
allowedValues: [AUTOMOBILE, BUILDING, FURNITURE, MACHINERY, HOUSEHOLD]

- field-name: min_balance
field-in: query
required: false
validators:
- type: int
min: 0
max: 1000000

- field-name: name
field-in: query
required: false
validators:
- type: string
min: 2
max: 50
regex: '^[A-Za-z ]+$'

template-source: customers/search.sql
connection:
- customers-parquet

MCP Resource

A readable resource — e.g. an introspection query exposed as data.

KeyTypeDefaultDescription
mcp-resource.namestringrequiredUnique resource name
mcp-resource.descriptionstringrequiredDescription shown to the model
mcp-resource.mime-typestringapplication/jsonContent MIME type
mcp-resource:
name: customer_schema
description: Customer table schema and field definitions
mime-type: application/json

template-source: customers/schema.sql
connection:
- customers-parquet

MCP Prompt

A reusable, parameterised Mustache prompt. Prompts have an inline template (no template-source) and do not need a connection.

KeyTypeDefaultDescription
mcp-prompt.namestringrequiredUnique prompt name
mcp-prompt.descriptionstringrequiredDescription shown to the model
mcp-prompt.templatestringrequiredInline Mustache template body
mcp-prompt.argumentslist[string][]Template argument names
mcp-prompt:
name: customer_analysis
description: Generate a customer analysis prompt
template: |
You are a data analyst. Analyse this customer:
{{#customer_id}}Customer ID: {{customer_id}}{{/customer_id}}
{{#segment}}Segment: {{segment}}{{/segment}}

Provide insights on purchasing patterns and recommendations.
arguments:
- customer_id
- segment

File Structure Best Practices

project/
├── flapi.yaml # Main configuration
├── common/ # Shared section definitions
│ ├── customer-common.yaml
│ └── _shared/
│ └── auth.yaml

└── sqls/ # Endpoint definitions + SQL templates
├── customers/
│ ├── list.yaml # GET /customers/
│ ├── list.sql
│ ├── detail.yaml # GET /customers/:id
│ ├── detail.sql
│ └── search.yaml # MCP search tool

├── orders/
│ ├── list.yaml
│ ├── list.sql
│ ├── create.yaml # POST /orders/
│ └── create.sql

└── analytics/
├── revenue.yaml
└── revenue.sql

Naming Conventions

sqls/customers/list.yaml      -> GET    /customers/
sqls/customers/detail.yaml -> GET /customers/:id
sqls/customers/search.yaml -> GET /customers/search/
sqls/orders/create.yaml -> POST /orders/
sqls/analytics/revenue.yaml -> GET /analytics/revenue/

Path Resolution

Includes

Includes resolve relative to the including file:

# flapi.yaml (project root) — main config does not use the include directive,
# it lives only inside endpoint YAML files.

# sqls/customers-rest.yaml
{{include:request from ../common/customer-common.yaml}}
# Resolves to: common/customer-common.yaml

Template Source

template-source: resolves relative to the directory configured as template.path in flapi.yaml. Sub-paths are honoured:

# template.path: sqls/

template-source: customers.sql
# -> sqls/customers.sql

template-source: customers/list.sql
# -> sqls/customers/list.sql

Absolute paths and remote (s3://, https://, ...) paths are used as-is.

Cache Template Override

cache:
template-file: customers_cache.sql
# Same resolution rules as template-source.

YAML Best Practices

1. Consistent indentation

# 2 spaces throughout
request:
- field-name: status
field-in: query
validators:
- type: enum
allowedValues: [active, inactive]

2. Comment intent, not mechanics

# Customer search endpoint
# Cached every 5 minutes, basic auth, read-only.
url-path: /customers/search/

3. Use descriptive field names

- field-name: customer_segment
description: Market segment classification
- field-name: minimum_account_balance
description: Minimum balance in USD
# -- Endpoint -----------------------------------------------
url-path: /customers/
method: GET

# -- Request ------------------------------------------------
request:
- field-name: id
field-in: query

# -- Caching ------------------------------------------------
cache:
enabled: true

5. Pull shared config out into common files

{{include:auth from ../common/_shared/auth-basic.yaml}}
{{include:rate-limit from ../common/_shared/rate-limit-standard.yaml}}

6. Always validate enums and ranges

validators:
- type: enum
allowedValues: [pending, completed, cancelled]

7. Multi-line descriptions for MCP tools

mcp-tool:
name: search_orders
description: |
Search orders by status, customer, and date range.
Returns up to 100 orders per page (use limit/offset for more).

8. Keep secrets out of YAML

auth:
jwt-secret: '${JWT_SECRET}' # not a literal

Validation & Testing

Validate YAML syntax

flapii endpoints validate /customers/

Inspect resolved paths and includes

flapii config paths sqls/customers/list.yaml

Export resolved configuration

flapii config export sqls/customers/list.yaml

Troubleshooting

YAML syntax errors

Check indentation and quoting; YAML is whitespace-sensitive.

Include not found

Error: Include file not found: common/missing.yaml

Verify the file exists at the path relative to the including YAML, and that the section name on the left of from matches a top-level key in that file.

Environment variable not resolved

Error: Environment variable 'DB_HOST' not found

Export the variable and add it to template.environment-whitelist in flapi.yaml.

Template path not found

Error: Template file not found: customers.sql

Confirm template-source resolves against template.path from flapi.yaml.

Next Steps

🍪 Cookie Settings