@expresscsv/react 0.1.13 → 0.1.15

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
@@ -283,6 +283,116 @@ function App() {
283
283
  }
284
284
  ```
285
285
 
286
+ ### Webhook Payload Structure
287
+
288
+ Each chunk is delivered as a JSON `POST` (or whichever method you configured) to your endpoint. The request body has this shape:
289
+
290
+ ```typescript
291
+ interface WebhookPayload {
292
+ /** Imported records for this chunk, matching your schema */
293
+ records: Record<string, unknown>[];
294
+ /** 0-based index of the current chunk */
295
+ chunkIndex: number;
296
+ /** Total number of chunks in this delivery */
297
+ totalChunks: number;
298
+ /** Total number of records across all chunks */
299
+ totalRecords: number;
300
+ /** Present only if you passed `metadata` in `WebhookConfig` */
301
+ metadata?: Record<string, unknown>;
302
+ /** Delivery context added by ExpressCSV */
303
+ delivery: {
304
+ publishableKey: string;
305
+ environmentName: string;
306
+ environmentType: string;
307
+ teamSlug: string;
308
+ importIdentifier: string;
309
+ deliveryId: string;
310
+ timestamp: string; // ISO 8601
311
+ };
312
+ }
313
+ ```
314
+
315
+ Example payload:
316
+
317
+ ```json
318
+ {
319
+ "records": [
320
+ { "name": "Alice Johnson", "email": "alice@example.com" },
321
+ { "name": "Bob Smith", "email": "bob@example.com" }
322
+ ],
323
+ "chunkIndex": 0,
324
+ "totalChunks": 3,
325
+ "totalRecords": 2500,
326
+ "metadata": {
327
+ "source": "react-app",
328
+ "userId": "user-123"
329
+ },
330
+ "delivery": {
331
+ "publishableKey": "pk_live_abc123",
332
+ "environmentName": "Production",
333
+ "environmentType": "production",
334
+ "teamSlug": "my-team",
335
+ "importIdentifier": "user-import",
336
+ "deliveryId": "del_abc123",
337
+ "timestamp": "2026-03-02T14:30:00.000Z"
338
+ }
339
+ }
340
+ ```
341
+
342
+ The request includes a `Content-Type: application/json` header plus any custom `headers` you specified in `WebhookConfig`.
343
+
344
+ ### Handling Webhooks on Your Server
345
+
346
+ Your endpoint should return a **2xx** status code to acknowledge each chunk. Non-2xx responses trigger the following retry behaviour:
347
+
348
+ - **5xx** and **429** responses are retried automatically (up to 5 attempts per chunk).
349
+ - **4xx** responses (except 429) are treated as permanent failures and are **not** retried.
350
+
351
+ Chunks are delivered **serially** — the next chunk is only sent after the previous one succeeds.
352
+
353
+ Below is a minimal Express.js example:
354
+
355
+ ```typescript
356
+ import express from "express";
357
+
358
+ const app = express();
359
+ app.use(express.json());
360
+
361
+ app.post("/webhooks/csv-import", async (req, res) => {
362
+ const token = req.headers.authorization;
363
+ if (token !== "Bearer your-api-token") {
364
+ return res.status(401).json({ error: "Unauthorized" });
365
+ }
366
+
367
+ const { records, chunkIndex, totalChunks, totalRecords, metadata, delivery } =
368
+ req.body;
369
+
370
+ try {
371
+ // Process the records — e.g. insert into your database
372
+ await db.insertMany("users", records);
373
+
374
+ console.log(
375
+ `Chunk ${chunkIndex + 1}/${totalChunks} processed ` +
376
+ `(${records.length} records, delivery ${delivery.deliveryId})`
377
+ );
378
+
379
+ res.status(200).json({ success: true });
380
+ } catch (error) {
381
+ // Return 500 so ExpressCSV retries this chunk
382
+ console.error("Failed to process chunk:", error);
383
+ res.status(500).json({ error: "Internal server error" });
384
+ }
385
+ });
386
+
387
+ app.listen(3000);
388
+ ```
389
+
390
+ **Tips:**
391
+
392
+ - Use `delivery.deliveryId` and `chunkIndex` to **deduplicate** retried chunks (the same chunk may be delivered more than once on retry).
393
+ - Use `chunkIndex` and `totalChunks` to track progress and know when the full import is complete (`chunkIndex === totalChunks - 1` for the last chunk).
394
+ - Store `metadata` alongside imported records if you need to correlate the import with a specific user or action in your app.
395
+
286
396
  ### Combined Local Callback and Webhook
287
397
 
288
398
  ```tsx