Skip to content

CI codecov Go Report Card License: MIT A YAML-driven HTTP server that maps endpoints to SQL queries. Define your API in YAML, run the server, and get a working REST API.

License

Notifications You must be signed in to change notification settings

mpyw/sql-http-proxy

Repository files navigation

sql-http-proxy

Test codecov Go Report Card License: MIT

A YAML-driven HTTP server that maps endpoints to SQL queries. Define your API in YAML, run the server, and get a working REST API.

Note

This project was written by AI (Claude Code).

Installation

Homebrew (macOS / Linux)

brew install mpyw/tap/sql-http-proxy

Scoop (Windows)

scoop bucket add mpyw https://github.com/mpyw/scoop-bucket
scoop install sql-http-proxy

Debian / Ubuntu

export VERSION=1.0.0
export ARCH=amd64  # or arm64
curl -LO "https://github.com/mpyw/sql-http-proxy/releases/download/v${VERSION}/sql-http-proxy_${VERSION}-1_${ARCH}.deb"
sudo dpkg -i "sql-http-proxy_${VERSION}-1_${ARCH}.deb"

RHEL / Fedora

export VERSION=1.0.0
export ARCH=x86_64  # or aarch64
curl -LO "https://github.com/mpyw/sql-http-proxy/releases/download/v${VERSION}/sql-http-proxy-${VERSION}-1.${ARCH}.rpm"
sudo rpm -i "sql-http-proxy-${VERSION}-1.${ARCH}.rpm"

Binary Download (Linux / macOS)

# Set version and architecture
export VERSION=1.0.0
export ARCH=amd64  # or arm64

# Linux
curl -L "https://github.com/mpyw/sql-http-proxy/releases/download/v${VERSION}/sql-http-proxy_${VERSION}_linux_${ARCH}.tar.gz" | tar xz
sudo mv sql-http-proxy /usr/local/bin/

# macOS
curl -L "https://github.com/mpyw/sql-http-proxy/releases/download/v${VERSION}/sql-http-proxy_${VERSION}_darwin_${ARCH}.tar.gz" | tar xz
sudo mv sql-http-proxy /usr/local/bin/

Or download manually from GitHub Releases.

Go Install

go install github.com/mpyw/sql-http-proxy/cmd/sql-http-proxy@latest

Build Tags

Use build tags for smaller binaries:

go install -tags postgres github.com/mpyw/sql-http-proxy/cmd/sql-http-proxy@latest  # PostgreSQL only
go install -tags sqlite github.com/mpyw/sql-http-proxy/cmd/sql-http-proxy@latest    # SQLite only
go install -tags mock github.com/mpyw/sql-http-proxy/cmd/sql-http-proxy@latest      # Mock only (no database)

Quick Start

1. Create Configuration

Create .sql-http-proxy.yaml:

database:
  dsn: postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST:-localhost}:${DB_PORT:-5432}/mydb

queries:
  - type: many
    path: /users
    sql: SELECT * FROM users

  - type: one
    path: /users/{id:*uuid_v7*}
    sql: SELECT * FROM users WHERE id = :id

2. Start Server

sql-http-proxy -l :8080

3. Make Requests

curl http://localhost:8080/users                                        # List all users
curl http://localhost:8080/users/019411a5-3d7f-7000-8000-000000000001   # Get user by ID (UUIDv7)

Mock Mode

Run without a database using mock data. Use mock instead of sql:

queries:
  # Array source for type: many
  - type: many
    path: /users
    mock:
      array:
        - { id: 1, name: Alice }
        - { id: 2, name: Bob }

  # JavaScript for dynamic data
  - type: one
    path: /user
    mock:
      object_js: |
        if (input.id === '404') return null;
        return { id: parseInt(input.id), name: 'User ' + input.id };

  # Array with filter for type: one
  - type: one
    path: /user-by-email
    mock:
      array:
        - { id: 1, name: Alice, email: alice@example.com }
        - { id: 2, name: Bob, email: bob@example.com }
      filter: return row.email === input.email

Returning null or undefined from mock JS results in 404 Not Found for type: one.

Configuration Overview

See SCHEMA.md for complete reference and sql-http-proxy.example.yaml for examples.

Database & HTTP Configuration

Database connection with ${VAR} environment variable expansion:

database:
  dsn: postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST:-localhost}:${DB_PORT:-5432}/mydb

http:
  cors: true  # or: { allowed_origins: [...], allow_credentials: true, max_age: 86400 }

Queries & Mutations

Each endpoint uses either sql OR mock, not both:

queries:
  - type: one|many
    path: /endpoint
    sql: SELECT * FROM table WHERE id = :id  # OR mock: { ... }
    transform: { ... }

mutations:
  - type: one|many|none
    method: POST
    path: /endpoint
    sql: INSERT INTO table (...) RETURNING *  # OR mock: { ... }
    transform: { ... }

Path Parameters

Use {param} syntax in paths to capture URL segments (chi router syntax):

queries:
  - type: one
    path: /users/{id:*uuid_v7*}
    sql: SELECT * FROM users WHERE id = :id

  - type: many
    path: /users/{user_id:*uuid_v7*}/posts
    sql: SELECT * FROM posts WHERE user_id = :user_id

mutations:
  - type: one
    method: PUT
    path: /users/{id:*uuid_v7*}
    sql: UPDATE users SET name = :name WHERE id = :id RETURNING *

Path parameters take priority over query string and body parameters.

Regex shorthands for common validation patterns:

Shorthand Description
*uuid* Any UUID (lowercase)
*uuid_v4* UUIDv4 only
*uuid_v7* UUIDv7 only

Custom regex (returns 404 if not matched):

# Numeric ID only
path: /posts/{id:[0-9]+}

# Slug format
path: /articles/{slug:[a-z0-9-]+}

See SCHEMA.md for full regex syntax.

Transform Pipeline

transform:
  pre: |
    # Validate input, modify SQL
    return { id: parseInt(input.id) };

  post: |
    # Transform output
    return { ...output, formatted: true };

Global Helpers

Reusable JavaScript functions for all transforms:

global_helpers: |
  function requireInt(val, name) {
    const n = parseInt(val);
    if (isNaN(n)) throw { status: 400, body: { error: name + ' required' } };
    return n;
  }

queries:
  - type: one
    path: /user
    sql: SELECT * FROM users WHERE id = :id
    transform:
      pre: |
        return { id: requireInt(input.id, 'id') };

Mock Sources

Mock sources are divided into two categories based on the endpoint type.

Object Sources (for type: one)

Return a single object directly:

mock:
  object: { id: 1, name: Alice }          # Inline YAML object
  # or
  object_json: '{"id": 1, "name": "Alice"}'  # JSON string
  # or
  object_json_file: ./data/user.json      # External JSON file
  # or
  object_js: |                            # JavaScript (dynamic)
    return { id: parseInt(input.id), name: 'User ' + input.id };

Array Sources (for type: many)

Return multiple rows:

mock:
  array:                                  # Inline YAML array
    - { id: 1, name: Alice }
    - { id: 2, name: Bob }
  # or
  array_json: '[{"id": 1}, {"id": 2}]'   # JSON string
  array_json_file: ./data/users.json     # External JSON file
  # or
  csv: |                                  # CSV with header
    id,name
    1,Alice
    2,Bob
  csv_file: ./data/users.csv             # External CSV file
  # or
  jsonl: |                                # JSON Lines
    {"id": 1, "name": "Alice"}
    {"id": 2, "name": "Bob"}
  jsonl_file: ./data/users.jsonl         # External JSONL file
  # or
  array_js: |                             # JavaScript (dynamic)
    return [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

Array Sources with Filter (for type: one)

Use filter to select a single row from array data:

- type: one
  path: /user
  mock:
    array:
      - { id: 1, name: Alice }
      - { id: 2, name: Bob }
    filter: return row.id === parseInt(input.id)

The filter receives row and input, returns true to include. First matching row is returned (404 if none match).

See SCHEMA.md for complete mock reference.

License

MIT

About

CI codecov Go Report Card License: MIT A YAML-driven HTTP server that maps endpoints to SQL queries. Define your API in YAML, run the server, and get a working REST API.

Resources

License

Stars

Watchers

Forks

Languages