ResolveSpec/pkg/restheadspec/HEADERS.md

14 KiB

RestHeadSpec Headers Documentation

RestHeadSpec provides a comprehensive header-based REST API where all query options are passed via HTTP headers instead of request body. This document describes all supported headers and their usage.

Overview

RestHeadSpec uses HTTP headers for:

  • Field selection
  • Filtering and searching
  • Joins and relationship loading
  • Sorting and pagination
  • Advanced query features
  • Response formatting
  • Transaction control

Header Naming Convention

All headers support optional identifiers at the end to allow multiple instances of the same header type. This is useful when you need to specify multiple related filters or options.

Examples:

# Standard header
x-preload: employees

# Headers with identifiers (both work the same)
x-preload-main: employees
x-preload-secondary: department
x-preload-1: projects

The system uses strings.HasPrefix() to match headers, so any suffix after the header name is ignored for matching purposes. This allows you to:

  • Add descriptive identifiers: x-sort-primary, x-sort-fallback
  • Add numeric identifiers: x-fieldfilter-status-1, x-fieldfilter-status-2
  • Organize related headers: x-preload-employee-data, x-preload-department-info

Header Categories

1. Field Selection

x-select-fields

Specify which columns to include in the response.

Format: Comma-separated list of column names

x-select-fields: id,name,email,created_at

x-not-select-fields

Specify which columns to exclude from the response.

Format: Comma-separated list of column names

x-not-select-fields: password,internal_notes

x-clean-json

Remove null and empty fields from the response.

Format: Boolean (true/false)

x-clean-json: true

x-fieldfilter-{colname}

Exact match filter on a specific column.

Format: x-fieldfilter-{columnName}: {value}

x-fieldfilter-status: active
x-fieldfilter-department_id: dept123

x-searchfilter-{colname}

Fuzzy search (ILIKE) on a specific column.

Format: x-searchfilter-{columnName}: {searchTerm}

x-searchfilter-name: john
x-searchfilter-description: website

This will match any records where the column contains the search term (case-insensitive).

x-searchop-{operator}-{colname}

Search with specific operators (AND logic).

Supported Operators:

  • contains - Contains substring (case-insensitive)
  • beginswith / startswith - Starts with (case-insensitive)
  • endswith - Ends with (case-insensitive)
  • equals / eq - Exact match
  • notequals / neq / ne - Not equal
  • greaterthan / gt - Greater than
  • lessthan / lt - Less than
  • greaterthanorequal / gte / ge - Greater than or equal
  • lessthanorequal / lte / le - Less than or equal
  • between - Between two values, exclusive (> val1 AND < val2) - format: value1,value2
  • betweeninclusive - Between two values, inclusive (>= val1 AND <= val2) - format: value1,value2
  • in - In a list of values - format: value1,value2,value3
  • empty / isnull / null - Is NULL or empty string
  • notempty / isnotnull / notnull - Is NOT NULL and not empty string

Type-Aware Features:

  • Text searches use case-insensitive matching (ILIKE with citext cast)
  • Numeric comparisons work with integers, floats, and decimals
  • Date/time comparisons handle timestamps correctly
  • JSON field support for structured data

Examples:

# Text search (case-insensitive)
x-searchop-contains-name: smith

# Numeric comparison
x-searchop-gt-age: 25
x-searchop-gte-salary: 50000

# Date range (exclusive)
x-searchop-between-created_at: 2024-01-01,2024-12-31

# Date range (inclusive)
x-searchop-betweeninclusive-birth_date: 1990-01-01,2000-12-31

# List matching
x-searchop-in-status: active,pending,review

# NULL checks
x-searchop-empty-deleted_at: true
x-searchop-notempty-email: true

x-searchor-{operator}-{colname}

Same as x-searchop but with OR logic instead of AND.

x-searchor-eq-status: active
x-searchor-eq-status: pending

x-searchand-{operator}-{colname}

Explicit AND logic (same as x-searchop).

x-searchand-gte-age: 18
x-searchand-lte-age: 65

x-searchcols

Specify columns for "all" search operations.

Format: Comma-separated list

x-searchcols: name,email,description

x-custom-sql-w

Raw SQL WHERE clause with AND condition.

Format: SQL WHERE clause (without the WHERE keyword)

x-custom-sql-w: status = 'active' AND created_at > '2024-01-01'

⚠️ Warning: Use with caution - ensure proper SQL injection prevention.

x-custom-sql-or

Raw SQL WHERE clause with OR condition.

Format: SQL WHERE clause

x-custom-sql-or: status = 'archived' OR is_deleted = true

3. Joins & Relations

x-preload

Preload related tables using the ORM's preload functionality.

Format: RelationName:field1,field2 or RelationName

Multiple relations can be specified using multiple headers or by separating with |

Examples:

# Preload all fields from employees relation
x-preload: employees

# Preload specific fields from employees
x-preload: employees:id,first_name,last_name,email

# Multiple preloads using pipe separator
x-preload: employees:id,name|department:id,name

# Multiple preloads using separate headers with identifiers
x-preload-1: employees:id,first_name,last_name
x-preload-2: department:id,name
x-preload-related: projects:id,name,status

x-expand

LEFT JOIN related tables and expand results inline.

Format: Same as x-preload

x-expand: department:id,name,code

Note: Currently, expand falls back to preload behavior. Full JOIN expansion is planned for future implementation.

x-custom-sql-join

Raw SQL JOIN statement.

Format: SQL JOIN clause

x-custom-sql-join: LEFT JOIN departments d ON d.id = employees.department_id

⚠️ Note: Not yet fully implemented.


4. Sorting & Pagination

x-sort

Sort results by one or more columns.

Format: Comma-separated list with optional + (ASC) or - (DESC) prefix

# Single column ascending (default)
x-sort: name

# Single column descending
x-sort: -created_at

# Multiple columns
x-sort: +department,- created_at,name

# Equivalent to: ORDER BY department ASC, created_at DESC, name ASC

x-limit

Limit the number of records returned.

Format: Integer

x-limit: 50

x-offset

Skip a number of records (offset-based pagination).

Format: Integer

x-offset: 100

x-cursor-forward

Cursor-based pagination (forward).

Format: Cursor string

x-cursor-forward: eyJpZCI6MTIzfQ==

⚠️ Note: Not yet fully implemented.

x-cursor-backward

Cursor-based pagination (backward).

Format: Cursor string

x-cursor-backward: eyJpZCI6MTIzfQ==

⚠️ Note: Not yet fully implemented.


5. Advanced Features

x-advsql-{colname}

Advanced SQL expression for a specific column.

Format: x-advsql-{columnName}: {SQLExpression}

x-advsql-full_name: CONCAT(first_name, ' ', last_name)
x-advsql-age_years: EXTRACT(YEAR FROM AGE(birth_date))

⚠️ Note: Not yet fully implemented in query execution.

x-cql-sel-{colname}

Computed Query Language - custom SQL expressions aliased as columns.

Format: x-cql-sel-{aliasName}: {SQLExpression}

x-cql-sel-employee_count: COUNT(employees.id)
x-cql-sel-total_revenue: SUM(orders.amount)

⚠️ Note: Not yet fully implemented in query execution.

x-distinct

Apply DISTINCT to the query.

Format: Boolean (true/false)

x-distinct: true

⚠️ Note: Implementation depends on ORM adapter support.

x-skipcount

Skip counting total records (performance optimization).

Format: Boolean (true/false)

x-skipcount: true

When enabled, the total count will be -1 in the response metadata.

x-skipcache

Bypass query cache (if caching is implemented).

Format: Boolean (true/false)

x-skipcache: true

x-fetch-rownumber

Get the row number of a specific record in the result set.

Format: Record identifier

x-fetch-rownumber: record123

⚠️ Note: Not yet implemented.

x-pkrow

Similar to x-fetch-rownumber - get row number by primary key.

Format: Primary key value

x-pkrow: 123

⚠️ Note: Not yet implemented.


6. Response Format

x-simpleapi

Return simple format (just the data array).

Format: Presence of header activates it

x-simpleapi: true

Response Format:

[
  { "id": 1, "name": "John" },
  { "id": 2, "name": "Jane" }
]

x-detailapi

Return detailed format with metadata (default).

Format: Presence of header activates it

x-detailapi: true

Response Format:

{
  "success": true,
  "data": [...],
  "metadata": {
    "total": 100,
    "filtered": 100,
    "limit": 50,
    "offset": 0
  }
}

x-syncfusion

Format response for Syncfusion UI components.

Format: Presence of header activates it

x-syncfusion: true

Response Format:

{
  "result": [...],
  "count": 100
}

7. Transaction Control

x-transaction-atomic

Use atomic transactions for write operations.

Format: Boolean (true/false)

x-transaction-atomic: true

Ensures that all write operations in the request succeed or fail together.


Base64 Encoding

Headers support base64 encoding for complex values. Use one of these prefixes:

  • ZIP_ - Base64 encoded value
  • __ - Base64 encoded value (double underscore)

Example:

# Plain value
x-custom-sql-w: status = 'active'

# Base64 encoded (same value)
x-custom-sql-w: ZIP_c3RhdHVzID0gJ2FjdGl2ZSc=

Complete Examples

Example 1: Basic Query

GET /api/employees HTTP/1.1
Host: example.com
x-select-fields: id,first_name,last_name,email,department_id
x-preload: department:id,name
x-searchfilter-name: john
x-searchop-gte-created_at: 2024-01-01
x-sort: -created_at,+last_name
x-limit: 50
x-offset: 0
x-skipcount: false
x-detailapi: true

Example 2: Complex Query with Multiple Filters and Preloads

GET /api/employees HTTP/1.1
Host: example.com
x-select-fields-main: id,first_name,last_name,email,department_id,manager_id
x-preload-1: department:id,name,code
x-preload-2: manager:id,first_name,last_name
x-preload-3: projects:id,name,status
x-fieldfilter-status-1: active
x-searchop-gte-created_at-filter1: 2024-01-01
x-searchop-lt-created_at-filter2: 2024-12-31
x-searchfilter-name-query: smith
x-sort-primary: -created_at
x-sort-secondary: +last_name
x-limit-page: 100
x-offset-page: 0
x-detailapi: true

Note: The identifiers after the header names (like -main, -1, -filter1, etc.) are optional and help organize multiple headers of the same type. Both approaches work:

# Without identifiers
x-preload: employees
x-preload: department

# With identifiers (more organized)
x-preload-1: employees
x-preload-2: department

Response:

{
  "success": true,
  "data": [
    {
      "id": "emp1",
      "first_name": "John",
      "last_name": "Doe",
      "email": "john@example.com",
      "department_id": "dept1",
      "department": {
        "id": "dept1",
        "name": "Engineering"
      }
    }
  ],
  "metadata": {
    "total": 1,
    "filtered": 1,
    "limit": 50,
    "offset": 0
  }
}

HTTP Method Mapping

  • GET /{schema}/{entity} - List all records
  • GET /{schema}/{entity}/{id} - Get single record
  • POST /{schema}/{entity} - Create record(s)
  • PUT /{schema}/{entity}/{id} - Update record
  • PATCH /{schema}/{entity}/{id} - Partial update
  • DELETE /{schema}/{entity}/{id} - Delete record
  • GET /{schema}/{entity}/metadata - Get table metadata

Implementation Status

Implemented:

  • Field selection (select/omit columns)
  • Filtering (field filters, search filters, operators)
  • Preloading relations
  • Sorting and pagination
  • Skip count optimization
  • Response format options
  • Base64 decoding

⚠️ Partially Implemented:

  • Expand (currently falls back to preload)
  • DISTINCT (depends on ORM adapter)

🚧 Planned:

  • Advanced SQL expressions (advsql, cql-sel)
  • Custom SQL joins
  • Cursor pagination
  • Row number fetching
  • Full expand with JOIN
  • Query caching control

Security Considerations

  1. SQL Injection: Custom SQL headers (x-custom-sql-*) should be properly sanitized or restricted to trusted users only.

  2. Query Complexity: Consider implementing query complexity limits to prevent resource exhaustion.

  3. Authentication: Implement proper authentication and authorization checks before processing requests.

  4. Rate Limiting: Apply rate limiting to prevent abuse.

  5. Field Restrictions: Consider implementing field-level permissions to restrict access to sensitive columns.


Performance Tips

  1. Use x-skipcount: true for large datasets when you don't need the total count
  2. Select only needed columns with x-select-fields
  3. Use preload wisely - only load relations you need
  4. Implement proper database indexes for filtered and sorted columns
  5. Consider pagination for large result sets

Migration from ResolveSpec

RestHeadSpec is an alternative to ResolveSpec that uses headers instead of request body for options:

ResolveSpec (body-based):

POST /api/departments
{
  "operation": "read",
  "options": {
    "preload": [{"relation": "employees"}],
    "filters": [{"column": "status", "operator": "eq", "value": "active"}],
    "limit": 50
  }
}

RestHeadSpec (header-based):

GET /api/departments
x-preload: employees
x-fieldfilter-status: active
x-limit: 50

Both implementations share the same core handler logic and database adapters.