Skip to content

Commit 11e4222

Browse files
committed
Better example and docs
1 parent 18051bc commit 11e4222

13 files changed

Lines changed: 837 additions & 35 deletions

File tree

README.md

Lines changed: 176 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
11
# sqlf
22

3-
MVP for a SQL-first TypeScript code generator targeting `effect` + `@effect/sql`.
3+
`sqlf` is an MVP for a SQL-first TypeScript code generator targeting `effect` + `@effect/sql`.
4+
5+
It is inspired by `sqlc`, but uses:
6+
7+
- TypeScript / JS config via `defineConfig()`
8+
- database introspection for result / parameter typing
9+
- generated `Schema` + `SqlSchema.*` helpers for Effect-native usage
410

511
## Workspace
612

713
- `packages/core` — config loading, SQL parsing, Postgres analysis, code generation
8-
- `packages/cli``sqlf generate` command
9-
- `examples/basic` — example config and SQL queries
14+
- `packages/cli``sqlf generate`
15+
- `examples/basic` — self-contained Postgres + generated CRUD example + Effect HTTP API
16+
17+
## MVP scope
18+
19+
Current MVP supports:
20+
21+
- JS / TS config via `defineConfig()`
22+
- Postgres-only
23+
- sqlc-style query annotations: `:one`, `:maybeOne`, `:many`, `:exec`
24+
- named params via `@param`
25+
- Postgres-backed result type inference
26+
- generated Effect SQL wrappers via `SqlSchema.single/findOne/findAll/void`
1027

1128
## Commands
1229

@@ -16,46 +33,176 @@ vp lint
1633
vp run -r check
1734
vp run -r test
1835
vp run -r build
36+
```
37+
38+
## Quick start
39+
40+
Generate the example output:
41+
42+
```bash
43+
vp run -r build
1944
vp run @sqlf/example-basic#generate
2045
```
2146

22-
## Generated output
47+
Start the example API:
48+
49+
```bash
50+
vp run @sqlf/example-basic#start
51+
```
52+
53+
Reset the example database if you want to re-run `schema.sql` from scratch:
2354

24-
The generated module now emits:
55+
```bash
56+
cd examples/basic
57+
pnpm run db:stop
58+
pnpm run db:start
59+
```
2560

26-
- `*ParamsSchema` and `*ResultSchema`
27-
- `*Params` and `*Result` type aliases via `Schema.Schema.Type<...>`
28-
- raw `*Sql` constants
29-
- query docs with source file + SQL
30-
- `SqlSchema.single/findOne/findAll/void` wrappers
61+
## Example: generated CRUD module
3162

32-
## Integration testing
63+
The basic example lives in `examples/basic` and is fully local:
3364

34-
`packages/core/tests/postgres.integration.test.ts` runs against:
65+
- `examples/basic/compose.yaml` starts Postgres on `127.0.0.1:54329`
66+
- `examples/basic/schema.sql` creates and seeds the `users` table
67+
- `examples/basic/sql/queries.sql` defines CRUD queries
68+
- `examples/basic/generated/index.ts` is committed generated output
69+
- `examples/basic/src/httpApi.ts` shows how to use generated queries inside an Effect `HttpApi`
3570

36-
- `SQLF_TEST_DATABASE_URL`, if provided, or
37-
- an ephemeral Docker `postgres:16-alpine` container
71+
The example SQL includes:
72+
73+
```sql
74+
-- name: CreateUser :one
75+
INSERT INTO users (id, email, created_at)
76+
VALUES (@id::uuid, @email::text, now())
77+
RETURNING id, email, created_at;
78+
79+
-- name: GetUser :one
80+
SELECT id, email, created_at
81+
FROM users
82+
WHERE id = @id::uuid;
83+
84+
-- name: ListUsers :many
85+
SELECT id, email, created_at
86+
FROM users
87+
ORDER BY created_at DESC;
88+
89+
-- name: UpdateUser :one
90+
UPDATE users
91+
SET email = @email::text
92+
WHERE id = @id::uuid
93+
RETURNING id, email, created_at;
94+
95+
-- name: DeleteUser :exec
96+
DELETE FROM users
97+
WHERE id = @id::uuid;
98+
```
99+
100+
## Example: direct generated usage
38101

39-
## Example
102+
You can call the generated functions directly by providing a SQL client layer:
40103

41-
`examples/basic` now includes a Docker Compose Postgres setup.
104+
```ts
105+
import { PgClient } from "@effect/sql-pg";
106+
import { Effect, Redacted } from "effect";
107+
import { createUser, listUsers } from "./generated/index.ts";
108+
109+
const program = Effect.gen(function* () {
110+
const created = yield* createUser({
111+
id: "33333333-3333-3333-3333-333333333333",
112+
email: "grace@example.com",
113+
});
114+
115+
const users = yield* listUsers({});
116+
117+
return { created, users };
118+
});
119+
120+
const runnable = program.pipe(
121+
Effect.provide(
122+
PgClient.layer({
123+
url: Redacted.make("postgres://postgres:postgres@127.0.0.1:54329/postgres"),
124+
}),
125+
),
126+
);
127+
```
128+
129+
## Example: generated output shape
130+
131+
Generated modules export:
132+
133+
- `*ParamsSchema`
134+
- `*ResultSchema`
135+
- `*Params` / `*Result` type aliases
136+
- raw `*Sql` strings
137+
- `SqlSchema.*` wrappers
138+
139+
Example output from `examples/basic/generated/index.ts`:
140+
141+
```ts
142+
export const CreateUserResultSchema = Schema.Struct({
143+
id: Schema.UUID,
144+
email: Schema.String,
145+
created_at: Schema.DateFromSelf,
146+
});
147+
148+
export const createUser = SqlSchema.single({
149+
Request: CreateUserParamsSchema,
150+
Result: CreateUserResultSchema,
151+
execute: (request) =>
152+
Effect.flatMap(SqlClient.SqlClient, (sql) =>
153+
sql.unsafe(createUserSql, [request.id, request.email]),
154+
),
155+
});
156+
```
157+
158+
## Example: Effect HTTP API
159+
160+
`examples/basic/src/httpApi.ts` wires the generated module into an Effect `HttpApi`.
161+
162+
It exposes:
163+
164+
- `POST /users`
165+
- `GET /users`
166+
- `GET /users/:id`
167+
- `PUT /users/:id`
168+
- `DELETE /users/:id`
169+
170+
Start it:
42171

43172
```bash
44-
vp run -r build
45-
vp run @sqlf/example-basic#generate
173+
vp run @sqlf/example-basic#start
46174
```
47175

48-
That will:
176+
Then try it:
49177

50-
- start Postgres via `examples/basic/compose.yaml`
51-
- initialize the `users` table from `examples/basic/schema.sql`
52-
- generate `examples/basic/generated/index.ts`
178+
```bash
179+
curl http://127.0.0.1:3000/users
53180

54-
## MVP scope
181+
curl http://127.0.0.1:3000/users/11111111-1111-1111-1111-111111111111
55182

56-
- JS/TS config via `defineConfig()`
57-
- Postgres-only
58-
- Query annotations inspired by sqlc (`:one`, `:maybeOne`, `:many`, `:exec`)
59-
- Named params via `@param`
60-
- Result type inference through lightweight Postgres analysis
61-
- Effect SQL code generation
183+
curl -X POST http://127.0.0.1:3000/users \
184+
-H 'content-type: application/json' \
185+
-d '{"email":"grace@example.com"}'
186+
187+
curl -X PUT http://127.0.0.1:3000/users/11111111-1111-1111-1111-111111111111 \
188+
-H 'content-type: application/json' \
189+
-d '{"email":"ada+updated@example.com"}'
190+
191+
curl -X DELETE http://127.0.0.1:3000/users/11111111-1111-1111-1111-111111111111
192+
```
193+
194+
## Generated output notes
195+
196+
The generator currently emits:
197+
198+
- query doc comments with source file + SQL
199+
- Effect schemas for params and results
200+
- raw SQL strings
201+
- executable wrappers built on `SqlSchema`
202+
203+
## Integration testing
204+
205+
`packages/core/tests/postgres.integration.test.ts` runs against either:
206+
207+
- `SQLF_TEST_DATABASE_URL`, or
208+
- an ephemeral Docker `postgres:16-alpine` container

examples/basic/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules
2-
generated
2+
generated/*
3+
!generated/index.ts
34
*.log
45
.DS_Store

examples/basic/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# @sqlf/example-basic
2+
3+
Self-contained example for `sqlf`.
4+
5+
## What it contains
6+
7+
- Docker Compose Postgres database
8+
- `schema.sql` to create + seed `users`
9+
- SQL CRUD queries in `sql/queries.sql`
10+
- committed generated output in `generated/index.ts`
11+
- Effect `HttpApi` in `src/httpApi.ts`
12+
- runnable Node server in `src/server.ts`
13+
14+
## Commands
15+
16+
```bash
17+
pnpm run db:start
18+
pnpm run generate
19+
pnpm run start
20+
```
21+
22+
Reset the database volume:
23+
24+
```bash
25+
pnpm run db:stop
26+
pnpm run db:start
27+
```
28+
29+
## API
30+
31+
- `POST /users`
32+
- `GET /users`
33+
- `GET /users/:id`
34+
- `PUT /users/:id`
35+
- `DELETE /users/:id`
36+
37+
## Sample requests
38+
39+
```bash
40+
curl http://127.0.0.1:3000/users
41+
42+
curl http://127.0.0.1:3000/users/11111111-1111-1111-1111-111111111111
43+
44+
curl -X POST http://127.0.0.1:3000/users \
45+
-H 'content-type: application/json' \
46+
-d '{"email":"grace@example.com"}'
47+
```

0 commit comments

Comments
 (0)