@expresscsv/sdk 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 +110 -0
- package/package.json +3 -10
package/README.md
CHANGED
|
@@ -83,6 +83,116 @@ importer.open({
|
|
|
83
83
|
});
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
### Webhook Payload Structure
|
|
87
|
+
|
|
88
|
+
Each chunk is delivered as a JSON `POST` (or whichever method you configured) to your endpoint. The request body has this shape:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
interface WebhookPayload {
|
|
92
|
+
/** Imported records for this chunk, matching your schema */
|
|
93
|
+
records: Record<string, unknown>[];
|
|
94
|
+
/** 0-based index of the current chunk */
|
|
95
|
+
chunkIndex: number;
|
|
96
|
+
/** Total number of chunks in this delivery */
|
|
97
|
+
totalChunks: number;
|
|
98
|
+
/** Total number of records across all chunks */
|
|
99
|
+
totalRecords: number;
|
|
100
|
+
/** Present only if you passed `metadata` in `WebhookConfig` */
|
|
101
|
+
metadata?: Record<string, unknown>;
|
|
102
|
+
/** Delivery context added by ExpressCSV */
|
|
103
|
+
delivery: {
|
|
104
|
+
publishableKey: string;
|
|
105
|
+
environmentName: string;
|
|
106
|
+
environmentType: string;
|
|
107
|
+
teamSlug: string;
|
|
108
|
+
importIdentifier: string;
|
|
109
|
+
deliveryId: string;
|
|
110
|
+
timestamp: string; // ISO 8601
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Example payload:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"records": [
|
|
120
|
+
{ "name": "Alice Johnson", "email": "alice@example.com", "age": 32 },
|
|
121
|
+
{ "name": "Bob Smith", "email": "bob@example.com", "age": 45 }
|
|
122
|
+
],
|
|
123
|
+
"chunkIndex": 0,
|
|
124
|
+
"totalChunks": 3,
|
|
125
|
+
"totalRecords": 2500,
|
|
126
|
+
"metadata": {
|
|
127
|
+
"source": "web-app",
|
|
128
|
+
"userId": "user-123"
|
|
129
|
+
},
|
|
130
|
+
"delivery": {
|
|
131
|
+
"publishableKey": "pk_live_abc123",
|
|
132
|
+
"environmentName": "Production",
|
|
133
|
+
"environmentType": "production",
|
|
134
|
+
"teamSlug": "my-team",
|
|
135
|
+
"importIdentifier": "user-import",
|
|
136
|
+
"deliveryId": "del_abc123",
|
|
137
|
+
"timestamp": "2026-03-02T14:30:00.000Z"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The request includes a `Content-Type: application/json` header plus any custom `headers` you specified in `WebhookConfig`.
|
|
143
|
+
|
|
144
|
+
### Handling Webhooks on Your Server
|
|
145
|
+
|
|
146
|
+
Your endpoint should return a **2xx** status code to acknowledge each chunk. Non-2xx responses trigger the following retry behaviour:
|
|
147
|
+
|
|
148
|
+
- **5xx** and **429** responses are retried automatically (up to 5 attempts per chunk).
|
|
149
|
+
- **4xx** responses (except 429) are treated as permanent failures and are **not** retried.
|
|
150
|
+
|
|
151
|
+
Chunks are delivered **serially** — the next chunk is only sent after the previous one succeeds.
|
|
152
|
+
|
|
153
|
+
Below is a minimal Express.js example:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import express from "express";
|
|
157
|
+
|
|
158
|
+
const app = express();
|
|
159
|
+
app.use(express.json());
|
|
160
|
+
|
|
161
|
+
app.post("/webhooks/csv-import", async (req, res) => {
|
|
162
|
+
const token = req.headers.authorization;
|
|
163
|
+
if (token !== "Bearer your-api-token") {
|
|
164
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { records, chunkIndex, totalChunks, totalRecords, metadata, delivery } =
|
|
168
|
+
req.body;
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// Process the records — e.g. insert into your database
|
|
172
|
+
await db.insertMany("users", records);
|
|
173
|
+
|
|
174
|
+
console.log(
|
|
175
|
+
`Chunk ${chunkIndex + 1}/${totalChunks} processed ` +
|
|
176
|
+
`(${records.length} records, delivery ${delivery.deliveryId})`
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
res.status(200).json({ success: true });
|
|
180
|
+
} catch (error) {
|
|
181
|
+
// Return 500 so ExpressCSV retries this chunk
|
|
182
|
+
console.error("Failed to process chunk:", error);
|
|
183
|
+
res.status(500).json({ error: "Internal server error" });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
app.listen(3000);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Tips:**
|
|
191
|
+
|
|
192
|
+
- Use `delivery.deliveryId` and `chunkIndex` to **deduplicate** retried chunks (the same chunk may be delivered more than once on retry).
|
|
193
|
+
- Use `chunkIndex` and `totalChunks` to track progress and know when the full import is complete (`chunkIndex === totalChunks - 1` for the last chunk).
|
|
194
|
+
- Store `metadata` alongside imported records if you need to correlate the import with a specific user or action in your app.
|
|
195
|
+
|
|
86
196
|
### Combined Local Callback and Webhook
|
|
87
197
|
|
|
88
198
|
You can use both `onData` and `webhook` simultaneously:
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expresscsv/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "SDK for integrating widget",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.mjs",
|
|
7
6
|
"types": "dist/index.d.ts",
|
|
8
7
|
"files": [
|
|
9
8
|
"dist"
|
|
@@ -26,14 +25,8 @@
|
|
|
26
25
|
},
|
|
27
26
|
"exports": {
|
|
28
27
|
".": {
|
|
29
|
-
"import":
|
|
30
|
-
|
|
31
|
-
"default": "./dist/index.mjs"
|
|
32
|
-
},
|
|
33
|
-
"require": {
|
|
34
|
-
"types": "./dist/index.d.cts",
|
|
35
|
-
"default": "./dist/index.js"
|
|
36
|
-
}
|
|
28
|
+
"import": "./dist/index.mjs",
|
|
29
|
+
"require": "./dist/index.js"
|
|
37
30
|
}
|
|
38
31
|
}
|
|
39
32
|
}
|