Skip to content

Commit 0547fdc

Browse files
Improve documentation and update VitePress config
This commit includes the following changes: - Updated VitePress configuration to render outlines for h2 and h3 headings. - Removed references to subqueries within other queries from `docs/advanced-queries.md` as this feature is not currently supported. - Reviewed and made minor improvements to all documentation files, including: - Clarifying explanations and code examples. - Fixing typos and ensuring consistency. - Improving the structure and readability of examples, particularly in `docs/databases/do.md`.
1 parent 2783082 commit 0547fdc

File tree

9 files changed

+99
-81
lines changed

9 files changed

+99
-81
lines changed

docs/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export default defineConfig({
99
themeConfig: {
1010
// https://vitepress.dev/reference/default-theme-config
1111
logo: 'https://raw.githubusercontent.com/G4brym/workers-qb/refs/heads/main/docs/assets/logo-icon.png',
12+
outline: [2, 3],
1213
nav: [
1314
{text: 'Home', link: '/'},
1415
{text: 'Docs', link: '/introduction'}

docs/advanced-queries.md

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -87,39 +87,6 @@ const userProductCombinations = await qb.fetchAll<UserAndProduct>({
8787
console.log('User and product combinations:', userProductCombinations.results);
8888
```
8989

90-
### JOIN with Subqueries
91-
92-
You can also use subqueries as tables in your JOIN clauses.
93-
94-
```typescript
95-
import { D1QB } from 'workers-qb';
96-
97-
// ... (D1QB initialization) ...
98-
99-
type UserWithOrderCount = {
100-
userName: string;
101-
orderCount: number;
102-
};
103-
104-
const usersWithOrderCounts = await qb.fetchAll<UserWithOrderCount>({
105-
tableName: 'users',
106-
fields: ['users.name AS userName', 'orderCounts.count AS orderCount'],
107-
join: {
108-
type: 'LEFT',
109-
table: qb.select('orders') // Subquery using select builder
110-
.fields('user_id, COUNT(*) AS count')
111-
.groupBy('user_id')
112-
.getQueryAll(), // Get the Query object from SelectBuilder
113-
alias: 'orderCounts',
114-
on: 'users.id = orderCounts.user_id',
115-
},
116-
}).execute();
117-
118-
console.log('Users with order counts:', usersWithOrderCounts.results);
119-
```
120-
121-
In this example, a subquery is built using `qb.select('orders')...getQueryAll()` to calculate the order count for each user. This subquery is then joined with the `users` table.
122-
12390
## Modular Select Queries
12491

12592
`workers-qb` provides a modular `select()` builder for constructing SELECT queries in a chainable and readable manner.
@@ -179,7 +146,7 @@ console.log('Users info:', usersInfo.results);
179146

180147
The `SelectBuilder` provides methods to execute the built query and retrieve results:
181148

182-
* `.execute()`: Executes the query and returns `ArrayResult` or `OneResult` based on the query configuration (implicitly calls `fetchAll` or `fetchOne` based on limit, etc. - in this case `fetchAll`).
149+
* `.execute()`: Executes the query and returns `ArrayResult` or `OneResult` based on the nature of the constructed query (e.g., if `.limit(1)` is used, it might behave like `fetchOne`).
183150
* `.all()`: Explicitly executes as `fetchAll` and returns `ArrayResult`.
184151
* `.one()`: Explicitly executes as `fetchOne` and returns `OneResult`.
185152
* `.count()`: Executes a `COUNT(*)` query based on the current builder configuration (ignoring fields, limit, offset, orderBy) and returns `CountResult`.
@@ -258,11 +225,6 @@ import { D1QB } from 'workers-qb';
258225

259226
// ... (D1QB initialization) ...
260227

261-
const usersInRoles = await qb.fetchAll({
262-
tableName: 'users',
263-
where: qb.select('roles').fields('id').where('is_admin = ?', true).getQueryAll(), // Subquery to get admin role IDs
264-
}).execute() // Error! 'where' option should be 'whereIn' for IN clause
265-
266228
const usersInSpecificRoles = await qb.select('users')
267229
.whereIn('role_id', [1, 2, 3]) // Filter users with role_id in [1, 2, 3]
268230
.execute();
@@ -276,8 +238,6 @@ const usersInSpecificRolesMultipleColumns = await qb.select('users')
276238
console.log('Users in specific role and department combinations:', usersInSpecificRolesMultipleColumns.results);
277239
```
278240

279-
**Important:** Note the corrected example using `whereIn` method on the `SelectBuilder` for IN clauses, instead of trying to put a subquery directly into the `where` option, which is not intended for `IN` clause subqueries in this builder's API design.
280-
281241
## Group By and Having
282242

283243
### Group By Clause

docs/basic-queries.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default {
7474
Use the `createTable` method to define and create a new table. You need to specify the `tableName` and the `schema` as a string defining the table columns and their types.
7575

7676
```typescript
77-
import { D1QB } from 'workers-qb';
77+
import { D1QB, Raw } from 'workers-qb';
7878

7979
// ... (D1QB initialization) ...
8080

@@ -254,7 +254,7 @@ console.log('Insert attempted, row replaced if email exists.');
254254
For more complex conflict resolution, you can perform an UPSERT operation, updating specific columns if a conflict occurs. Use `onConflict` with an object to define the columns causing conflict, the data to update, and optional `where` conditions for the update.
255255

256256
```typescript
257-
import { D1QB } from 'workers-qb';
257+
import { D1QB, Raw } from 'workers-qb'; // Ensure Raw is imported here
258258

259259
// ... (D1QB initialization) ...
260260

@@ -413,13 +413,13 @@ import { D1QB } from 'workers-qb';
413413

414414
// ... (D1QB initialization) ...
415415

416-
type UpdatedUser = {
416+
type User = { // Assuming User type includes id, name, email
417417
id: number;
418418
name: string;
419419
email: string;
420420
};
421421

422-
const updatedUser = await qb.update<UpdatedUser>({
422+
const updatedUser = await qb.update<User>({
423423
tableName: 'users',
424424
data: {
425425
name: 'Corrected John Doe',
@@ -488,13 +488,13 @@ import { D1QB } from 'workers-qb';
488488

489489
// ... (D1QB initialization) ...
490490

491-
type DeletedUser = {
491+
type User = { // Assuming User type includes id, name, email
492492
id: number;
493493
name: string;
494494
email: string;
495495
};
496496

497-
const deletedUser = await qb.delete<DeletedUser>({
497+
const deletedUser = await qb.delete<User>({
498498
tableName: 'users',
499499
where: {
500500
conditions: 'id = ?',

docs/databases/byodb.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ export class SQLiteQB extends QueryBuilder<SQLiteResultWrapper, false> { // Sync
9999

100100
**To create a production-ready adapter for your database:**
101101

102-
1. **Choose a suitable database client library** for your target database in JavaScript/TypeScript (if one exists for Cloudflare Workers environment).
103-
2. **Study the documentation of your chosen database and its client library.**
104-
3. **Implement `execute`, `batchExecute`, and `lazyExecute` methods** in your custom QueryBuilder class, handling connection, query execution, parameter binding, error handling, and result formatting according to your database's specifics.
105-
4. **Consider adding migration support** by extending or adapting `syncMigrationsBuilder` or `asyncMigrationsBuilder` if needed.
106-
5. **Thoroughly test your custom adapter** with various query types and scenarios.
102+
1. **Choose a suitable database client library** for your target database in JavaScript/TypeScript (if one exists for the Cloudflare Workers environment).
103+
2. **Study the documentation of your chosen database and its client library** thoroughly.
104+
3. **Implement `execute`, `batchExecute` (if applicable), and `lazyExecute` (if applicable) methods** in your custom `QueryBuilder` class. This involves handling connections, query execution, parameter binding, error management, and transforming results into the `workers-qb` expected formats.
105+
4. **Consider adding migration support** by extending or adapting `syncMigrationsBuilder` or `asyncMigrationsBuilder` if your database requires schema management.
106+
5. **Thoroughly test your custom adapter** with a comprehensive suite of query types and edge cases.
107107

108108
By creating custom database adapters, you can extend the reach of `workers-qb` to support a wide variety of SQL and SQL-like databases in your Cloudflare Worker projects.

docs/databases/do.md

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,47 @@ Within your Durable Object class, you can access the `DurableObjectStorage` inst
2727
```typescript
2828
import { DOQB } from 'workers-qb';
2929

30+
// Define Env if it's used for configuration, otherwise it might not be needed for basic DOQB
31+
export interface Env {
32+
// Example: You might have environment variables here for other purposes
33+
}
34+
3035
export class MyDurableObject extends DurableObject {
36+
#qb: DOQB; // Make qb a class member
37+
3138
constructor(state: DurableObjectState, env: Env) {
3239
super(state, env);
33-
// ... other initialization ...
40+
this.#qb = new DOQB(this.storage.sql); // Initialize DOQB with DurableObjectStorage
41+
42+
// It's common to run schema migrations or table creations in the constructor,
43+
// wrapped in blockConcurrencyWhile to ensure they complete before other operations.
44+
this.ctx.blockConcurrencyWhile(async () => {
45+
await this.initializeDB();
46+
});
47+
}
48+
49+
async initializeDB() {
50+
// Example: Create table if it doesn't exist
51+
// Note: .execute() is synchronous for DOQB, but blockConcurrencyWhile expects a Promise
52+
this.#qb.createTable({
53+
tableName: 'items', // Example table
54+
ifNotExists: true,
55+
schema: `
56+
id INTEGER PRIMARY KEY AUTOINCREMENT,
57+
name TEXT NOT NULL,
58+
value TEXT
59+
`
60+
}).execute();
3461
}
3562

3663
async fetch(request: Request): Promise<Response> {
37-
const qb = new DOQB(this.storage.sql); // Initialize DOQB with DurableObjectStorage
64+
// Now use this.#qb for queries
65+
// ... your queries using this.#qb ...
3866

39-
// ... your queries using qb ...
67+
// Example: Fetching an item (replace with actual logic)
68+
const items = this.#qb.fetchAll({ tableName: 'items' }).execute();
4069

41-
return new Response("Durable Object queries executed");
70+
return new Response(`Durable Object queries executed. Items count: ${items.results?.length}`);
4271
}
4372
}
4473
```
@@ -56,20 +85,32 @@ All basic and advanced query operations described in [Basic Queries](../basic-qu
5685
```typescript
5786
import { DOQB } from 'workers-qb';
5887

88+
// Define Env if it's used for configuration
89+
export interface Env { /* ... */ }
90+
5991
export class MyDurableObject extends DurableObject {
60-
async fetch(request: Request): Promise<Response> {
61-
const qb = new DOQB(this.storage.sql);
92+
#qb: DOQB;
6293

63-
// Create table (if not exists) - typically in Durable Object's constructor or first fetch
64-
qb.createTable({
65-
tableName: 'items',
66-
ifNotExists: true,
67-
schema: `
68-
id INTEGER PRIMARY KEY AUTOINCREMENT,
69-
name TEXT NOT NULL,
70-
value TEXT
71-
`
72-
}).execute();
94+
constructor(state: DurableObjectState, env: Env) {
95+
super(state, env);
96+
this.#qb = new DOQB(this.storage.sql);
97+
98+
this.ctx.blockConcurrencyWhile(async () => {
99+
// Create table (if not exists) - good practice in constructor
100+
this.#qb.createTable({
101+
tableName: 'items',
102+
ifNotExists: true,
103+
schema: `
104+
id INTEGER PRIMARY KEY AUTOINCREMENT,
105+
name TEXT NOT NULL,
106+
value TEXT
107+
`
108+
}).execute(); // Synchronous execute for DOQB
109+
});
110+
}
111+
112+
async fetch(request: Request): Promise<Response> {
113+
// qb is now this.#qb
73114

74115
type Item = {
75116
id: number;
@@ -78,7 +119,7 @@ export class MyDurableObject extends DurableObject {
78119
};
79120

80121
// Insert an item
81-
const insertedItem = qb.insert<Item>({
122+
const insertedItem = this.#qb.insert<Item>({
82123
tableName: 'items',
83124
data: {
84125
name: 'Example Item',
@@ -90,7 +131,7 @@ export class MyDurableObject extends DurableObject {
90131
console.log('Inserted item:', insertedItem.results);
91132

92133
// Fetch all items
93-
const allItems = qb.fetchAll<Item>({
134+
const allItems = this.#qb.fetchAll<Item>({
94135
tableName: 'items',
95136
}).execute();
96137

@@ -113,9 +154,20 @@ export class MyDurableObject extends DurableObject {
113154
```typescript
114155
import { DOQB } from 'workers-qb';
115156

157+
// Define Env if it's used for configuration
158+
export interface Env { /* ... */ }
159+
116160
export class MyDurableObject extends DurableObject {
161+
#qb: DOQB;
162+
163+
constructor(state: DurableObjectState, env: Env) {
164+
super(state, env);
165+
this.#qb = new DOQB(this.storage.sql);
166+
// Assuming table 'items' is created in constructor as shown in the previous example
167+
}
168+
117169
async fetch(request: Request): Promise<Response> {
118-
const qb = new DOQB(this.storage.sql);
170+
// qb is now this.#qb
119171

120172
type Item = {
121173
id: number;
@@ -124,7 +176,7 @@ export class MyDurableObject extends DurableObject {
124176
};
125177

126178
// Lazy fetch all items
127-
const lazyItemsResult = await qb.fetchAll<Item, true>({ // Note: <Item, true> for lazy fetch
179+
const lazyItemsResult = await this.#qb.fetchAll<Item, true>({ // Note: <Item, true> for lazy fetch
128180
tableName: 'items',
129181
lazy: true, // Explicitly set lazy: true
130182
}).execute();

docs/databases/postgresql.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ While `workers-qb` core doesn't provide a dedicated transaction management API,
163163
**Example (Conceptual - Transaction Handling Outside `workers-qb` API):**
164164
165165
```typescript
166-
import { PGQB } from 'workers-qb';
166+
import { PGQB, Raw } from 'workers-qb'; // Added Raw import
167167
import { Client } from 'pg';
168168
169169
export interface Env {

docs/introduction.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
* Bulk inserts
2121
* JOIN queries
2222
* Modular SELECT query building
23-
* ON CONFLICT handling (for inserts and updates)
24-
* UPSERT support
23+
* ON CONFLICT handling (e.g., IGNORE, REPLACE, UPSERT)
2524

2625
## Supported Databases
2726

@@ -88,7 +87,7 @@ This code snippet demonstrates:
8887
Explore the comprehensive documentation to learn more about `workers-qb`:
8988

9089
* **[Basic Queries](basic-queries.md):** Learn the fundamentals of creating, reading, updating, and deleting data.
91-
* **[Advanced Queries](advanced-queries.md):** Dive into more complex query structures, including joins, subqueries, and conditional logic.
90+
* **[Advanced Queries](advanced-queries.md):** Dive into more complex query structures, including joins and conditional logic.
9291
* **[Migrations](migrations.md):** Discover how to manage your database schema using migrations.
9392
* **[Type Checking](type-check.md):** Understand how to leverage TypeScript for type-safe database interactions.
9493
* **[Database-Specific Guides](databases/d1.md):** Find detailed guides and examples for each supported database.

docs/migrations.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ Database migrations are scripts that define changes to your database schema. Eac
1313

1414
## Creating Migration Files
1515

16-
Each migration is defined as an object with a `name` and `sql` property. The `name` should be a unique identifier for the migration. The `sql` property contains the SQL statements to be executed.
16+
Each migration is defined as an object with a `name` and `sql` property. The `name` should be a unique identifier for the migration (e.g., `YYYYMMDDHHMMSS_descriptive_name` or a sequential number like `0001_descriptive_name`). The `sql` property contains the SQL statements to be executed.
17+
18+
You can organize your migration files as you see fit. For example, you might have one `.ts` file per migration object, or a single file that exports an array of all migration objects.
1719

1820
**Example Migration Structure (in code):**
1921

2022
```typescript
21-
// Optinally you can type the migrations with Migration
23+
// Optionally you can type the migrations with Migration
2224
import { type Migration } from 'workers-qb';
2325

24-
// migrations/0001_create_users_table.ts (if using separate files)
26+
// Example: migrations/0001_create_users_table.ts (if using separate files)
2527
export const createUsersTableMigration = {
2628
name: '0001_create_users_table',
2729
sql: `
@@ -88,12 +90,14 @@ export class MyDurableObject extends DurableObject {
8890

8991
this.#qb = new DOQB(this.ctx.storage.sql);
9092
void this.ctx.blockConcurrencyWhile(async () => {
93+
// Assuming 'migrations' is an array of Migration objects defined elsewhere
9194
const migrationBuilder = this.#qb.migrations({ migrations });
92-
migrationBuilder.apply();
95+
await migrationBuilder.apply(); // Ensure apply is awaited
9396
});
9497
}
9598

9699
async getUsers(): Promise<Array<object>> {
100+
// Example method, ensure migrations are applied before accessing tables
97101
return this.#qb.select('users').all().results
98102
}
99103
}
@@ -108,6 +112,7 @@ You can check the status of your migrations using the `getApplied()` and `getUna
108112
```typescript
109113
import { D1QB } from 'workers-qb';
110114
// ...
115+
// Assuming 'env' and 'migrations' are defined as in previous examples
111116
const qb = new D1QB(env.DB);
112117
const migrationBuilder = qb.migrations({ migrations });
113118
const appliedMigrations = await migrationBuilder.getApplied();
@@ -119,6 +124,7 @@ const appliedMigrations = await migrationBuilder.getApplied();
119124
```typescript
120125
import { D1QB } from 'workers-qb';
121126
// ...
127+
// Assuming 'env' and 'migrations' are defined as in previous examples
122128
const qb = new D1QB(env.DB);
123129
const migrationBuilder = qb.migrations({ migrations });
124130
const unappliedMigrations = await migrationBuilder.getUnapplied();

docs/type-check.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ You can also leverage type checking when using `insert` and `update` queries, es
2727
**Example: Type Checking with `insert` and `returning`**
2828

2929
```typescript
30-
import { D1QB, Raw } from 'workers-qb';
30+
import { D1QB } from 'workers-qb'; // Raw removed as it's not used in this specific example
3131

3232
// ... (D1QB initialization and User type definition from previous examples) ...
3333

0 commit comments

Comments
 (0)