| 
									
										
										
										
											2023-02-15 06:00:28 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | title: Airtable | 
					
						
							| 
									
										
										
										
											2023-02-28 11:40:44 +00:00
										 |  |  | pagination_prev: demos/local/index | 
					
						
							|  |  |  | pagination_next: demos/extensions/index | 
					
						
							| 
									
										
										
										
											2023-02-15 06:00:28 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-27 09:12:19 +00:00
										 |  |  | import current from '/version.js'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | Airtable recommends Personal Access Tokens for interacting with their API. When | 
					
						
							|  |  |  | fetching data from the API, the result will include an array of row objects that | 
					
						
							|  |  |  | can be converted to a worksheet with `XLSX.utils.json_to_sheet`. The API methods | 
					
						
							|  |  |  | to write data will accept row objects generated by `XLSX.utils.sheet_to_json`. | 
					
						
							| 
									
										
										
										
											2023-02-15 06:00:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ## NodeJS Integration
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The main module is `airtable` and can be installed with `npm`: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-27 09:12:19 +00:00
										 |  |  | <pre><code parentName="pre" {...{"className": "language-bash"}}>{`\ | 
					
						
							|  |  |  | npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz airtable`} | 
					
						
							|  |  |  | </code></pre> | 
					
						
							| 
									
										
										
										
											2023-02-15 06:00:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | To obtain a reference to a table, code needs a [PAT](#personal-access-token), | 
					
						
							|  |  |  | the name of the workspace (typically starting with `app`), and the name of the | 
					
						
							|  |  |  | desired table (the Excel import typically supports the worksheet name): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | const Airtable = require("airtable"), XLSX = require("xlsx"); | 
					
						
							|  |  |  | /* query all records in a table */ | 
					
						
							|  |  |  | const conn = new Airtable({apiKey: "PAT...", /* and other options ... */}); | 
					
						
							|  |  |  | const table = conn.base("app...").table("tablename..."); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Exporting Data
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | When querying data, a result set will be a simple array of Record objects. The | 
					
						
							|  |  |  | `fields` property is a simple JS object compatible with `json_to_sheet`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | /** Create SheetJS worksheet from Airtable table */ | 
					
						
							|  |  |  | async function airtable_to_worksheet(table) { | 
					
						
							|  |  |  |   /* get all rows */ | 
					
						
							|  |  |  |   const result = await table.select().all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* pull raw objects from the result */ | 
					
						
							|  |  |  |   // highlight-next-line | 
					
						
							|  |  |  |   const aoo = result.map(r => r.fields); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* create a worksheet */ | 
					
						
							|  |  |  |   const worksheet = XLSX.utils.json_to_sheet(aoo); | 
					
						
							|  |  |  |   return worksheet; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :::caution | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The results are not guaranteed to be sorted.  The official API includes options | 
					
						
							|  |  |  | for sorting by fields. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Importing Data
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | When inserting records, each object should be wrapped in a parent object with a | 
					
						
							|  |  |  | single `fields` property: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | /** Append records from a SheetJS worksheet to Airtable table */ | 
					
						
							|  |  |  | async function airtable_load_worksheet(table, worksheet) { | 
					
						
							|  |  |  |   /* suppose the field names */ | 
					
						
							|  |  |  |   const aoo = XLSX.utils.sheet_to_json(worksheet); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* reshape to be compatible with Airtable API */ | 
					
						
							|  |  |  |   // highlight-next-line | 
					
						
							|  |  |  |   const airtable_rows = aoo.map(fields => ({ fields })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* upload data */ | 
					
						
							|  |  |  |   return await table.create(airtable_rows); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Complete Example
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | :::note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This demo was last tested on 2023 February 15.  At the time, it was possible to | 
					
						
							|  |  |  | create a free account with API access. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 06:00:28 +00:00
										 |  |  | 0) Create a free Airtable account. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Personal Access Token
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :::note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | In the past, Airtable offered API keys.  They are slated to deprecate API keys | 
					
						
							|  |  |  | in January 2024. They recommend "Personal Access Tokens" for operations. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | API actions will require a PAT, which must be created through the developer hub: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 1) Click on account icon (topright area of the page) and select "Developer Hub". | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 2) Click "Create Token". | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 3) In the form, make the following selections: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - Name: enter any name (for example, "SheetJS Test") | 
					
						
							|  |  |  | - Scopes: `data.records:read` and `data.records:write` (adding 2 scopes) | 
					
						
							|  |  |  | - Access: "All current and future bases in all current and future workspaces" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The form will look like the screenshot below: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 4) Click "Create Token" and you will see a popup.  Copy the token and save it. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Workspace
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For the purposes of this demo, a sample workspace should be created: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 5) Download <https://sheetjs.com/pres.xlsx> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 6) Create a project in Airtable using "Quickly upload". Select "Microsoft Excel" | 
					
						
							|  |  |  | and select the downloaded file from step 1.  Click "Upload", then "Import". | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 7) A workspace will be created. The name will be found in the URL. For example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | https://airtable.com/appblahblah/tblblahblah/blahblah | 
					
						
							|  |  |  | --------------------/^^^^^^^^^^^/ workspace name | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | the first part after the `.com` will be the workspace name. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Exporting Data
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 8) Save the following to `read.js`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js title="read.js" | 
					
						
							|  |  |  | const Airtable = require("airtable"), XLSX = require("xlsx"); | 
					
						
							|  |  |  | // highlight-start | 
					
						
							|  |  |  | /* replace the value with the personal access token */ | 
					
						
							|  |  |  | const apiKey = "pat..."; | 
					
						
							|  |  |  | /* replace the value with the workspace name */ | 
					
						
							|  |  |  | const base = "app..."; | 
					
						
							|  |  |  | // highlight-end | 
					
						
							|  |  |  | (async() => { | 
					
						
							|  |  |  |   const conn = new Airtable({ apiKey }); | 
					
						
							|  |  |  |   const table = conn.base(base).table("Sheet1"); | 
					
						
							|  |  |  |   const result = await table.select().all(); | 
					
						
							|  |  |  |   const aoo = result.map(r => r.fields); | 
					
						
							|  |  |  |   const ws = XLSX.utils.json_to_sheet(aoo); | 
					
						
							|  |  |  |   const wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  |   XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); | 
					
						
							|  |  |  |   XLSX.writeFile(wb, "SheetJSAirtable.xlsb"); | 
					
						
							|  |  |  | })(); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 9) Replace the values in the highlighted lines with the PAT and workspace name. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 10) Run `node read.js`.  The script should write `SheetJSAirtable.xlsb`. The file | 
					
						
							|  |  |  | can be opened in Excel. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Importing Data
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 11) Create a file `SheetJSAirpend.xlsx` with some new records in sheet `Sheet1`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | `npx xlsx-cli SheetJSAirpend.xlsx` should print the following data: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```csv | 
					
						
							|  |  |  | Sheet1 | 
					
						
							|  |  |  | Name,Index | 
					
						
							|  |  |  | Someone Else,47 | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 12) Save the following to `write.js`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js title="write.js" | 
					
						
							|  |  |  | const Airtable = require("airtable"), XLSX = require("xlsx"); | 
					
						
							|  |  |  | // highlight-start | 
					
						
							|  |  |  | /* replace the value with the personal access token */ | 
					
						
							|  |  |  | const apiKey = "pat..."; | 
					
						
							|  |  |  | /* replace the value with the workspace name */ | 
					
						
							|  |  |  | const base = "app..."; | 
					
						
							|  |  |  | // highlight-end | 
					
						
							|  |  |  | (async() => { | 
					
						
							|  |  |  |   const conn = new Airtable({ apiKey }); | 
					
						
							|  |  |  |   const table = conn.base(base).table("Sheet1"); | 
					
						
							|  |  |  |   const wb = XLSX.readFile("SheetJSAirpend.xlsx"); | 
					
						
							|  |  |  |   const ws = wb.Sheets["Sheet1"]; | 
					
						
							|  |  |  |   const aoo = XLSX.utils.sheet_to_json(ws); | 
					
						
							|  |  |  |   await table.create(aoo.map(fields => ({ fields }))); | 
					
						
							|  |  |  | })(); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 13) Replace the values in the highlighted lines with the PAT and workspace name. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 14) Run `node write.js`.  Open Airtable and verify the new row: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  |