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.
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.mdEach Markdown file contains self-contained SQL cells with Processing Instructions (PIs) and Attributes that define behavior and types.
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 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"
}
}Provides validation, type-checking, and IDE completion support
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;
```Spry's runtime becomes a SQL Curator — organizing, validating, and distributing your SQL knowledge across multiple languages and platforms.
Indexes all SQL cells across Markdown files
Type-checks parameters and schemas
Creates language-specific bindings
Serves via codegen, runtime parsing, or JSON
Markdown SQL Libraries → Spry Curator → TypeScript, Python, Rust, Go
instructions and appendix regions.
attrs (the type contract).
--inject flag.
These can be merged into target cells using prepend, append, or both modes.
import that materialize content from local file globs or remote URLs at runtime, expanding into new virtual `CodeCell`s.
${...} are evaluated as a TypeScript template literal, allowing dynamic, type-safe code logic within the SQL content.
route attribute are compiled into a comprehensive, hierarchical navigation asset, forest.auto.json.
Source<P> structure captures source code provenance (file, line, column) for every element, ensuring full traceability and auditability.
Generates per-language clients with type definitions and IDE support.
Each language parses Markdown directly and extracts SQL cells dynamically.
Compiles SQL libraries into a portable JSON manifest for remote consumption.
One Markdown SQL cell generates type-safe code for any language.
Here's the same query in TypeScript and Python:
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;
}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 NoneSQL lives next to documentation in Markdown
Same types, different runtimes, zero duplication
Direct SQL control without magic or abstractions
Every change tracked, reviewed, and auditable
SQL authors don't need TypeScript/Python knowledge
SQL definitions are provable and traceable
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
Shared SQL library across polyglot services
Type-safe database access layers for REST/GraphQL
SQL authors → Developer consumers without friction
Same queries in web (TS) and scripts (Python)
Versioned SQL with inline documentation
Consistent interfaces for test fixtures
SQL-backed REST endpoints with OpenAPI
Rapid development with reusable queries
Organize queries by domain:
user.*,
invoice.*
--tx join|new|none
for precise transaction behavior
--cache 5m
for automatic query result caching
PostgreSQL, SQLite, MySQL hints with
--driver
Parameter checking and type validation at execution time
All executions logged and traceable for debugging
These features are under consideration. We'd love your feedback and contributions!
Should SQL libraries support
--import user/*
for composition?
Should Markdown include reusable param definitions across queries?
Should runtime parsers expose spry.sql.load('user') for dynamic loading?
What metaphor best captures Spry's role in organizing SQL knowledge?
# user.md
```sql [--module user --name list_active --mode many --lang ts]
select id, email, created_at
from app_user
where active = true;
```spry generate --lang ts user.md > user.tsimport { listActive } from './user';
const users = await listActive(db);
console.log(users); // Fully typed!