Skip to content

Commit 919722d

Browse files
authored
durable objects: examples for using type args with SQL API
cc @geelen to keep me honest
1 parent a18546f commit 919722d

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

src/content/docs/durable-objects/api/sql-storage.mdx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,57 @@ let now = new Date();
142142
let bookmark = ctx.storage.getBookmarkForTime(now - 2);
143143
ctx.storage.onNextSessionRestoreBookmark(bookmark);
144144
```
145+
146+
## TypeScript and query results
147+
148+
You can use TypeScript [type parameters](https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables) to provide a type for your results, allowing you to benefit from type hints and checks when iterating over the results of a query.
149+
150+
:::caution
151+
152+
Providing a type parameter does _not_ validate that the query result matches your type definition. In TypeScript, properties (fields) that do not exist in your result type will be silently dropped.
153+
154+
:::
155+
156+
Your type must conform to the shape of a TypeScript [Record](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) type representing the name (`string)` of the column and the type of the column. The column type must be a valid `SqlStorageValue`: one of `ArrayBuffer | string | number | null`.
157+
158+
For example,
159+
160+
```ts
161+
type User = {
162+
id: string;
163+
name: string;
164+
email_address: string;
165+
version: number;
166+
}
167+
```
168+
169+
This type can then be passed as the type parameter to a `sql.exec` call:
170+
171+
```ts
172+
// The type parameter is passed between the "pointy brackets" before the function argument:
173+
const result = this.ctx.storage.sql.exec<User>("SELECT id, name, email_address, version FROM users WHERE id = ?", [user_id]).one()
174+
// result will now have a type of "User"
175+
176+
// Alternatively, if you are iterating over results using a cursor
177+
let cursor = this.sql.exec<User>("SELECT id, name, email_address, version FROM users WHERE id = ?", [user_id])
178+
for (let row of cursor) {
179+
// Each row object will be of type User
180+
}
181+
182+
// Or, if you are using raw() to convert results into an array, define an array type:
183+
type UserRow = [
184+
id: string,
185+
name: string,
186+
email_address: string,
187+
version: number,
188+
];
189+
190+
// ... and then pass it as the type argument to the raw() method:
191+
let cursor = sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", [user_id]).raw<UserRow>();
192+
193+
for (let row of cursor) {
194+
// row is of type User
195+
}
196+
```
197+
198+
You can represent the shape of any result type you wish, including more complex types. If you are performing a JOIN across multiple tables, you can compose a type that reflects that results of your queries.

0 commit comments

Comments
 (0)