New: create tables, create fields, and update field options from the Scripting App

We recently added the ability to create tables, create fields, and update field options from the scripting app. The methods to perform each of these actions are the same as in the Blocks SDK.

The field types that are allowed to be created via base.createTableAsync and table.createFieldAsync are:

  • barcode
  • checkbox
  • currency
  • date
  • dateTime
  • duration
  • email
  • multilineText
  • multipleAttachments
  • multipleCollaborators
  • multipleRecordLinks
  • multipleSelects
  • number
  • percent
  • phoneNumber
  • rating
  • richText
  • singleCollaborator
  • singleLineText
  • singleSelect
  • url

The field types which cannot be created are:

  • autoNumber
  • button
  • count
  • createdBy
  • createdTime
  • externalSyncSource
  • formula
  • lastModifiedBy
  • lastModifiedTime
  • multipleLookupValues
  • rollup

These three new methods are currently only supported in the scripting app, not in automation “Run a script” actions.

Finally, here is an example that showcases these methods. The source code can be found below.

61156e2c2cfc7764851315

output.markdown(`# Base Creator`)
 
const addTableOrField = await input.buttonsAsync('Do you want to create a new table or add fields to an existing table?', [
   {label: 'Create new table', value: 'table'},
   {label: 'Add fields to existing table', value: 'fields'},
]);
 
if (addTableOrField === 'table') {
   const tableName = await input.textAsync('Name of table to create');
   output.markdown('Each table needs at least one field. This field will be used as the _primary_ field for the table.')
   const primaryFieldName = await input.textAsync('Name of primary field');
   const primaryFieldType = await selectFieldTypeAsync('Type of primary field');
   let tableId = await base.createTableAsync(tableName, [
       {name: primaryFieldName, type: primaryFieldType, options: getFieldTypeOptions(primaryFieldType)}
   ])
   output.markdown(`**Created table "${tableName}" (${tableId})**`);
 
   const moreFields = await input.buttonsAsync('Would you like to create additional fields now?', ['Yes', 'No']);
   if (moreFields === 'Yes') {
       await createMultipleNewFieldsAsync(tableId);
   }
} else {
   const table = await input.tableAsync('Which table do you want to add fields to?');
   await createMultipleNewFieldsAsync(table.id);
}
 
/**
* @param {string} label
*/
async function selectFieldTypeAsync(label) {
   return input.buttonsAsync(label, [
       // There are more field types beyond these!
       {label: 'Text', value: 'singleLineText'},
       {label: 'Multiline Text', value: 'multilineText'},
       {label: 'Number', value: 'number'},
       {label: 'Email', value: 'email'},
       {label: 'URL', value: 'url'},
   ]);
}
 
/**
* @param {Field['type']} fieldType
*/
function getFieldTypeOptions(fieldType) {
   switch (fieldType) {
       case 'singleLineText':
       case 'multilineText':
       case 'email':
       case 'url':
           return null;
       case 'number':
           return {precision: 5}
       default:
           throw new Error(`Unexpected field type ${fieldType}`)
   }
}
 
/**
* @param {string} tableId
*/
async function createMultipleNewFieldsAsync(tableId) {
   const table = base.getTable(tableId)
   let numFields;
   while (numFields === undefined) {
       numFields = parseInt(await input.textAsync('How many new fields?'));
       if (isNaN(numFields)) {
           output.text('Please enter a number.');
           numFields = undefined;
       }
   }
 
   for (let i = 0; i < numFields; i++) {
       const fieldName = await input.textAsync(`Name of additional field ${i+1}/${numFields}`);
       const fieldType = await selectFieldTypeAsync('Type of field');
       const fieldId = await table.createFieldAsync(fieldName, fieldType, getFieldTypeOptions(fieldType));
       output.markdown(`**Created field "${fieldName}" (${fieldId})**`);
   }
}

9 Likes

Thank you so much for posting this!
I noticed the “soft release” of these features and was eagerly hoping for the public announcement.

Typo.

2 Likes

Typo.

Thank you :slight_smile:

@Kevin_Wilde By the way, welcome to the Airtable community!
Thank you for joining us. It is always nice to see Airtable staff on this forum.

Would you mind giving us a bit of an introduction so that community members can better know your role at Airtable and what to expect from you on this forum?

2 Likes

Hi @Kevin_Wilde ,

I noticed this by following @kuovonne messages and replies on a regular basis !

Thank you for your announcement and welcome to Airtable’s Community.

I’m mostly a Scripter, not a great Formulator, and I’m scripting in Google Apps Script too.

I was waiting for this as a great toolbox to develop more delayed or freezed useCases of mine so I’m very happy now to start something new from Scripting App where I still was an earliest hour Scripting-Block Beta user / tester !

Could I hope that’s the future way to another of my mostly waiting wish ?

  • Choose current View in my Browser’s viewPort by script ;
  • Hide or Show any field from current View by script ;

Cheers,

oLπ

Hey, this is great news! I’ve been wanting a way to scaffold a base programtically, without a full-blown block!

Also, @Kevin_Wilde, I found this post because I searched this Community when all of a sudden I started getting the following ts-check error message in the scripting app:

Type ‘null’ is not assignable to type…

I have the need/want to clear out previous values when a user clicks a button on a record before fetching a network resource (an external API call). My code has gone from:

const clearFields = {
    [fldDeliveryLine1.id]  : null,
    [fldDeliveryLine2.id]  : null,
    [fldLastLine.id]       : null,
    [fldCountyName.id]     : null,
...

// Wipe out any previous validation, no artifacts
await TABLE.updateRecordAsync(record.id, clearFields);

to:

const clearFields = {
    [fldDeliveryLine1.id]  : '',
    [fldDeliveryLine2.id]  : '',
    [fldLastLine.id]       : '',
    [fldCountyName.id]     : { name: '' },
...

which seems uglier to me—I thought null represented the idea of deleting previous data very well; empty string, not so much.

Does the new API allow for null-ifying (deleting) cell values?

Thanks!

1 Like

Glad to see the official announcement! I just made use of the new table.createFieldAsync today on a client project, and it worked beautifully.

3 Likes

First, just to clarify for others reading this thread – the API you are asking about is an existing API and is used to update table data, so is not related to the update in this post, which is about new APIs for updating base metadata (i.e. schema).

To answer your question, though, the current type definitions in the scripting editor allow for using undefined here, so try using that.

I will check into whether the type definition should also allow null.

1 Like

@Kevin_Wilde

Thanks, it’s just that I noticed errors in the editor where there were not any before, around the time this was released—null used to appear to be valid, then all of a sudden wasn’t—and so I thought there was a broader change and conflated the two.

I’ll try out undefined, thanks!

Hey there, Airtable community. We recently added the ability to write field descriptions from the scripting app, and write description data when creating a field or table.

Field descriptions can now be edited and specified when creating a new field:

  • Added field.updateDescriptionAsync (docs).
  • Added optional description argument to table.createFieldAsync (docs).
  • Added optional description property to field objects accepted by base.createTableAsync (docs).

You can find more information about the scripting API here. These methods are not available in the automations scripting app.

Here’s an example that showcases the field.updateDescriptionAsync method in action. The source code can be found below.

field_description_gif

output.markdown("# Update Field Description");

let table = await input.tableAsync("Pick a table!");
let field = await input.fieldAsync("Which field would you like to add a description to?", table);
let description = await input.textAsync("Insert the description you'd like to add to this field");
let confirmation = await input.buttonsAsync("Are you sure you want to add this description?", ['Yes', "No"]);

if (confirmation === 'Yes'){
    await field.updateDescriptionAsync(description);
}
4 Likes