Spry SQL Libraries

SQL Libraries in Markdown —Type-Safe, Polyglot, ORM-Free

Define reusable SQL as executable Markdown cells.
Generate type-safe bindings for TypeScript, Python, Rust, and more.
Spry acts as your SQL Curator — organizing, validating, and distributing SQL logic across your entire stack.

What Are SQL Libraries?

A structured, reusable collection of SQL queries organized in Markdown

A SQL Library is a Markdown collection of SQL cells — each cell defines a reusable, strongly typed SQL function. Spry acts as the SQL Curator, indexing, validating, and generating type-safe bindings for these cells in any supported language.

Example SQL Library Structure:

sql/
 ├── user.md
 ├── invoice.md
 ├── analytics/
 │     ├── sessions.md
 │     └── cohorts.md
 └── index.md
bash

Each Markdown file contains self-contained SQL cells with Processing Instructions (PIs) and Attributes that define behavior and types.

Markdown Syntax: PIs + Attributes

Processing Instructions [PI]

Command-line-like flags that tell Spry how to process the SQL cell.

--module Library namespace
--name Query/function name
--mode one | many | scalar | exec
--lang Target languages (ts,py,rust)
--tx Transaction behavior
--driver SQL dialect (postgres, sqlite)
--cache Cache TTL (5m, 1h)
--doc Human documentation

Attributes {attrs}

JSON5 schema defining the type system: input parameters and return types.

{
  "params": {
    "user_id": {
      "type": "uuid",
      "required": true,
      "doc": "Primary key"
    }
  },
  "returns": {
    "id": "uuid",
    "email": "text",
    "created_at": "timestamptz"
  }
}
bash

Provides validation, type-checking, and IDE completion support

Complete Working Example

A SQL Library cell with both Processing Instructions and Attributes:

```sql [--module user --name get_by_id --mode one --lang ts,py --tx join]
{
  "params": {
    "user_id": { "type": "uuid", "required": true, "doc": "Primary key" }
  },
  "returns": {
    "id": "uuid",
    "email": "text",
    "created_at": "timestamptz"
  }
}
select id, email, created_at
from app_user
where id = :user_id;
```
bash

Spry as Your SQL Curator

Spry's runtime becomes a SQL Curator — organizing, validating, and distributing your SQL knowledge across multiple languages and platforms.

Catalogs

Indexes all SQL cells across Markdown files

Validates

Type-checks parameters and schemas

Generates

Creates language-specific bindings

Distributes

Serves via codegen, runtime parsing, or JSON

Markdown SQL LibrariesSpry CuratorTypeScript, Python, Rust, Go

Architectural Deep Dive: The Spry Engine

Core Models & Transformation Pipeline

  • Notebook vs. Playbook: The system first parses into the raw Notebook model. It is then semantically enriched into a Playbook, which organizes content into logical instructions and appendix regions.
  • Cell Anatomy: Every content block is a Cell (`CodeCell` or `MarkdownCell`). A `CodeCell` includes the code content, `pi` (Processing Instructions), and JSON5-parsed attrs (the type contract).
  • Task Directive Inspectors (TDIs): Low-level functions that process code fences and determine the nature of the task, such as file generation (`sqlPageFileLangCellTDI`).

Modularity & Dynamic Content Injection

  • Code Partials: Reusable code blocks configured with the --inject flag. These can be merged into target cells using prepend, append, or both modes.
  • Virtual Cells: Code blocks with language import that materialize content from local file globs or remote URLs at runtime, expanding into new virtual `CodeCell`s.
  • Type-Safe Interpolation: SQL blocks containing ${...} are evaluated as a TypeScript template literal, allowing dynamic, type-safe code logic within the SQL content.

Developer Utilities & Auditing

  • CQL-mini: A minimal CodeCell Query Language (DSL) used to define type-safe filter functions for selecting specific `CodeCell` objects based on flags, language, or attributes.
  • Reflection (`reflect.ts`): Provides deep, recursive runtime introspection to analyze the type and structure of any variable, returning detailed `TypeInfo`.
  • Markdown Audit Log (`mdbus.ts`): A specialized event bus that translates low-level shell events (spawn, exit code, cleanup) into a human-readable, rich-formatted Markdown audit log.

Output, Routing & Data Provenance

  • Fluent Output (`MarkdownDoc`): A standalone API (`fluent-doc.ts`) for programmatically building new, well-formed Markdown documents (e.g., generating documentation or TOCs).
  • Routing & Navigation: SQL blocks with an optional route attribute are compiled into a comprehensive, hierarchical navigation asset, forest.auto.json.
  • Provenance Tracking: The core Source<P> structure captures source code provenance (file, line, column) for every element, ensuring full traceability and auditability.

Three Ways to Consume SQL Libraries

Type-Safe Codegen

Generates per-language clients with type definitions and IDE support.

Use Case: Production apps needing strict type safety

Runtime Parsing

Each language parses Markdown directly and extracts SQL cells dynamically.

Use Case: Rapid prototyping, scripting, notebooks

Registry Service

Compiles SQL libraries into a portable JSON manifest for remote consumption.

Use Case: Non-Spry clients, microservices

Generated Code: Same SQL, Multiple Languages

One Markdown SQL cell generates type-safe code for any language.
Here's the same query in TypeScript and Python:

TypeScript

export interface GetByIdParams { 
  user_id: string; 
}

export interface UserRecord { 
  id: string;
  email: string; 
  created_at: string; 
}

export async function getById(
  db: PgClient, 
  args: GetByIdParams
): Promise<UserRecord | null> {
  const sql = `
    select id, email, created_at 
    from app_user 
    where id = $1
  `;
  const res = await db.query(sql, [args.user_id]);
  return res.rows[0] ?? null;
}
bash

Python

from typing import Optional, TypedDict

class UserRecord(TypedDict):
    id: str
    email: str
    created_at: str

def get_by_id(
    conn, 
    user_id: str
) -> Optional[UserRecord]:
    q = """
      select id, email, created_at 
      from app_user 
      where id = %(user_id)s
    """
    with conn.cursor() as cur:
        cur.execute(q, {"user_id": user_id})
        row = cur.fetchone()
        if row:
            return dict(zip(
                [c[0] for c in cur.description], 
                row
            ))
        return None
bash

Why Teams Choose SQL Libraries

Single Source of Truth

SQL lives next to documentation in Markdown

Type Safety Across Languages

Same types, different runtimes, zero duplication

No ORM Lock-in

Direct SQL control without magic or abstractions

Git-Versioned

Every change tracked, reviewed, and auditable

Analyst-Developer Bridge

SQL authors don't need TypeScript/Python knowledge

Reproducible & Auditable

SQL definitions are provable and traceable

SQL Libraries vs Traditional Approaches

Traditional Approach

ORMs with leaky abstractions
SQL scattered across codebases
Duplicate logic in multiple languages
No type safety on raw SQL strings
Documentation separate from code

Spry SQL Libraries

Direct SQL control with type safety

Centralized, reusable SQL definitions

Single Markdown source → many languages

Generated type-safe bindings with IDE support

Documentation IS the executable code

Real-World Use Cases

Microservices

Shared SQL library across polyglot services

Data APIs

Type-safe database access layers for REST/GraphQL

Analytics Teams

SQL authors → Developer consumers without friction

Multi-Runtime Apps

Same queries in web (TS) and scripts (Python)

Database Migrations

Versioned SQL with inline documentation

Testing & Mocking

Consistent interfaces for test fixtures

API Gateways

SQL-backed REST endpoints with OpenAPI

Internal Tools

Rapid development with reusable queries

Advanced Features

Module Namespacing

Organize queries by domain: user.*, invoice.*

Transaction Control

--tx join|new|none for precise transaction behavior

Built-in Caching

--cache 5m for automatic query result caching

Dialect Support

PostgreSQL, SQLite, MySQL hints with --driver

Runtime Validation

Parameter checking and type validation at execution time

Full Observability

All executions logged and traceable for debugging

Future Roadmap & Community Input

These features are under consideration. We'd love your feedback and contributions!

? Module Imports

Should SQL libraries support --import user/* for composition?

? Shared Parameter Schemas

Should Markdown include reusable param definitions across queries?

? Lazy Loading API

Should runtime parsers expose spry.sql.load('user') for dynamic loading?

? Naming: Librarian vs Curator vs Cartographer

What metaphor best captures Spry's role in organizing SQL knowledge?

Getting Started in 3 Steps

1

Create a SQL Library File

# user.md

```sql [--module user --name list_active --mode many --lang ts]
select id, email, created_at 
from app_user 
where active = true;
```
bash
2

Generate Type-Safe Bindings

spry generate --lang ts user.md > user.ts
bash
3

Use in Your Application

import { listActive } from './user';
const users = await listActive(db);
console.log(users); // Fully typed!
bash

Build Type-Safe SQL Libraries with Spry

Start defining reusable, polyglot SQL today. One Markdown file, infinite possibilities.