dynamodb-reactive 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -124,35 +124,125 @@ export async function POST(request: Request) {
124
124
  }
125
125
  ```
126
126
 
127
- ### **Step 4: Call from Client**
127
+ ### **Step 4: Set Up React Provider**
128
128
 
129
129
  ```typescript
130
- // Query
131
- const response = await fetch('/api/reactive', {
132
- method: 'POST',
133
- body: JSON.stringify({
134
- type: 'subscribe',
135
- subscriptionId: 'sub-1',
136
- path: 'todos.list',
137
- input: {},
138
- }),
130
+ // app/layout.tsx or providers.tsx
131
+ 'use client';
132
+
133
+ import { ReactiveClientProvider } from 'dynamodb-reactive/client';
134
+
135
+ export function Providers({ children }: { children: React.ReactNode }) {
136
+ return (
137
+ <ReactiveClientProvider
138
+ config={{
139
+ url: process.env.NEXT_PUBLIC_WS_URL!,
140
+ httpUrl: '/api/reactive',
141
+ }}
142
+ >
143
+ {children}
144
+ </ReactiveClientProvider>
145
+ );
146
+ }
147
+ ```
148
+
149
+ ### **Step 5: Use React Hooks**
150
+
151
+ ```typescript
152
+ 'use client';
153
+
154
+ import type { Todo } from './types';
155
+ import type { AppRouter } from './router';
156
+ import { useClient, useConnectionState } from 'dynamodb-reactive/client';
157
+
158
+ export function TodoList() {
159
+ const connectionState = useConnectionState();
160
+ const client = useClient<AppRouter>();
161
+
162
+ // Subscribe to todos - receives real-time updates via WebSocket
163
+ const { data: todos, loading, error } = client.todos.list.useQuery({});
164
+
165
+ // Mutations with automatic optimistic updates
166
+ const createMutation = client.todos.create.useMutation();
167
+ const toggleMutation = client.todos.toggle.useMutation();
168
+ const deleteMutation = client.todos.delete.useMutation();
169
+
170
+ const handleCreate = async (text: string) => {
171
+ await createMutation.mutate({ text });
172
+ };
173
+
174
+ const handleToggle = async (id: string) => {
175
+ await toggleMutation.mutate({ id });
176
+ };
177
+
178
+ const handleDelete = async (id: string) => {
179
+ await deleteMutation.mutate({ id }); // Removes instantly from UI
180
+ };
181
+
182
+ if (loading) return <div>Loading...</div>;
183
+ if (error) return <div>Error: {error.message}</div>;
184
+
185
+ return (
186
+ <div>
187
+ <p>WebSocket: {connectionState}</p>
188
+ {todos?.map((todo) => (
189
+ <div key={todo.id}>
190
+ <input
191
+ type="checkbox"
192
+ checked={todo.completed}
193
+ onChange={() => handleToggle(todo.id)}
194
+ />
195
+ <span>{todo.text}</span>
196
+ <button onClick={() => handleDelete(todo.id)}>Delete</button>
197
+ </div>
198
+ ))}
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+
204
+ ## **5. Optimistic Updates**
205
+
206
+ All mutations apply **instant** optimistic updates (before HTTP request):
207
+
208
+ | Update Type | Behavior | Example Input |
209
+ | :--- | :--- | :--- |
210
+ | `'merge'` | Find item by `input.id`, merge input fields | `{ id: '123', completed: true }` |
211
+ | `'remove'` | Remove item by `input.id` | `{ id: '123' }` |
212
+ | Custom function | Apply custom logic | `(data, input) => [...]` |
213
+
214
+ **Convention:** `<namespace>.<action>` mutations auto-invalidate `<namespace>.list` subscriptions.
215
+
216
+ **Example - Instant Toggle:**
217
+ ```typescript
218
+ // For instant optimistic updates, include the fields you want to change
219
+ const handleToggle = async (todo: Todo) => {
220
+ await updateMutation.mutate({
221
+ id: todo.id,
222
+ completed: !todo.completed // Include new value for instant UI update
223
+ });
224
+ };
225
+ ```
226
+
227
+ **Custom options:**
228
+ ```typescript
229
+ const mutation = client.todos.update.useMutation({
230
+ invalidates: 'todos.list', // Override auto-detection
231
+ optimisticUpdate: 'merge', // 'merge' | 'remove' | custom function
139
232
  });
140
- const { data } = await response.json();
141
-
142
- // Mutation
143
- const response = await fetch('/api/reactive', {
144
- method: 'POST',
145
- body: JSON.stringify({
146
- type: 'call',
147
- callId: 'call-1',
148
- path: 'todos.create',
149
- input: { text: 'New todo' },
233
+
234
+ // For creates (no id), provide optimisticData
235
+ const createMutation = client.todos.create.useMutation({
236
+ optimisticData: (input) => ({
237
+ id: `temp-${Date.now()}`,
238
+ ...input,
239
+ completed: false,
240
+ createdAt: Date.now(),
150
241
  }),
151
242
  });
152
- const { data } = await response.json();
153
243
  ```
154
244
 
155
- ## **5. Database Context Methods**
245
+ ## **6. Database Context Methods**
156
246
 
157
247
  The `ctx.db` object provides these methods:
158
248
 
@@ -164,11 +254,11 @@ The `ctx.db` object provides these methods:
164
254
  | `update(table, key, updates)` | Update item fields |
165
255
  | `delete(table, key)` | Delete item |
166
256
 
167
- ## **6. Requirements**
257
+ ## **7. Requirements**
168
258
 
169
259
  * Node.js >= 18.0.0
170
260
  * TypeScript >= 5.3.0
171
261
 
172
- ## **7. License**
262
+ ## **8. License**
173
263
 
174
264
  MIT