chub-dev 0.1.0 → 0.1.2-beta.0

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.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1437 @@
1
+ ---
2
+ name: database
3
+ description: "Airtable JavaScript SDK (airtable.js) — use the official airtable npm package for Airtable API operations"
4
+ metadata:
5
+ languages: "javascript"
6
+ versions: "0.12.2"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "airtable,database,low-code,spreadsheet,api"
10
+ ---
11
+
12
+ # Airtable JavaScript SDK (airtable.js) - Version 0.12.2
13
+
14
+ ## Golden Rule
15
+
16
+ **ALWAYS use the official `airtable` npm package (version 0.12.2 or later)**
17
+
18
+ ```bash
19
+ npm install airtable
20
+ ```
21
+
22
+ **DO NOT use:**
23
+ - Deprecated or unofficial packages like `airtable-node`, `airtable-plus`, or other third-party wrappers
24
+ - The old API key authentication method (deprecated as of February 1, 2024)
25
+ - The `@airtable/blocks` package unless building Airtable extensions
26
+
27
+ **ALWAYS use Personal Access Tokens (PATs) for authentication**, not the deprecated API keys.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install airtable
33
+ ```
34
+
35
+ ### Environment Variable Setup
36
+
37
+ Create a `.env` file:
38
+
39
+ ```bash
40
+ AIRTABLE_API_KEY=your_personal_access_token_here
41
+ ```
42
+
43
+ For production applications, use proper secret management systems.
44
+
45
+ ## Authentication & Initialization
46
+
47
+ ### Personal Access Token Setup
48
+
49
+ 1. Visit https://airtable.com/create/tokens to create a Personal Access Token
50
+ 2. Name your token (e.g., "My App Token")
51
+ 3. Add required scopes:
52
+ - `data.records:read` - to read records
53
+ - `data.records:write` - to create/update/delete records
54
+ - `schema.bases:read` - to read base structure (optional)
55
+ 4. Select base access level (specific bases or all workspace bases)
56
+ 5. Copy the token immediately (shown only once)
57
+
58
+ ### Basic Configuration
59
+
60
+ **Option 1: Global Configuration**
61
+
62
+ ```javascript
63
+ const Airtable = require('airtable');
64
+
65
+ Airtable.configure({
66
+ apiKey: 'your_personal_access_token'
67
+ });
68
+
69
+ const base = Airtable.base('appYourBaseId');
70
+ ```
71
+
72
+ **Option 2: Environment Variable**
73
+
74
+ ```javascript
75
+ const Airtable = require('airtable');
76
+
77
+ // Automatically reads from AIRTABLE_API_KEY environment variable
78
+ const base = Airtable.base('appYourBaseId');
79
+ ```
80
+
81
+ **Option 3: Per-Instance Configuration**
82
+
83
+ ```javascript
84
+ const Airtable = require('airtable');
85
+
86
+ const airtable = new Airtable({
87
+ apiKey: process.env.AIRTABLE_API_KEY
88
+ });
89
+
90
+ const base = airtable.base('appYourBaseId');
91
+ ```
92
+
93
+ ### Advanced Configuration Options
94
+
95
+ ```javascript
96
+ const Airtable = require('airtable');
97
+
98
+ Airtable.configure({
99
+ apiKey: process.env.AIRTABLE_API_KEY,
100
+ endpointUrl: 'https://api.airtable.com', // Custom endpoint (optional)
101
+ requestTimeout: 300000 // Request timeout in milliseconds (default: 300000)
102
+ });
103
+ ```
104
+
105
+ ### Finding Your Base and Table IDs
106
+
107
+ 1. Go to https://airtable.com/api
108
+ 2. Select your base
109
+ 3. Base ID format: `appXXXXXXXXXXXXXX`
110
+ 4. Table names are case-sensitive strings (e.g., 'Tasks', 'Contacts')
111
+
112
+ ## Core API Surfaces
113
+
114
+ ### Reading Records
115
+
116
+ #### Get Single Record by ID
117
+
118
+ ```javascript
119
+ const table = base('Tasks');
120
+
121
+ // Callback style
122
+ table.find('recXXXXXXXXXXXXXX', function(err, record) {
123
+ if (err) {
124
+ console.error(err);
125
+ return;
126
+ }
127
+ console.log('Retrieved', record.get('Name'));
128
+ console.log('Record ID:', record.id);
129
+ console.log('Created time:', record._rawJson.createdTime);
130
+ });
131
+
132
+ // Promise style
133
+ table.find('recXXXXXXXXXXXXXX')
134
+ .then(record => {
135
+ console.log('Retrieved', record.get('Name'));
136
+ })
137
+ .catch(err => {
138
+ console.error(err);
139
+ });
140
+
141
+ // Async/await style
142
+ async function getRecord() {
143
+ try {
144
+ const record = await table.find('recXXXXXXXXXXXXXX');
145
+ console.log('Name:', record.get('Name'));
146
+ console.log('Status:', record.get('Status'));
147
+
148
+ // Access all fields
149
+ const fields = record.fields;
150
+ console.log('All fields:', fields);
151
+ } catch (err) {
152
+ console.error(err);
153
+ }
154
+ }
155
+ ```
156
+
157
+ #### Get All Records (≤100 records)
158
+
159
+ Use `firstPage()` when you know your table has 100 or fewer records:
160
+
161
+ ```javascript
162
+ const table = base('Tasks');
163
+
164
+ // Callback style
165
+ table.select({
166
+ view: 'Grid view'
167
+ }).firstPage(function(err, records) {
168
+ if (err) {
169
+ console.error(err);
170
+ return;
171
+ }
172
+
173
+ records.forEach(function(record) {
174
+ console.log('Retrieved', record.get('Name'));
175
+ });
176
+ });
177
+
178
+ // Promise style
179
+ table.select().firstPage()
180
+ .then(records => {
181
+ records.forEach(record => {
182
+ console.log('Name:', record.get('Name'));
183
+ console.log('Status:', record.get('Status'));
184
+ });
185
+ })
186
+ .catch(err => {
187
+ console.error(err);
188
+ });
189
+
190
+ // Async/await style
191
+ async function getAllRecords() {
192
+ try {
193
+ const records = await table.select().firstPage();
194
+
195
+ records.forEach(record => {
196
+ console.log('ID:', record.id);
197
+ console.log('Name:', record.get('Name'));
198
+ });
199
+ } catch (err) {
200
+ console.error(err);
201
+ }
202
+ }
203
+ ```
204
+
205
+ #### Paginated Reading (>100 records)
206
+
207
+ Use `eachPage()` for tables with more than 100 records:
208
+
209
+ ```javascript
210
+ const table = base('Tasks');
211
+
212
+ let allRecords = [];
213
+
214
+ table.select({
215
+ view: 'Grid view'
216
+ }).eachPage(
217
+ function page(records, fetchNextPage) {
218
+ // Called for each page of records
219
+ records.forEach(function(record) {
220
+ console.log('Retrieved', record.get('Name'));
221
+ allRecords.push(record);
222
+ });
223
+
224
+ // Fetch the next page of records
225
+ fetchNextPage();
226
+ },
227
+ function done(err) {
228
+ // Called when all pages have been retrieved
229
+ if (err) {
230
+ console.error(err);
231
+ return;
232
+ }
233
+
234
+ console.log(`Total records: ${allRecords.length}`);
235
+ }
236
+ );
237
+ ```
238
+
239
+ #### Using all() Method with Async/Await
240
+
241
+ The `all()` method retrieves all pages synchronously:
242
+
243
+ ```javascript
244
+ const table = base('Tasks');
245
+
246
+ async function getAllRecordsSimple() {
247
+ try {
248
+ const records = await table.select({
249
+ view: 'Grid view'
250
+ }).all();
251
+
252
+ console.log(`Total records: ${records.length}`);
253
+
254
+ records.forEach(record => {
255
+ console.log('Name:', record.get('Name'));
256
+ });
257
+
258
+ return records;
259
+ } catch (err) {
260
+ console.error(err);
261
+ }
262
+ }
263
+ ```
264
+
265
+ **Important:** Only use `all()` when you expect a manageable number of records. For very large tables, use `eachPage()` to avoid memory issues.
266
+
267
+ ### Filtering Records
268
+
269
+ #### Using filterByFormula
270
+
271
+ ```javascript
272
+ const table = base('Tasks');
273
+
274
+ // Simple filter
275
+ const records = await table.select({
276
+ filterByFormula: "{Status} = 'Active'"
277
+ }).all();
278
+
279
+ // Multiple conditions with AND
280
+ const activeHighPriority = await table.select({
281
+ filterByFormula: "AND({Status} = 'Active', {Priority} = 'High')"
282
+ }).all();
283
+
284
+ // Multiple conditions with OR
285
+ const urgentOrBlocked = await table.select({
286
+ filterByFormula: "OR({Status} = 'Urgent', {Status} = 'Blocked')"
287
+ }).all();
288
+
289
+ // Not empty check
290
+ const withTitles = await table.select({
291
+ filterByFormula: "NOT({Title} = '')"
292
+ }).all();
293
+
294
+ // Greater than comparison
295
+ const recentTasks = await table.select({
296
+ filterByFormula: "{Created} > '2025-01-01'"
297
+ }).all();
298
+
299
+ // String matching with variable
300
+ const email = 'user@example.com';
301
+ const userRecords = await table.select({
302
+ filterByFormula: `{Email} = "${email}"`
303
+ }).all();
304
+
305
+ // Complex formula
306
+ const complexFilter = await table.select({
307
+ filterByFormula: `AND(
308
+ {Status} = 'In Progress',
309
+ {Priority} = 'High',
310
+ {Assignee} != '',
311
+ {DueDate} <= TODAY()
312
+ )`
313
+ }).all();
314
+ ```
315
+
316
+ ### Sorting Records
317
+
318
+ ```javascript
319
+ const table = base('Tasks');
320
+
321
+ // Sort by single field (ascending)
322
+ const sortedAsc = await table.select({
323
+ sort: [{field: 'Name', direction: 'asc'}]
324
+ }).all();
325
+
326
+ // Sort by single field (descending)
327
+ const sortedDesc = await table.select({
328
+ sort: [{field: 'Created', direction: 'desc'}]
329
+ }).all();
330
+
331
+ // Sort by multiple fields
332
+ const multiSort = await table.select({
333
+ sort: [
334
+ {field: 'Priority', direction: 'desc'},
335
+ {field: 'DueDate', direction: 'asc'},
336
+ {field: 'Name', direction: 'asc'}
337
+ ]
338
+ }).all();
339
+ ```
340
+
341
+ ### Limiting and Pagination Parameters
342
+
343
+ ```javascript
344
+ const table = base('Tasks');
345
+
346
+ // Limit total number of records returned
347
+ const limitedRecords = await table.select({
348
+ maxRecords: 50
349
+ }).all();
350
+
351
+ // Set page size (max 100)
352
+ const customPageSize = await table.select({
353
+ pageSize: 50
354
+ }).firstPage();
355
+
356
+ // Combine all parameters
357
+ const complexQuery = await table.select({
358
+ view: 'Active Tasks',
359
+ maxRecords: 100,
360
+ pageSize: 50,
361
+ filterByFormula: "NOT({Name} = '')",
362
+ sort: [{field: 'Priority', direction: 'desc'}]
363
+ }).all();
364
+ ```
365
+
366
+ ### Selecting Specific Fields
367
+
368
+ ```javascript
369
+ const table = base('Tasks');
370
+
371
+ // Only retrieve specific fields
372
+ const records = await table.select({
373
+ fields: ['Name', 'Status', 'Assignee']
374
+ }).all();
375
+
376
+ records.forEach(record => {
377
+ console.log('Name:', record.get('Name'));
378
+ console.log('Status:', record.get('Status'));
379
+ // Other fields will be undefined
380
+ });
381
+ ```
382
+
383
+ ### Using Views
384
+
385
+ ```javascript
386
+ const table = base('Tasks');
387
+
388
+ // Select from a specific view
389
+ const viewRecords = await table.select({
390
+ view: 'Active Tasks'
391
+ }).all();
392
+
393
+ // Combine view with other parameters
394
+ const filteredView = await table.select({
395
+ view: 'Active Tasks',
396
+ filterByFormula: "{Priority} = 'High'",
397
+ sort: [{field: 'DueDate', direction: 'asc'}]
398
+ }).all();
399
+ ```
400
+
401
+ ### Creating Records
402
+
403
+ #### Create Single Record
404
+
405
+ ```javascript
406
+ const table = base('Tasks');
407
+
408
+ // Callback style
409
+ table.create({
410
+ 'Name': 'New Task',
411
+ 'Status': 'To Do',
412
+ 'Priority': 'Medium'
413
+ }, function(err, record) {
414
+ if (err) {
415
+ console.error(err);
416
+ return;
417
+ }
418
+ console.log('Created record:', record.id);
419
+ });
420
+
421
+ // Promise style
422
+ table.create({
423
+ 'Name': 'New Task',
424
+ 'Status': 'To Do'
425
+ }).then(record => {
426
+ console.log('Created record:', record.id);
427
+ }).catch(err => {
428
+ console.error(err);
429
+ });
430
+
431
+ // Async/await style
432
+ async function createTask() {
433
+ try {
434
+ const record = await table.create({
435
+ 'Name': 'New Task',
436
+ 'Status': 'To Do',
437
+ 'Priority': 'High',
438
+ 'Notes': 'This is a new task'
439
+ });
440
+
441
+ console.log('Created record:', record.id);
442
+ console.log('Name:', record.get('Name'));
443
+
444
+ return record;
445
+ } catch (err) {
446
+ console.error(err);
447
+ }
448
+ }
449
+ ```
450
+
451
+ #### Create with Typecast
452
+
453
+ ```javascript
454
+ const table = base('Tasks');
455
+
456
+ // Typecast converts strings to appropriate types
457
+ const record = await table.create({
458
+ 'Name': 'Task with Date',
459
+ 'DueDate': '2025-12-31', // String will be converted to date
460
+ 'Count': '42' // String will be converted to number
461
+ }, {
462
+ typecast: true
463
+ });
464
+ ```
465
+
466
+ #### Batch Create (Multiple Records)
467
+
468
+ Airtable allows up to 10 records per batch operation:
469
+
470
+ ```javascript
471
+ const table = base('Tasks');
472
+
473
+ // Callback style
474
+ table.create([
475
+ {
476
+ 'Name': 'Task 1',
477
+ 'Status': 'To Do'
478
+ },
479
+ {
480
+ 'Name': 'Task 2',
481
+ 'Status': 'In Progress'
482
+ },
483
+ {
484
+ 'Name': 'Task 3',
485
+ 'Status': 'Done'
486
+ }
487
+ ], function(err, records) {
488
+ if (err) {
489
+ console.error(err);
490
+ return;
491
+ }
492
+
493
+ records.forEach(function(record) {
494
+ console.log('Created:', record.id);
495
+ });
496
+ });
497
+
498
+ // Async/await style
499
+ async function batchCreate() {
500
+ try {
501
+ const records = await table.create([
502
+ {'Name': 'Task 1', 'Status': 'To Do'},
503
+ {'Name': 'Task 2', 'Status': 'In Progress'},
504
+ {'Name': 'Task 3', 'Status': 'Done'},
505
+ {'Name': 'Task 4', 'Status': 'To Do'},
506
+ {'Name': 'Task 5', 'Status': 'Review'}
507
+ ]);
508
+
509
+ console.log(`Created ${records.length} records`);
510
+
511
+ return records;
512
+ } catch (err) {
513
+ console.error(err);
514
+ }
515
+ }
516
+
517
+ // With typecast
518
+ const recordsWithTypecast = await table.create([
519
+ {'Name': 'Task 1', 'Count': '10'},
520
+ {'Name': 'Task 2', 'Count': '20'}
521
+ ], {
522
+ typecast: true
523
+ });
524
+ ```
525
+
526
+ ### Updating Records
527
+
528
+ #### Update Single Record (Partial Update)
529
+
530
+ ```javascript
531
+ const table = base('Tasks');
532
+
533
+ // Callback style
534
+ table.update('recXXXXXXXXXXXXXX', {
535
+ 'Status': 'In Progress',
536
+ 'Notes': 'Updated notes'
537
+ }, function(err, record) {
538
+ if (err) {
539
+ console.error(err);
540
+ return;
541
+ }
542
+ console.log('Updated record:', record.id);
543
+ });
544
+
545
+ // Promise style
546
+ table.update('recXXXXXXXXXXXXXX', {
547
+ 'Status': 'Done'
548
+ }).then(record => {
549
+ console.log('Updated:', record.get('Status'));
550
+ }).catch(err => {
551
+ console.error(err);
552
+ });
553
+
554
+ // Async/await style
555
+ async function updateTask(recordId) {
556
+ try {
557
+ const record = await table.update(recordId, {
558
+ 'Status': 'In Progress',
559
+ 'Progress': 50
560
+ });
561
+
562
+ console.log('Updated record:', record.id);
563
+ console.log('New status:', record.get('Status'));
564
+
565
+ return record;
566
+ } catch (err) {
567
+ console.error(err);
568
+ }
569
+ }
570
+ ```
571
+
572
+ #### Replace Record (Full Update)
573
+
574
+ The `replace()` method replaces all fields (fields not included will be cleared):
575
+
576
+ ```javascript
577
+ const table = base('Tasks');
578
+
579
+ // Replace clears fields not specified
580
+ const record = await table.replace('recXXXXXXXXXXXXXX', {
581
+ 'Name': 'Completely New Task',
582
+ 'Status': 'To Do'
583
+ // All other fields will be cleared
584
+ });
585
+ ```
586
+
587
+ #### Batch Update (Multiple Records)
588
+
589
+ ```javascript
590
+ const table = base('Tasks');
591
+
592
+ // Update up to 10 records at once
593
+ const records = await table.update([
594
+ {
595
+ id: 'recXXXXXXXXXXXXXX',
596
+ fields: {
597
+ 'Status': 'Done'
598
+ }
599
+ },
600
+ {
601
+ id: 'recYYYYYYYYYYYYYY',
602
+ fields: {
603
+ 'Status': 'In Progress',
604
+ 'Progress': 75
605
+ }
606
+ }
607
+ ]);
608
+
609
+ console.log(`Updated ${records.length} records`);
610
+
611
+ // With typecast
612
+ const updatedWithTypecast = await table.update([
613
+ {
614
+ id: 'recXXXXXXXXXXXXXX',
615
+ fields: {'Count': '100'}
616
+ }
617
+ ], {
618
+ typecast: true
619
+ });
620
+ ```
621
+
622
+ #### Batch Replace (Multiple Records)
623
+
624
+ ```javascript
625
+ const table = base('Tasks');
626
+
627
+ // Replace up to 10 records at once
628
+ const records = await table.replace([
629
+ {
630
+ id: 'recXXXXXXXXXXXXXX',
631
+ fields: {
632
+ 'Name': 'New Name',
633
+ 'Status': 'To Do'
634
+ }
635
+ },
636
+ {
637
+ id: 'recYYYYYYYYYYYYYY',
638
+ fields: {
639
+ 'Name': 'Another Task',
640
+ 'Status': 'Done'
641
+ }
642
+ }
643
+ ]);
644
+
645
+ // With typecast
646
+ const replacedWithTypecast = await table.replace([
647
+ {
648
+ id: 'recXXXXXXXXXXXXXX',
649
+ fields: {'Name': 'Task', 'Count': '50'}
650
+ }
651
+ ], {
652
+ typecast: true
653
+ });
654
+ ```
655
+
656
+ ### Deleting Records
657
+
658
+ #### Delete Single Record
659
+
660
+ ```javascript
661
+ const table = base('Tasks');
662
+
663
+ // Callback style
664
+ table.destroy('recXXXXXXXXXXXXXX', function(err, deletedRecord) {
665
+ if (err) {
666
+ console.error(err);
667
+ return;
668
+ }
669
+ console.log('Deleted record:', deletedRecord.id);
670
+ });
671
+
672
+ // Promise style
673
+ table.destroy('recXXXXXXXXXXXXXX')
674
+ .then(deletedRecord => {
675
+ console.log('Deleted:', deletedRecord.id);
676
+ })
677
+ .catch(err => {
678
+ console.error(err);
679
+ });
680
+
681
+ // Async/await style
682
+ async function deleteTask(recordId) {
683
+ try {
684
+ const deletedRecord = await table.destroy(recordId);
685
+ console.log('Deleted record:', deletedRecord.id);
686
+
687
+ return deletedRecord;
688
+ } catch (err) {
689
+ console.error(err);
690
+ }
691
+ }
692
+ ```
693
+
694
+ #### Batch Delete (Multiple Records)
695
+
696
+ ```javascript
697
+ const table = base('Tasks');
698
+
699
+ // Delete up to 10 records at once
700
+ // Callback style
701
+ table.destroy([
702
+ 'recXXXXXXXXXXXXXX',
703
+ 'recYYYYYYYYYYYYYY',
704
+ 'recZZZZZZZZZZZZZZ'
705
+ ], function(err, deletedRecords) {
706
+ if (err) {
707
+ console.error(err);
708
+ return;
709
+ }
710
+ console.log('Deleted', deletedRecords.length, 'records');
711
+ });
712
+
713
+ // Async/await style
714
+ async function batchDelete(recordIds) {
715
+ try {
716
+ const deletedRecords = await table.destroy(recordIds);
717
+ console.log(`Deleted ${deletedRecords.length} records`);
718
+
719
+ deletedRecords.forEach(record => {
720
+ console.log('Deleted:', record.id);
721
+ });
722
+
723
+ return deletedRecords;
724
+ } catch (err) {
725
+ console.error(err);
726
+ }
727
+ }
728
+
729
+ // Example usage
730
+ await batchDelete([
731
+ 'recXXXXXXXXXXXXXX',
732
+ 'recYYYYYYYYYYYYYY'
733
+ ]);
734
+ ```
735
+
736
+ ### Working with Different Field Types
737
+
738
+ #### Text Fields
739
+
740
+ ```javascript
741
+ const record = await table.create({
742
+ 'Single Line Text': 'Short text',
743
+ 'Long Text': 'This is a much longer text\nwith multiple lines',
744
+ 'Email': 'user@example.com',
745
+ 'URL': 'https://example.com',
746
+ 'Phone': '+1-555-0100'
747
+ });
748
+ ```
749
+
750
+ #### Number Fields
751
+
752
+ ```javascript
753
+ const record = await table.create({
754
+ 'Number': 42,
755
+ 'Currency': 99.99,
756
+ 'Percent': 0.75,
757
+ 'Rating': 5
758
+ });
759
+ ```
760
+
761
+ #### Date and Time Fields
762
+
763
+ ```javascript
764
+ const record = await table.create({
765
+ 'Date': '2025-10-25',
766
+ 'DateTime': '2025-10-25T14:30:00.000Z'
767
+ });
768
+
769
+ // Access date fields
770
+ const dateValue = record.get('Date');
771
+ console.log('Date:', dateValue);
772
+ ```
773
+
774
+ #### Checkbox (Boolean) Fields
775
+
776
+ ```javascript
777
+ const record = await table.create({
778
+ 'Completed': true,
779
+ 'Active': false
780
+ });
781
+
782
+ // Access checkbox
783
+ const isCompleted = record.get('Completed');
784
+ if (isCompleted) {
785
+ console.log('Task is completed');
786
+ }
787
+ ```
788
+
789
+ #### Single Select Fields
790
+
791
+ ```javascript
792
+ const record = await table.create({
793
+ 'Status': 'In Progress', // Must match exact option name
794
+ 'Priority': 'High'
795
+ });
796
+ ```
797
+
798
+ #### Multiple Select Fields
799
+
800
+ ```javascript
801
+ const record = await table.create({
802
+ 'Tags': ['Important', 'Urgent', 'Client Work']
803
+ });
804
+
805
+ // Access multiple select
806
+ const tags = record.get('Tags');
807
+ console.log('Tags:', tags.join(', '));
808
+ ```
809
+
810
+ #### Attachment Fields
811
+
812
+ ```javascript
813
+ const record = await table.create({
814
+ 'Attachments': [
815
+ {
816
+ url: 'https://example.com/image.jpg'
817
+ },
818
+ {
819
+ url: 'https://example.com/document.pdf'
820
+ }
821
+ ]
822
+ });
823
+
824
+ // Access attachments
825
+ const attachments = record.get('Attachments');
826
+ attachments.forEach(attachment => {
827
+ console.log('File:', attachment.filename);
828
+ console.log('URL:', attachment.url);
829
+ console.log('Size:', attachment.size);
830
+ console.log('Type:', attachment.type);
831
+ });
832
+ ```
833
+
834
+ #### Linked Record Fields
835
+
836
+ ```javascript
837
+ // Link to existing records by their IDs
838
+ const record = await table.create({
839
+ 'Linked Records': [
840
+ 'recXXXXXXXXXXXXXX',
841
+ 'recYYYYYYYYYYYYYY'
842
+ ]
843
+ });
844
+
845
+ // Access linked records
846
+ const linkedRecords = record.get('Linked Records');
847
+ console.log('Linked record IDs:', linkedRecords);
848
+ ```
849
+
850
+ #### Collaborator Fields
851
+
852
+ ```javascript
853
+ const record = await table.create({
854
+ 'Assignee': {
855
+ id: 'usrXXXXXXXXXXXXXX',
856
+ email: 'user@example.com'
857
+ },
858
+ 'Collaborators': [
859
+ {id: 'usrXXXXXXXXXXXXXX'},
860
+ {id: 'usrYYYYYYYYYYYYYY'}
861
+ ]
862
+ });
863
+ ```
864
+
865
+ ## Complete Examples
866
+
867
+ ### Example 1: Task Management System
868
+
869
+ ```javascript
870
+ const Airtable = require('airtable');
871
+
872
+ Airtable.configure({
873
+ apiKey: process.env.AIRTABLE_API_KEY
874
+ });
875
+
876
+ const base = Airtable.base('appTaskManager');
877
+ const tasksTable = base('Tasks');
878
+
879
+ // Create a new task
880
+ async function createTask(taskData) {
881
+ try {
882
+ const record = await tasksTable.create({
883
+ 'Name': taskData.name,
884
+ 'Description': taskData.description,
885
+ 'Status': 'To Do',
886
+ 'Priority': taskData.priority || 'Medium',
887
+ 'Assignee': taskData.assignee,
888
+ 'Due Date': taskData.dueDate
889
+ });
890
+
891
+ console.log('Created task:', record.id);
892
+ return record;
893
+ } catch (err) {
894
+ console.error('Error creating task:', err);
895
+ throw err;
896
+ }
897
+ }
898
+
899
+ // Get all active tasks
900
+ async function getActiveTasks() {
901
+ try {
902
+ const records = await tasksTable.select({
903
+ filterByFormula: "AND({Status} != 'Done', {Status} != 'Cancelled')",
904
+ sort: [
905
+ {field: 'Priority', direction: 'desc'},
906
+ {field: 'Due Date', direction: 'asc'}
907
+ ]
908
+ }).all();
909
+
910
+ return records.map(record => ({
911
+ id: record.id,
912
+ name: record.get('Name'),
913
+ status: record.get('Status'),
914
+ priority: record.get('Priority'),
915
+ assignee: record.get('Assignee'),
916
+ dueDate: record.get('Due Date')
917
+ }));
918
+ } catch (err) {
919
+ console.error('Error fetching tasks:', err);
920
+ throw err;
921
+ }
922
+ }
923
+
924
+ // Update task status
925
+ async function updateTaskStatus(taskId, newStatus) {
926
+ try {
927
+ const record = await tasksTable.update(taskId, {
928
+ 'Status': newStatus,
929
+ 'Last Modified': new Date().toISOString()
930
+ });
931
+
932
+ console.log('Updated task:', record.id);
933
+ return record;
934
+ } catch (err) {
935
+ console.error('Error updating task:', err);
936
+ throw err;
937
+ }
938
+ }
939
+
940
+ // Get overdue tasks
941
+ async function getOverdueTasks() {
942
+ try {
943
+ const today = new Date().toISOString().split('T')[0];
944
+
945
+ const records = await tasksTable.select({
946
+ filterByFormula: `AND(
947
+ {Status} != 'Done',
948
+ {Due Date} < '${today}'
949
+ )`,
950
+ sort: [{field: 'Due Date', direction: 'asc'}]
951
+ }).all();
952
+
953
+ return records;
954
+ } catch (err) {
955
+ console.error('Error fetching overdue tasks:', err);
956
+ throw err;
957
+ }
958
+ }
959
+
960
+ // Bulk update multiple tasks
961
+ async function bulkUpdateStatus(taskIds, newStatus) {
962
+ try {
963
+ const updates = taskIds.map(id => ({
964
+ id: id,
965
+ fields: {
966
+ 'Status': newStatus,
967
+ 'Last Modified': new Date().toISOString()
968
+ }
969
+ }));
970
+
971
+ // Process in batches of 10
972
+ const results = [];
973
+ for (let i = 0; i < updates.length; i += 10) {
974
+ const batch = updates.slice(i, i + 10);
975
+ const updated = await tasksTable.update(batch);
976
+ results.push(...updated);
977
+ }
978
+
979
+ console.log(`Updated ${results.length} tasks`);
980
+ return results;
981
+ } catch (err) {
982
+ console.error('Error bulk updating:', err);
983
+ throw err;
984
+ }
985
+ }
986
+
987
+ // Delete completed tasks older than 30 days
988
+ async function deleteOldCompletedTasks() {
989
+ try {
990
+ const thirtyDaysAgo = new Date();
991
+ thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
992
+ const cutoffDate = thirtyDaysAgo.toISOString().split('T')[0];
993
+
994
+ const records = await tasksTable.select({
995
+ filterByFormula: `AND(
996
+ {Status} = 'Done',
997
+ {Completed Date} < '${cutoffDate}'
998
+ )`
999
+ }).all();
1000
+
1001
+ const recordIds = records.map(record => record.id);
1002
+
1003
+ // Delete in batches of 10
1004
+ for (let i = 0; i < recordIds.length; i += 10) {
1005
+ const batch = recordIds.slice(i, i + 10);
1006
+ await tasksTable.destroy(batch);
1007
+ }
1008
+
1009
+ console.log(`Deleted ${recordIds.length} old completed tasks`);
1010
+ } catch (err) {
1011
+ console.error('Error deleting old tasks:', err);
1012
+ throw err;
1013
+ }
1014
+ }
1015
+
1016
+ // Export module
1017
+ module.exports = {
1018
+ createTask,
1019
+ getActiveTasks,
1020
+ updateTaskStatus,
1021
+ getOverdueTasks,
1022
+ bulkUpdateStatus,
1023
+ deleteOldCompletedTasks
1024
+ };
1025
+ ```
1026
+
1027
+ ### Example 2: Contact Management with Error Handling
1028
+
1029
+ ```javascript
1030
+ const Airtable = require('airtable');
1031
+
1032
+ const airtable = new Airtable({
1033
+ apiKey: process.env.AIRTABLE_API_KEY
1034
+ });
1035
+
1036
+ const base = airtable.base('appContactManager');
1037
+ const contactsTable = base('Contacts');
1038
+
1039
+ // Find contact by email
1040
+ async function findContactByEmail(email) {
1041
+ try {
1042
+ const records = await contactsTable.select({
1043
+ filterByFormula: `{Email} = "${email}"`,
1044
+ maxRecords: 1
1045
+ }).firstPage();
1046
+
1047
+ return records.length > 0 ? records[0] : null;
1048
+ } catch (err) {
1049
+ console.error(`Error finding contact with email ${email}:`, err);
1050
+ throw err;
1051
+ }
1052
+ }
1053
+
1054
+ // Create or update contact (upsert pattern)
1055
+ async function upsertContact(contactData) {
1056
+ try {
1057
+ // First, try to find existing contact
1058
+ const existingContact = await findContactByEmail(contactData.email);
1059
+
1060
+ if (existingContact) {
1061
+ // Update existing contact
1062
+ const updated = await contactsTable.update(existingContact.id, {
1063
+ 'First Name': contactData.firstName,
1064
+ 'Last Name': contactData.lastName,
1065
+ 'Phone': contactData.phone,
1066
+ 'Company': contactData.company,
1067
+ 'Last Updated': new Date().toISOString()
1068
+ });
1069
+
1070
+ console.log('Updated existing contact:', updated.id);
1071
+ return {record: updated, action: 'updated'};
1072
+ } else {
1073
+ // Create new contact
1074
+ const created = await contactsTable.create({
1075
+ 'First Name': contactData.firstName,
1076
+ 'Last Name': contactData.lastName,
1077
+ 'Email': contactData.email,
1078
+ 'Phone': contactData.phone,
1079
+ 'Company': contactData.company,
1080
+ 'Created': new Date().toISOString()
1081
+ });
1082
+
1083
+ console.log('Created new contact:', created.id);
1084
+ return {record: created, action: 'created'};
1085
+ }
1086
+ } catch (err) {
1087
+ console.error('Error upserting contact:', err);
1088
+ throw err;
1089
+ }
1090
+ }
1091
+
1092
+ // Get all contacts from a company
1093
+ async function getContactsByCompany(companyName) {
1094
+ try {
1095
+ const records = await contactsTable.select({
1096
+ filterByFormula: `{Company} = "${companyName}"`,
1097
+ sort: [
1098
+ {field: 'Last Name', direction: 'asc'},
1099
+ {field: 'First Name', direction: 'asc'}
1100
+ ]
1101
+ }).all();
1102
+
1103
+ return records.map(record => ({
1104
+ id: record.id,
1105
+ name: `${record.get('First Name')} ${record.get('Last Name')}`,
1106
+ email: record.get('Email'),
1107
+ phone: record.get('Phone')
1108
+ }));
1109
+ } catch (err) {
1110
+ console.error(`Error fetching contacts for ${companyName}:`, err);
1111
+ throw err;
1112
+ }
1113
+ }
1114
+
1115
+ // Export contacts to array
1116
+ async function exportAllContacts() {
1117
+ try {
1118
+ const allContacts = [];
1119
+
1120
+ await contactsTable.select().eachPage(
1121
+ function page(records, fetchNextPage) {
1122
+ records.forEach(record => {
1123
+ allContacts.push({
1124
+ id: record.id,
1125
+ firstName: record.get('First Name'),
1126
+ lastName: record.get('Last Name'),
1127
+ email: record.get('Email'),
1128
+ phone: record.get('Phone'),
1129
+ company: record.get('Company'),
1130
+ created: record.get('Created')
1131
+ });
1132
+ });
1133
+
1134
+ fetchNextPage();
1135
+ },
1136
+ function done(err) {
1137
+ if (err) {
1138
+ console.error('Error during pagination:', err);
1139
+ throw err;
1140
+ }
1141
+ }
1142
+ );
1143
+
1144
+ return allContacts;
1145
+ } catch (err) {
1146
+ console.error('Error exporting contacts:', err);
1147
+ throw err;
1148
+ }
1149
+ }
1150
+
1151
+ module.exports = {
1152
+ findContactByEmail,
1153
+ upsertContact,
1154
+ getContactsByCompany,
1155
+ exportAllContacts
1156
+ };
1157
+ ```
1158
+
1159
+ ### Example 3: E-commerce Inventory Management
1160
+
1161
+ ```javascript
1162
+ const Airtable = require('airtable');
1163
+ const base = Airtable.base('appInventory');
1164
+
1165
+ const productsTable = base('Products');
1166
+ const ordersTable = base('Orders');
1167
+
1168
+ // Check product availability
1169
+ async function checkInventory(productId) {
1170
+ try {
1171
+ const product = await productsTable.find(productId);
1172
+
1173
+ return {
1174
+ id: product.id,
1175
+ name: product.get('Name'),
1176
+ sku: product.get('SKU'),
1177
+ quantity: product.get('Quantity in Stock'),
1178
+ available: product.get('Quantity in Stock') > 0
1179
+ };
1180
+ } catch (err) {
1181
+ console.error(`Error checking inventory for ${productId}:`, err);
1182
+ throw err;
1183
+ }
1184
+ }
1185
+
1186
+ // Update stock after purchase
1187
+ async function updateStockQuantity(productId, quantityChange) {
1188
+ try {
1189
+ const product = await productsTable.find(productId);
1190
+ const currentStock = product.get('Quantity in Stock');
1191
+ const newStock = currentStock + quantityChange;
1192
+
1193
+ if (newStock < 0) {
1194
+ throw new Error('Insufficient stock');
1195
+ }
1196
+
1197
+ const updated = await productsTable.update(productId, {
1198
+ 'Quantity in Stock': newStock,
1199
+ 'Last Updated': new Date().toISOString()
1200
+ });
1201
+
1202
+ console.log(`Updated stock for ${product.get('Name')}: ${currentStock} -> ${newStock}`);
1203
+ return updated;
1204
+ } catch (err) {
1205
+ console.error(`Error updating stock for ${productId}:`, err);
1206
+ throw err;
1207
+ }
1208
+ }
1209
+
1210
+ // Get low stock products
1211
+ async function getLowStockProducts(threshold = 10) {
1212
+ try {
1213
+ const records = await productsTable.select({
1214
+ filterByFormula: `{Quantity in Stock} < ${threshold}`,
1215
+ sort: [{field: 'Quantity in Stock', direction: 'asc'}]
1216
+ }).all();
1217
+
1218
+ return records.map(record => ({
1219
+ id: record.id,
1220
+ name: record.get('Name'),
1221
+ sku: record.get('SKU'),
1222
+ quantity: record.get('Quantity in Stock'),
1223
+ reorderLevel: record.get('Reorder Level')
1224
+ }));
1225
+ } catch (err) {
1226
+ console.error('Error fetching low stock products:', err);
1227
+ throw err;
1228
+ }
1229
+ }
1230
+
1231
+ // Create order with multiple line items
1232
+ async function createOrder(orderData) {
1233
+ try {
1234
+ // Create order record
1235
+ const order = await ordersTable.create({
1236
+ 'Order Number': orderData.orderNumber,
1237
+ 'Customer Name': orderData.customerName,
1238
+ 'Customer Email': orderData.customerEmail,
1239
+ 'Status': 'Pending',
1240
+ 'Total Amount': orderData.totalAmount,
1241
+ 'Order Date': new Date().toISOString()
1242
+ });
1243
+
1244
+ // Update inventory for each product
1245
+ for (const item of orderData.items) {
1246
+ await updateStockQuantity(item.productId, -item.quantity);
1247
+ }
1248
+
1249
+ console.log('Created order:', order.id);
1250
+ return order;
1251
+ } catch (err) {
1252
+ console.error('Error creating order:', err);
1253
+ throw err;
1254
+ }
1255
+ }
1256
+
1257
+ // Get orders by status
1258
+ async function getOrdersByStatus(status) {
1259
+ try {
1260
+ const records = await ordersTable.select({
1261
+ filterByFormula: `{Status} = "${status}"`,
1262
+ sort: [{field: 'Order Date', direction: 'desc'}]
1263
+ }).all();
1264
+
1265
+ return records.map(record => ({
1266
+ id: record.id,
1267
+ orderNumber: record.get('Order Number'),
1268
+ customer: record.get('Customer Name'),
1269
+ total: record.get('Total Amount'),
1270
+ date: record.get('Order Date')
1271
+ }));
1272
+ } catch (err) {
1273
+ console.error(`Error fetching ${status} orders:`, err);
1274
+ throw err;
1275
+ }
1276
+ }
1277
+
1278
+ module.exports = {
1279
+ checkInventory,
1280
+ updateStockQuantity,
1281
+ getLowStockProducts,
1282
+ createOrder,
1283
+ getOrdersByStatus
1284
+ };
1285
+ ```
1286
+
1287
+ ## Rate Limits and Error Handling
1288
+
1289
+ Airtable enforces a rate limit of **5 requests per second per base**.
1290
+
1291
+ ### Handling Rate Limits
1292
+
1293
+ ```javascript
1294
+ async function retryWithBackoff(fn, maxRetries = 3, initialDelay = 1000) {
1295
+ for (let i = 0; i < maxRetries; i++) {
1296
+ try {
1297
+ return await fn();
1298
+ } catch (err) {
1299
+ if (err.statusCode === 429 && i < maxRetries - 1) {
1300
+ const delay = initialDelay * Math.pow(2, i);
1301
+ console.log(`Rate limited. Retrying in ${delay}ms...`);
1302
+ await new Promise(resolve => setTimeout(resolve, delay));
1303
+ } else {
1304
+ throw err;
1305
+ }
1306
+ }
1307
+ }
1308
+ }
1309
+
1310
+ // Usage
1311
+ const record = await retryWithBackoff(() =>
1312
+ table.create({'Name': 'New Record'})
1313
+ );
1314
+ ```
1315
+
1316
+ ### Error Handling Patterns
1317
+
1318
+ ```javascript
1319
+ const table = base('Tasks');
1320
+
1321
+ // Basic error handling
1322
+ try {
1323
+ const record = await table.find('recXXXXXXXXXXXXXX');
1324
+ } catch (err) {
1325
+ if (err.statusCode === 404) {
1326
+ console.error('Record not found');
1327
+ } else if (err.statusCode === 401) {
1328
+ console.error('Authentication failed');
1329
+ } else if (err.statusCode === 429) {
1330
+ console.error('Rate limit exceeded');
1331
+ } else {
1332
+ console.error('Unexpected error:', err);
1333
+ }
1334
+ }
1335
+
1336
+ // Comprehensive error handling
1337
+ async function safeCreate(recordData) {
1338
+ try {
1339
+ const record = await table.create(recordData);
1340
+ return {success: true, record};
1341
+ } catch (err) {
1342
+ console.error('Error creating record:', err.message);
1343
+
1344
+ return {
1345
+ success: false,
1346
+ error: {
1347
+ message: err.message,
1348
+ statusCode: err.statusCode,
1349
+ type: err.error
1350
+ }
1351
+ };
1352
+ }
1353
+ }
1354
+ ```
1355
+
1356
+ ## TypeScript Support
1357
+
1358
+ ```typescript
1359
+ import Airtable, { FieldSet, Records } from 'airtable';
1360
+
1361
+ interface Task extends FieldSet {
1362
+ 'Name': string;
1363
+ 'Status': 'To Do' | 'In Progress' | 'Done';
1364
+ 'Priority': 'Low' | 'Medium' | 'High';
1365
+ 'Due Date': string;
1366
+ 'Assignee': string;
1367
+ }
1368
+
1369
+ Airtable.configure({
1370
+ apiKey: process.env.AIRTABLE_API_KEY!
1371
+ });
1372
+
1373
+ const base = Airtable.base('appYourBaseId');
1374
+ const tasksTable = base<Task>('Tasks');
1375
+
1376
+ async function getHighPriorityTasks(): Promise<Records<Task>> {
1377
+ const records = await tasksTable.select({
1378
+ filterByFormula: "{Priority} = 'High'",
1379
+ sort: [{field: 'Due Date', direction: 'asc'}]
1380
+ }).all();
1381
+
1382
+ return records;
1383
+ }
1384
+
1385
+ async function createTask(taskData: Partial<Task>) {
1386
+ const record = await tasksTable.create(taskData);
1387
+ return record;
1388
+ }
1389
+ ```
1390
+
1391
+ ## Common Formulas Reference
1392
+
1393
+ ```javascript
1394
+ // Exact match
1395
+ "{Status} = 'Active'"
1396
+
1397
+ // Not equal
1398
+ "{Status} != 'Done'"
1399
+
1400
+ // Greater than / Less than
1401
+ "{Price} > 100"
1402
+ "{Stock} <= 10"
1403
+
1404
+ // String contains (case-sensitive)
1405
+ "FIND('urgent', {Notes}) > 0"
1406
+
1407
+ // String contains (case-insensitive)
1408
+ "SEARCH('urgent', LOWER({Notes})) > 0"
1409
+
1410
+ // Is empty
1411
+ "{Email} = ''"
1412
+ "OR({Email} = BLANK())"
1413
+
1414
+ // Is not empty
1415
+ "NOT({Email} = '')"
1416
+ "{Email} != ''"
1417
+
1418
+ // AND condition
1419
+ "AND({Status} = 'Active', {Priority} = 'High')"
1420
+
1421
+ // OR condition
1422
+ "OR({Status} = 'Urgent', {Priority} = 'High')"
1423
+
1424
+ // Date comparisons
1425
+ "{Created} > '2025-01-01'"
1426
+ "{Due Date} < TODAY()"
1427
+ "{Modified} >= DATEADD(TODAY(), -7, 'days')"
1428
+
1429
+ // Multiple conditions
1430
+ "AND({Status} = 'Active', OR({Priority} = 'High', {Due Date} < TODAY()))"
1431
+
1432
+ // Check if field is in a list
1433
+ "OR({Status} = 'Active', {Status} = 'In Progress', {Status} = 'Review')"
1434
+
1435
+ // Numeric range
1436
+ "AND({Price} >= 10, {Price} <= 100)"
1437
+ ```