Skip to content

Commit 765fe16

Browse files
committed
tests and examples
1 parent 7efbe17 commit 765fe16

File tree

22 files changed

+1393
-429
lines changed

22 files changed

+1393
-429
lines changed

example/.yarn/install-state.gz

8.39 KB
Binary file not shown.

example/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "sqlc-typescript-example",
3+
"type": "module",
4+
"packageManager": "yarn@4.6.0",
5+
"dependencies": {
6+
"pg": "^8.13.1"
7+
},
8+
"devDependencies": {
9+
"@types/pg": "^8.11.11"
10+
}
11+
}
File renamed without changes.

tests/sqlc.json renamed to example/sqlc.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
"include": "src/**/*.ts",
33
"output": "src/sqlc.ts",
44
"schema": "schema.sql",
5+
"tmp_dir": ".sqlc",
56
"clear_tmp": true,
7+
"types": {
8+
"timestamp": "Date"
9+
},
610
"columns": {
711
"customer.customer_id": "UUID"
812
},
9-
"imports": ["import { UUID } from '../types'"]
13+
"imports": ["import type { UUID } from './types.ts'"]
1014
}

example/src/index.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { sqlc, type InferParam, type InferRow } from './sqlc.ts';
2+
import { Pool, type PoolClient } from 'pg';
3+
import type { UUID } from './types.ts';
4+
5+
export class CustomerService {
6+
private pool = new Pool();
7+
8+
private with_client = async <T>(fn: (client: PoolClient) => Promise<T>) => {
9+
const client = await this.pool.connect();
10+
11+
try {
12+
return await fn(client);
13+
} finally {
14+
client.release();
15+
}
16+
};
17+
18+
public get_all = async () => {
19+
return this.with_client((client) => {
20+
return sqlc(/* sql */ `
21+
SELECT
22+
customer_id,
23+
store_id
24+
FROM customer
25+
`).exec(client);
26+
});
27+
};
28+
29+
public get_by_id = async (customer_id: string) => {
30+
return this.with_client((client) => {
31+
// comment is not necessary, but it's nice to have
32+
// to have syntax highlighting and formatting
33+
return sqlc(`
34+
SELECT
35+
customer_id,
36+
store_id
37+
FROM customer
38+
WHERE customer_id = @customer_id
39+
`).exec(client, {
40+
customer_id: customer_id as UUID,
41+
});
42+
});
43+
};
44+
45+
public complex_query = async () => {
46+
const query = sqlc(/* sql */ `
47+
WITH customer_spending AS (
48+
SELECT
49+
c.customer_id,
50+
c.first_name,
51+
c.last_name,
52+
COUNT(r.rental_id) as total_rentals,
53+
SUM(p.amount) as total_spent,
54+
AVG(p.amount) as avg_payment
55+
FROM customer c
56+
JOIN rental r ON c.customer_id = r.customer_id
57+
JOIN payment p ON r.rental_id = p.rental_id
58+
GROUP BY c.customer_id, c.first_name, c.last_name
59+
),
60+
customer_categories AS (
61+
SELECT DISTINCT
62+
c.customer_id,
63+
STRING_AGG(cat.name, ', ') OVER (PARTITION BY c.customer_id) as favorite_categories
64+
FROM customer c
65+
JOIN rental r ON c.customer_id = r.customer_id
66+
JOIN inventory i ON r.inventory_id = i.inventory_id
67+
JOIN film_category fc ON i.film_id = fc.film_id
68+
JOIN category cat ON fc.category_id = cat.category_id
69+
)
70+
SELECT
71+
cs.*,
72+
cc.favorite_categories,
73+
RANK() OVER (ORDER BY cs.total_spent DESC) as spending_rank,
74+
NTILE(4) OVER (ORDER BY cs.total_rentals) as rental_quartile,
75+
ROUND(cs.total_spent / SUM(cs.total_spent) OVER () * 100, 2) as percentage_of_total_revenue
76+
FROM customer_spending cs
77+
JOIN customer_categories cc ON cs.customer_id = cc.customer_id
78+
WHERE cs.total_rentals > 5
79+
ORDER BY spending_rank
80+
`);
81+
82+
// you can get the types from the query
83+
type QueryType = InferRow<typeof query>;
84+
type QueryParam = InferParam<typeof query>;
85+
86+
return this.with_client((client) => query.exec(client));
87+
};
88+
}

example/src/sqlc.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// This file was generated by sqlc-typescript
2+
// Do not modify this file by hand
3+
4+
import type { UUID } from './types.ts';
5+
6+
type Json = JsonPrimitive | Json[] | { [key: string]: Json };
7+
type JsonPrimitive = string | number | boolean | null;
8+
9+
type QueryClient = {
10+
query: (
11+
query: string,
12+
params: unknown[],
13+
) => Promise<{
14+
rows: Array<Record<string, unknown>>;
15+
}>;
16+
};
17+
18+
type Override<TSpec, TRow> = Partial<{
19+
[K in keyof TSpec]: K extends keyof TRow ? TSpec[K] : never;
20+
}>;
21+
22+
type ApplyOverride<TSpec, TRow> = {
23+
[K in keyof TRow]: K extends keyof TSpec ? TSpec[K] : TRow[K];
24+
};
25+
26+
type ExecFn<TRow, TParam> = [TParam] extends [never]
27+
? <TSpec extends Override<TSpec, TRow>>(client: QueryClient) => Promise<Array<ApplyOverride<TSpec, TRow>>>
28+
: <TSpec extends Override<TSpec, TRow>>(
29+
client: QueryClient,
30+
params: TParam & Record<string, unknown>,
31+
) => Promise<Array<ApplyOverride<TSpec, TRow>>>;
32+
33+
type SqlType<T, TDbType, TDbSource> = T & {
34+
/**
35+
* @deprecated this property does not exist in runtime, it is used for type-checking, do not use it in runtime!
36+
*/
37+
__dbtype: TDbType;
38+
39+
/**
40+
* @deprecated this property does not exist in runtime, it is used for type-checking, do not use it in runtime!
41+
*/
42+
__dbsource: TDbSource;
43+
};
44+
45+
class Query<TRow, TParam> {
46+
public query;
47+
public params;
48+
49+
public constructor(query: string, params: string[]) {
50+
this.query = query;
51+
this.params = params;
52+
}
53+
54+
public exec = (async (client, params) => {
55+
const { rows } = await client.query(
56+
this.query,
57+
this.params.map((param) => params[param]),
58+
);
59+
60+
return rows;
61+
}) as ExecFn<TRow, TParam>;
62+
}
63+
64+
export type InferRow<T> = T extends Query<infer R, any> ? R : never;
65+
export type InferParam<T> = T extends Query<any, infer P> ? P : never;
66+
67+
type Queries = typeof queries;
68+
69+
const queries = {
70+
[`
71+
SELECT
72+
customer_id,
73+
store_id
74+
FROM customer
75+
`]: new Query<
76+
{ customer_id: SqlType<UUID, 'int4', 'customer.customer_id'>; store_id: SqlType<number, 'int4', 'customer.store_id'> },
77+
never
78+
>(
79+
`SELECT
80+
customer_id,
81+
store_id
82+
FROM customer`,
83+
[],
84+
),
85+
[`
86+
SELECT
87+
customer_id,
88+
store_id
89+
FROM customer
90+
WHERE customer_id = @customer_id
91+
`]: new Query<
92+
{ customer_id: SqlType<UUID, 'int4', 'customer.customer_id'>; store_id: SqlType<number, 'int4', 'customer.store_id'> },
93+
{ customer_id: UUID }
94+
>(
95+
`SELECT
96+
customer_id,
97+
store_id
98+
FROM customer
99+
WHERE customer_id = $1`,
100+
['customer_id'],
101+
),
102+
[`
103+
WITH customer_spending AS (
104+
SELECT
105+
c.customer_id,
106+
c.first_name,
107+
c.last_name,
108+
COUNT(r.rental_id) as total_rentals,
109+
SUM(p.amount) as total_spent,
110+
AVG(p.amount) as avg_payment
111+
FROM customer c
112+
JOIN rental r ON c.customer_id = r.customer_id
113+
JOIN payment p ON r.rental_id = p.rental_id
114+
GROUP BY c.customer_id, c.first_name, c.last_name
115+
),
116+
customer_categories AS (
117+
SELECT DISTINCT
118+
c.customer_id,
119+
STRING_AGG(cat.name, ', ') OVER (PARTITION BY c.customer_id) as favorite_categories
120+
FROM customer c
121+
JOIN rental r ON c.customer_id = r.customer_id
122+
JOIN inventory i ON r.inventory_id = i.inventory_id
123+
JOIN film_category fc ON i.film_id = fc.film_id
124+
JOIN category cat ON fc.category_id = cat.category_id
125+
)
126+
SELECT
127+
cs.*,
128+
cc.favorite_categories,
129+
RANK() OVER (ORDER BY cs.total_spent DESC) as spending_rank,
130+
NTILE(4) OVER (ORDER BY cs.total_rentals) as rental_quartile,
131+
ROUND(cs.total_spent / SUM(cs.total_spent) OVER () * 100, 2) as percentage_of_total_revenue
132+
FROM customer_spending cs
133+
JOIN customer_categories cc ON cs.customer_id = cc.customer_id
134+
WHERE cs.total_rentals > 5
135+
ORDER BY spending_rank
136+
`]: new Query<
137+
{
138+
customer_id: SqlType<number, 'int4', 'customer_spending.customer_id'>;
139+
first_name: SqlType<string, 'text', 'customer_spending.first_name'>;
140+
last_name: SqlType<string, 'text', 'customer_spending.last_name'>;
141+
total_rentals: SqlType<number, 'bigint', 'customer_spending.total_rentals'>;
142+
total_spent: SqlType<number, 'bigint', 'customer_spending.total_spent'>;
143+
avg_payment: SqlType<unknown, 'double precision', 'customer_spending.avg_payment'>;
144+
favorite_categories: SqlType<Buffer, 'bytea', 'customer_categories.favorite_categories'>;
145+
spending_rank: SqlType<number, 'bigint', never>;
146+
rental_quartile: SqlType<number, 'integer', never>;
147+
percentage_of_total_revenue: SqlType<number, 'numeric', never>;
148+
},
149+
never
150+
>(
151+
`WITH customer_spending AS (
152+
SELECT
153+
c.customer_id,
154+
c.first_name,
155+
c.last_name,
156+
COUNT(r.rental_id) as total_rentals,
157+
SUM(p.amount) as total_spent,
158+
AVG(p.amount) as avg_payment
159+
FROM customer c
160+
JOIN rental r ON c.customer_id = r.customer_id
161+
JOIN payment p ON r.rental_id = p.rental_id
162+
GROUP BY c.customer_id, c.first_name, c.last_name
163+
),
164+
customer_categories AS (
165+
SELECT DISTINCT
166+
c.customer_id,
167+
STRING_AGG(cat.name, ', ') OVER (PARTITION BY c.customer_id) as favorite_categories
168+
FROM customer c
169+
JOIN rental r ON c.customer_id = r.customer_id
170+
JOIN inventory i ON r.inventory_id = i.inventory_id
171+
JOIN film_category fc ON i.film_id = fc.film_id
172+
JOIN category cat ON fc.category_id = cat.category_id
173+
)
174+
SELECT
175+
cs.customer_id, cs.first_name, cs.last_name, cs.total_rentals, cs.total_spent, cs.avg_payment,
176+
cc.favorite_categories,
177+
RANK() OVER (ORDER BY cs.total_spent DESC) as spending_rank,
178+
NTILE(4) OVER (ORDER BY cs.total_rentals) as rental_quartile,
179+
ROUND(cs.total_spent / SUM(cs.total_spent) OVER () * 100, 2) as percentage_of_total_revenue
180+
FROM customer_spending cs
181+
JOIN customer_categories cc ON cs.customer_id = cc.customer_id
182+
WHERE cs.total_rentals > 5
183+
ORDER BY spending_rank`,
184+
[],
185+
),
186+
};
187+
188+
export type mpaa_rating = 'G' | 'PG' | 'PG-13' | 'R' | 'NC-17';
189+
190+
export const sqlc = <T extends keyof Queries>(query: T) => queries[query];

example/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type UUID = string & { __uuid: never };

example/tsconfig.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"compilerOptions": {
3+
"rootDir": "${configDir}",
4+
"lib": ["ESNext"],
5+
"module": "NodeNext",
6+
"moduleResolution": "NodeNext",
7+
"baseUrl": ".",
8+
9+
"forceConsistentCasingInFileNames": true,
10+
"strict": true,
11+
"noUncheckedIndexedAccess": true,
12+
"noUnusedLocals": true,
13+
"noUnusedParameters": true,
14+
"noPropertyAccessFromIndexSignature": true,
15+
"noImplicitOverride": true,
16+
"noFallthroughCasesInSwitch": true,
17+
18+
"esModuleInterop": true,
19+
"skipLibCheck": true,
20+
"target": "ESNext",
21+
"allowJs": true,
22+
"resolveJsonModule": true,
23+
"moduleDetection": "force",
24+
"isolatedModules": true,
25+
"verbatimModuleSyntax": true,
26+
"allowImportingTsExtensions": true,
27+
"rewriteRelativeImportExtensions": true,
28+
"noEmit": true
29+
},
30+
"include": ["${configDir}/src"]
31+
}

0 commit comments

Comments
 (0)