Extensions

Press shift + S to search API reference.

Guide

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 values
table.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 records
table.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 records
table.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 why
alert(`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.
  • Any individual write cannot have a payload (e.g. fields content for updateRecordAsync) 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 awaiting 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 limit
await table.deleteRecordsAsync(recordBatch);
i += BATCH_SIZE;
}
}

Special behavior for updating records

For updating records in real time, awaiting 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'}
]
});