Skip to content

Commit adc2734

Browse files
zeevdrclaude
andauthored
docs: showcase await using pattern in README quickstart (#104)
Replace the try/finally examples in Quick Start and Watch for Changes with `await using` / `using`, since ConfigClient and ConfigWatcher both implement Symbol.asyncDispose. Fold the old try/finally snippet into a <details> block so readers who need it can still find it. Also update examples/quickstart/main.ts to use `await using` so the runnable example matches the README. Closes #68 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4097dbb commit adc2734

2 files changed

Lines changed: 55 additions & 44 deletions

File tree

README.md

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,41 +24,57 @@ npm install @opendecree/sdk
2424

2525
## Quick Start
2626

27+
The SDK implements `Symbol.dispose` / `Symbol.asyncDispose`, so you can use TypeScript 5.2's `using` statement for automatic cleanup — no try/finally needed.
28+
2729
```typescript
2830
import { ConfigClient } from '@opendecree/sdk';
2931

30-
const client = new ConfigClient('localhost:9090', { subject: 'myapp' });
31-
try {
32-
// Get config values (default: string)
33-
const fee = await client.get('tenant-id', 'payments.fee');
32+
// `using` closes the gRPC channel automatically when the block exits
33+
await using client = new ConfigClient('localhost:9090', { subject: 'myapp' });
3434

35-
// Typed gets via runtime converters
36-
const retries = await client.get('tenant-id', 'payments.retries', Number);
37-
const enabled = await client.get('tenant-id', 'payments.enabled', Boolean);
35+
// Get config values (default: string)
36+
const fee = await client.get('tenant-id', 'payments.fee');
3837

39-
// Nullable gets
40-
const optional = await client.get('tenant-id', 'payments.fee', Number, { nullable: true });
38+
// Typed gets via runtime converters
39+
const retries = await client.get('tenant-id', 'payments.retries', Number);
40+
const enabled = await client.get('tenant-id', 'payments.enabled', Boolean);
41+
42+
// Nullable gets
43+
const optional = await client.get('tenant-id', 'payments.fee', Number, { nullable: true });
44+
45+
// Set values
46+
await client.set('tenant-id', 'payments.fee', '0.5%');
47+
48+
// Set multiple values atomically
49+
await client.setMany('tenant-id', {
50+
'payments.fee': '0.5%',
51+
'payments.retries': '3',
52+
});
53+
```
4154

42-
// Set values
43-
await client.set('tenant-id', 'payments.fee', '0.5%');
55+
<details>
56+
<summary>Prefer try/finally?</summary>
4457

45-
// Set multiple values atomically
46-
await client.setMany('tenant-id', {
47-
'payments.fee': '0.5%',
48-
'payments.retries': '3',
49-
});
58+
```typescript
59+
const client = new ConfigClient('localhost:9090', { subject: 'myapp' });
60+
try {
61+
const fee = await client.get('tenant-id', 'payments.fee');
62+
// ...
5063
} finally {
5164
client.close();
5265
}
5366
```
67+
</details>
5468

5569
## Watch for Changes
5670

71+
`ConfigWatcher` also supports `await using` for automatic stop + close:
72+
5773
```typescript
5874
import { ConfigClient } from '@opendecree/sdk';
5975

60-
const client = new ConfigClient('localhost:9090', { subject: 'myapp' });
61-
const watcher = client.watch('tenant-id');
76+
await using client = new ConfigClient('localhost:9090', { subject: 'myapp' });
77+
await using watcher = client.watch('tenant-id');
6278

6379
// Register fields before starting
6480
const fee = watcher.field('payments.fee', Number, { default: 0.01 });
@@ -80,10 +96,7 @@ fee.on('change', (oldVal, newVal) => {
8096
for await (const change of fee) {
8197
console.log(change.fieldPath, change.newValue);
8298
}
83-
84-
// Cleanup
85-
await watcher.stop();
86-
client.close();
99+
// watcher.stop() + client.close() called automatically
87100
```
88101

89102
## Examples
@@ -92,7 +105,7 @@ Runnable examples in the [`examples/`](examples/) directory:
92105

93106
| Example | What it shows |
94107
|---------|--------------|
95-
| [quickstart](examples/quickstart/) | Type converters (`Number`, `Boolean`), try/finally |
108+
| [quickstart](examples/quickstart/) | `using` / `await using`, type converters (`Number`, `Boolean`) |
96109
| [live-config](examples/live-config/) | `ConfigWatcher`, `.on('change')`, `for await...of` |
97110
| [nextjs-integration](examples/nextjs-integration/) | Singleton watcher for server-side config |
98111
| [error-handling](examples/error-handling/) | `RetryConfig`, `{ nullable: true }`, `instanceof` narrowing |

examples/quickstart/main.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,31 @@ import { ConfigClient } from "@opendecree/sdk";
1313
import { getTenantId } from "../shared.js";
1414

1515
async function main(): Promise<void> {
16-
const tenantId = getTenantId();
17-
const client = new ConfigClient("localhost:9090", { subject: "quickstart-example" });
16+
const tenantId = getTenantId();
1817

19-
try {
20-
// get() returns string by default.
21-
const name = await client.get(tenantId, "app.name");
22-
console.log(`app.name: ${name}`);
18+
// `await using` calls [Symbol.asyncDispose] automatically — no try/finally needed.
19+
await using client = new ConfigClient("localhost:9090", { subject: "quickstart-example" });
2320

24-
// Pass a type converter for typed values.
25-
const debug = await client.get(tenantId, "app.debug", Boolean);
26-
console.log(`app.debug: ${debug}`);
21+
// get() returns string by default.
22+
const name = await client.get(tenantId, "app.name");
23+
console.log(`app.name: ${name}`);
2724

28-
const rateLimit = await client.get(tenantId, "server.rate_limit", Number);
29-
console.log(`server.rate_limit: ${rateLimit}`);
25+
// Pass a type converter for typed values.
26+
const debug = await client.get(tenantId, "app.debug", Boolean);
27+
console.log(`app.debug: ${debug}`);
3028

31-
const feeRate = await client.get(tenantId, "payments.fee_rate", Number);
32-
console.log(`payments.fee_rate: ${feeRate}`);
29+
const rateLimit = await client.get(tenantId, "server.rate_limit", Number);
30+
console.log(`server.rate_limit: ${rateLimit}`);
3331

34-
// set() and setMany() for writes.
35-
await client.set(tenantId, "app.debug", "true");
36-
console.log("\nSet app.debug = true");
32+
const feeRate = await client.get(tenantId, "payments.fee_rate", Number);
33+
console.log(`payments.fee_rate: ${feeRate}`);
3734

38-
const updated = await client.get(tenantId, "app.debug", Boolean);
39-
console.log(`app.debug: ${updated}`);
40-
} finally {
41-
client.close();
42-
}
35+
// set() and setMany() for writes.
36+
await client.set(tenantId, "app.debug", "true");
37+
console.log("\nSet app.debug = true");
38+
39+
const updated = await client.get(tenantId, "app.debug", Boolean);
40+
console.log(`app.debug: ${updated}`);
4341
}
4442

4543
main().catch(console.error);

0 commit comments

Comments
 (0)