Write back to Airtable
Learn how to create, update and delete records in your base and learn about permissions and asynchronous updates.
Getting started
Three operations are supported, in single and batch formats:
const base = useBase();const table = base.getTableByName('My writable table');const records = useRecords(table.selectRecords());// Update cell valuestable.updateRecordAsync(record, {'My field name': 'updated value'});table.updateRecordsAsync([{id: record1.id, fields: {'My field name': 'updated value 1'}},{id: record2.id, fields: {'My field name': 'updated value 2'}},]);// Create recordstable.createRecordAsync();table.createRecordAsync({'My field name': 'new value'});table.createRecordsAsync([{fields: {'My field name': '1'}},{fields: {'My field name': '2'}},{fields: {'My field name': '3'}},]);// Delete recordstable.deleteRecordAsync(record);table.deleteRecordsAsync([record1, record2]);
That's all you need to get started! See the API reference for per-function documentation and examples, or read on for more specific details about how writes work. You can also see the Update records example extension and Wikipedia enrichment example extension for full examples of how to use writes in an extension.
These methods allow you to modify the data in your base. To learn more about modifying the base schema (table & field configurations), read Changing base schema.
Permissions
In many cases, a particular write might not be permitted. For example:
- the user of the extension might only have read-only access to the base
- you could be trying to update a cell value in a field that cannot be updated (e.g. formula field)
An error will be thrown if you attempt to perform such a write. Use the
checkPermissionsFor[Action]
helpers to determine whether an action is possible:
function updateRecordIfPossible(record, fieldValue) {const fieldsToUpdate = {'My field name': fieldValue,};const checkResult = table.checkPermissionsForUpdateRecord(record, fieldsToUpdate);if (checkResult.hasPermission) {table.updateRecordAsync(record, fieldsToUpdate);} else {// if the user does not have permission to perform this action, we// provide a message explaining whyalert(`You could not update the record: ${checkResult.reasonDisplayString}`);}}
In situations where you don't have the full specifics of the write yet (e.g. you want to know whether
to enable or disable a record creation interface), these helpers all accept partial inputs. There
are also hasPermissionTo
variants of each permission check that return boolean
values but no
reasonDisplayString
:
function shouldShowRecordCreationButton() {return table.hasPermissionToCreateRecord();}function isFieldValidForEditing(field) {// undefined is used as a placeholder for unknown values (eg record being// edited, new cell value)return table.hasPermissionToUpdateRecord(undefined, {[field.id]: undefined,});}
To make the check as accurate as possible, provide all the information you have available at the time.
When developing an extension, you can preview the extension with different permission levels using the “Advanced” tab.
Asynchronous updates
All write methods are asynchronous: they return a
promise
that resolves when the update has been persisted to Airtable's servers. (You'll notice there's a
Saving...
spinner in the top left corner of the Airtable base while this is happening!)
However, we also optimistically apply updates locally: this means that (assuming use of useRecords
or similar to watch for updates) your changes will be reflected within the extension and within your
base immediately, before the updates are saved to Airtable's servers.
In other words: you'll see the changes immediately, but other users may not have received them yet.
This means that you can choose whether or not to wait for the update to be complete based on your use case. For example, updating cell values may have side effects (e.g. if a formula field relies on it, you need to wait for the update to be complete to get the updated values for the formula field).
The examples in our API documentation use
async/await syntax,
but you can also use
.then
:
async function createRecordWithAsyncAwait(fieldValue) {const recordId = await table.createRecordAsync({'My field name': fieldValue,});alert(`new record created! ID: ${recordId}`);}function createRecordWithCallback(record, fieldValue) {table.createRecordAsync({'My field name': fieldValue}).then(function (recordId) {alert(`new record created! ID: ${recordId}`);});}
Size limits & rate limits
Two size limits apply to writes:
- Batch methods (
updateRecordsAsync
,createRecordsAsync
,deleteRecordsAsync
) may only update/create/delete up to 50 records in one call.- Similarly,
globalConfig.setPathsAsync
can only set 50 paths in one call.
- Similarly,
- Any individual write cannot have a payload (e.g.
fields
content forupdateRecordAsync
) exceeding 1.9MB in size
An error will be thrown if these limits are exceeded.
Additionally, writes are rate-limited (maximum of 15 writes per second). Your extension will crash if this limit is exceeded.
When performing writes to a lot of records, we recommend splitting your updates into batches of 50
and await
ing individual calls to satisfy these limits:
const BATCH_SIZE = 50;async function deleteLotsOfRecords(records) {let i = 0;while (i < records.length) {const recordBatch = records.slice(i, i + BATCH_SIZE);// awaiting the delete means that next batch won't be deleted until the current// batch has been fully deleted, keeping you under the rate limitawait table.deleteRecordsAsync(recordBatch);i += BATCH_SIZE;}}
Special behavior for updating records
For updating records in real time, await
ing each write can appear laggy (for example, an input
field that syncs the user's typing to an Airtable record in real time).
In order to support these interactions, consecutive updateRecordAsync
and updateRecordsAsync
calls to the same table within a short time period are merged into one request. This means that
they won't be rate limited, as they count as one write request.
If the writes are too large to merge (would exceed the 50 record limit or the 1.9MB size limit) they will not be merged, so we recommend only relying on this behavior for small updates.
Updating specific field types
You can find the accepted cell write format for each field type in the API reference for FieldType.
NOTE: When updating array-type fields (attachment, linked record, multiple select, multiple collaborators), you must set a new array of items. Whatever value you set will override the previous value. If you wish to append to the existing values in the cell, you can spread the current cell's items. For example:
updateRecordAsync(recordToUpdate, {'Linked record field': [...recordToUpdate.getCellValue('Linked record field'),{id: 'recABC123xyz'}]});