Skip to content

Commit 5260ee3

Browse files
KyleAMathewsclaude
andauthored
feat: replace string-based errors with named error classes for better error handling (#297)
Co-authored-by: Claude <[email protected]>
1 parent 608be0c commit 5260ee3

30 files changed

+1106
-239
lines changed

.changeset/short-dingos-raise.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
"@tanstack/trailbase-db-collection": patch
3+
"@tanstack/electric-db-collection": patch
4+
"@tanstack/query-db-collection": patch
5+
"@tanstack/db": patch
6+
---
7+
8+
feat: Replace string-based errors with named error classes for better error handling
9+
10+
This comprehensive update replaces all string-based error throws throughout the TanStack DB codebase with named error classes, providing better type safety and developer experience.
11+
12+
## New Features
13+
14+
- **Root `TanStackDBError` class** - all errors inherit from a common base for unified error handling
15+
- **Named error classes** organized by package and functional area
16+
- **Type-safe error handling** using `instanceof` checks instead of string matching
17+
- **Package-specific error definitions** - each adapter has its own error classes
18+
- **Better IDE support** with autocomplete for error types
19+
20+
## Package Structure
21+
22+
### Core Package (`@tanstack/db`)
23+
24+
Contains generic errors used across the ecosystem:
25+
26+
- Collection configuration, state, and operation errors
27+
- Transaction lifecycle and mutation errors
28+
- Query building, compilation, and execution errors
29+
- Storage and serialization errors
30+
31+
### Adapter Packages
32+
33+
Each adapter now exports its own specific error classes:
34+
35+
- **`@tanstack/electric-db-collection`**: Electric-specific errors
36+
- **`@tanstack/trailbase-db-collection`**: TrailBase-specific errors
37+
- **`@tanstack/query-db-collection`**: Query collection specific errors
38+
39+
## Breaking Changes
40+
41+
- Error handling code using string matching will need to be updated to use `instanceof` checks
42+
- Some error messages may have slight formatting changes
43+
- Adapter-specific errors now need to be imported from their respective packages
44+
45+
## Migration Guide
46+
47+
### Core DB Errors
48+
49+
**Before:**
50+
51+
```ts
52+
try {
53+
collection.insert(data)
54+
} catch (error) {
55+
if (error.message.includes("already exists")) {
56+
// Handle duplicate key error
57+
}
58+
}
59+
```
60+
61+
**After:**
62+
63+
```ts
64+
import { DuplicateKeyError } from "@tanstack/db"
65+
66+
try {
67+
collection.insert(data)
68+
} catch (error) {
69+
if (error instanceof DuplicateKeyError) {
70+
// Type-safe error handling
71+
}
72+
}
73+
```
74+
75+
### Adapter-Specific Errors
76+
77+
**Before:**
78+
79+
```ts
80+
// Electric collection errors were imported from @tanstack/db
81+
import { ElectricInsertHandlerMustReturnTxIdError } from "@tanstack/db"
82+
```
83+
84+
**After:**
85+
86+
```ts
87+
// Now import from the specific adapter package
88+
import { ElectricInsertHandlerMustReturnTxIdError } from "@tanstack/electric-db-collection"
89+
```
90+
91+
### Unified Error Handling
92+
93+
**New:**
94+
95+
```ts
96+
import { TanStackDBError } from "@tanstack/db"
97+
98+
try {
99+
// Any TanStack DB operation
100+
} catch (error) {
101+
if (error instanceof TanStackDBError) {
102+
// Handle all TanStack DB errors uniformly
103+
console.log("TanStack DB error:", error.message)
104+
}
105+
}
106+
```
107+
108+
## Benefits
109+
110+
- **Type Safety**: All errors now have specific types that can be caught with `instanceof`
111+
- **Unified Error Handling**: Root `TanStackDBError` class allows catching all library errors with a single check
112+
- **Better Package Separation**: Each adapter manages its own error types
113+
- **Developer Experience**: Better IDE support with autocomplete for error types
114+
- **Maintainability**: Error definitions are co-located with their usage
115+
- **Consistency**: Uniform error handling patterns across the entire codebase
116+
117+
All error classes maintain the same error messages and behavior while providing better structure and package separation.

docs/error-handling.md

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ TanStack DB provides comprehensive error handling capabilities to ensure robust
99

1010
## Error Types
1111

12+
TanStack DB provides named error classes for better error handling and type safety. All error classes can be imported from `@tanstack/db` (or more commonly, the framework-specific package e.g. `@tanstack/react-db`):
13+
14+
```ts
15+
import {
16+
SchemaValidationError,
17+
CollectionInErrorStateError,
18+
DuplicateKeyError,
19+
MissingHandlerError,
20+
TransactionError,
21+
// ... and many more
22+
} from "@tanstack/db"
23+
```
24+
1225
### SchemaValidationError
1326

1427
Thrown when data doesn't match the collection's schema during insert or update operations:
@@ -168,10 +181,12 @@ try {
168181
Collections in an `error` state cannot perform operations and must be manually recovered:
169182

170183
```ts
184+
import { CollectionInErrorStateError } from "@tanstack/db"
185+
171186
try {
172187
todoCollection.insert(newTodo)
173188
} catch (error) {
174-
if (error.message.includes("collection is in error state")) {
189+
if (error instanceof CollectionInErrorStateError) {
175190
// Collection needs to be cleaned up and restarted
176191
await todoCollection.cleanup()
177192

@@ -202,10 +217,14 @@ todoCollection.insert(newTodo)
202217
Inserting items with existing keys will throw:
203218

204219
```ts
220+
import { DuplicateKeyError } from "@tanstack/db"
221+
205222
try {
206223
todoCollection.insert({ id: "existing-id", text: "Todo" })
207224
} catch (error) {
208-
// Error: Cannot insert document with ID "existing-id" because it already exists in the collection
225+
if (error instanceof DuplicateKeyError) {
226+
console.log(`Duplicate key: ${error.message}`)
227+
}
209228
}
210229
```
211230

@@ -384,16 +403,31 @@ tx.rollback() // Error: You can no longer call .rollback() as the transaction is
384403

385404
## Best Practices
386405

387-
1. **Always handle SchemaValidationError** - Provide clear feedback for validation failures
388-
2. **Check collection status** - Use `isError`, `isLoading`, `isReady` flags in React components
389-
3. **Handle transaction promises** - Always handle `isPersisted.promise` rejections
406+
1. **Use instanceof checks** - Use `instanceof` instead of string matching for error handling:
407+
```ts
408+
// ✅ Good - type-safe error handling
409+
if (error instanceof SchemaValidationError) {
410+
// Handle validation error
411+
}
412+
413+
// ❌ Avoid - brittle string matching
414+
if (error.message.includes("validation failed")) {
415+
// Handle validation error
416+
}
417+
```
418+
419+
2. **Import specific error types** - Import only the error classes you need for better tree-shaking
420+
3. **Always handle SchemaValidationError** - Provide clear feedback for validation failures
421+
4. **Check collection status** - Use `isError`, `isLoading`, `isReady` flags in React components
422+
5. **Handle transaction promises** - Always handle `isPersisted.promise` rejections
390423

391424
## Example: Complete Error Handling
392425

393426
```tsx
394427
import {
395428
createCollection,
396429
SchemaValidationError,
430+
DuplicateKeyError,
397431
createTransaction
398432
} from "@tanstack/db"
399433
import { useLiveQuery } from "@tanstack/react-db"
@@ -442,7 +476,7 @@ const TodoApp = () => {
442476
} catch (error) {
443477
if (error instanceof SchemaValidationError) {
444478
alert(`Validation error: ${error.issues[0]?.message}`)
445-
} else if (error.message?.includes("already exists")) {
479+
} else if (error instanceof DuplicateKeyError) {
446480
alert("A todo with this ID already exists")
447481
} else {
448482
alert(`Failed to add todo: ${error.message}`)

0 commit comments

Comments
 (0)