Creating a scripting environment for Airtable that anyone can use

Alex Dytrych
For the Record
Published in
8 min readMar 20, 2020

Airtable believes that software creation shouldn’t be restricted only to professional developers — so for us, making customization approachable is key. Combining different views, field types, blocks, and integrations means that anyone can use Airtable to create software tailored specifically to them.

A lot of our users likely don’t consider that what they’re doing is creating software, nor would they consider themselves programmers. Nevertheless, they write code (formulas) and do computational-thinking (base design) every day — perhaps even without realising it. The new scripting block is designed to make scripting an accessible step up from formulas for these people, whilst preserving the power of JavaScript for more experienced devs. 👩‍💻

In this blog post, we’ll look at how we balanced these concerns at three different layers within the scripting block:

  1. The interaction model for scripts 🗣
  2. The editor where scripts are written ✏️
  3. The documentation and resources available within the block 📚

Interacting with scripts

Traditional databases have had something similar to scripting for a long time in the form of stored procedures. These act like functions in other programming languages: they have a fixed list of inputs, and return a single value as an output, and might have side effects in the meantime. This model is pretty simple and easy to understand, but also feels limiting — there’s no interactivity. You can’t ask your user for more information as your script progresses, and your output has to all happen at once. We can do better!

At the other end of the spectrum, there are full-fledged app UIs built with tools like React. Building entire apps inside Airtable would be awesome (and if you think so too you should sign up for this beta 👀), but is a pretty high bar for the casual formula author we were thinking about before. They’re busy! They’ve got dogs to walk and kites to fly or whatever it is people with real jobs do.

We needed something simple but flexible, and found inspiration from a type of UI that’s been around since the very early days of software: command-line interfaces. They fell out of fashion in mainstream computer UI in the 80s, replaced by synth pop and big hair. CLIs can be very complex, but typically they have a pretty simple model: they prompt the user to input some information, then print some output based on that.

This model allows a very wide range of interfaces to be created, but the programmer only needs to learn two fundamental patterns: one for input, and one for output.

INPUT

result = prompt("Ask a question")

OUTPUT

print("some information")

But because we’re running in a web browser instead of a terminal, we can offer much richer inputs and outputs. They all follow this same print/prompt pattern though — so a new developer can learn that pattern once, and use it again and again for different situations and types of information.

Autocomplete-driven development

A simple and easy to understand programming model is one thing, but that’s not much consolation against how intimidating a lone flashing cursor in an empty text area can be — especially for a new coder. We have all these inputs and outputs, but no way of knowing what they actually are without reading a bunch of documentation. This is one of a few problems discussed in “Learnable Programming,” a 2012 Bret Victor essay, which is now something of a classic in this space. Several ideas are presented, but one of my favourite sections discusses a common feature in programming environments: autocomplete.

Autocomplete is useful for making the act of writing code faster (more time for snacks), but its main value is in discoverability. In Bret’s essay, he mentions how some of his example uses an API specifically designed for autocomplete, for example by having related methods start with the same words.

Strangely, I don’t actually know of any APIs that are intentionally designed with autocomplete in mind. I do know many APIs, such as Processing, that are designed for brevity, which is irrelevant in an environment with good autocomplete.
— Bret Victor in “Learnable Programming”

In the scripting block, our code editor and autocomplete is powered by Microsoft’s awesome Monaco editor — the same editor used by VS Code. One of the biggest factors in our decision to use Monaco was its tight integration with TypeScript, which makes it really easy to provide powerful autocomplete. You supply a special type declaration file describing every variable and function available, and Monaco/TypeScript do the rest.

A type declaration file like this:

declare var budget: {
food: 200;
data: 150;
rent: 800;
candles: 3000;
utility: 150;
};

Results in autocomplete like this:

In the scripting block, this means that when it comes to the input and output system we talked about above, discovering the different kinds of input/output is easy:

base is the third main part of the scripting API alongside input and output. It’s how the script author gets at everything in their base: all the tables, views, fields, and records. A basic type declaration file that would give us autocomplete for the general shape of the API looks something like this:

declare class Base {
id: string;
name: string;
activeCollaborators: Array<string>;
tables: Array<Table>;
getCollaborator(nameOrEmailOrId: string): Collaborator;
getTable(nameOrId: string): Table;
}
declare var base: Base;

With this, typing base. gives you a list of all those properties and methods. If you say myTable = base.getTable('Dogs'), TypeScript understands what the return value of that will be, so typing myTable. will give you a list of all the properties and methods on a Table too.

It’s a good start, but we can do better. We’re running inside your Airtable base, so we know everything about it. That means we know much more specifically than just string which values can be passed to getTable. We can generate a type definition file from your base schema, using overloads to tell TypeScript about all the different variations:

declare class Base {
id: string;
name: string;
activeCollaborators: Array<string>;
tables: Array<Table>;
getCollaborator(nameOrEmailOrId: string): Collaborator;

getTable(name: “Content production”): ContentProductionTable;
getTable(name: “Social schedule”): SocialScheduleTable;
getTable(name: “Freelancer timesheets”): FreelancerTimesheetsTable;

getTable(nameOrId: string): Table;
}
declare var base: Base;

As well as listing out each table name, we can generate a special definition for every single table, view, and field in your base too. When you put everything together, this style of autocomplete means much less time trying to figure out what you can write, so you can focus on actually doing whatever it is you want. For someone new to coding who might struggle to know what their options are, this can make a really big difference!

Show, don’t tell

“Show, don’t tell” is a concept from storytelling that basically means that anything you can demonstrate rather than explain will make for a more engaging and interesting story. The same idea applies to technical documentation: explaining why something exist and how to use it is important, but demonstrating that is way more powerful. The most common form of ‘showing’ in API documentation is code examples.

Showing how an API is actually used helps contextualize it — developers can see how it fits in with the bigger picture, which helps them use it to create higher level abstractions and ideas. In the scripting block, API docs are tightly integrated with the rest of the development environment. They sit right below your code editor.

In the scripting block, we can take “show, don’t tell” one step further by letting people actually see what each example does, instead of just showing them the code. The ‘Run’ button next to each example lets the user see exactly what the result of running a particular example would be:

The thing that makes this hard in most other API documentation is that you need some data to run your API against. One option is to keep everything generic enough that it doesn’t refer to specific data, but that only works for really simple examples. Another is to create a specific set of data to use just for examples and sort of fake things in the background. We can do better in the scripting block though. We’re running in your base, so we already know about all the data you might want to use. We can use that to customize the examples to your specific base. This is the same example for the scripting block installed in two different bases:

That means that when you run the example it both works and shows you information that you’re already familiar with. This puts the examples immediately in context, which helps you understand both how you can customize them to fit your own needs, and how they might fit together with other examples to form a complete script. We do something similar with our HTTP API docs (check out https://airtable.com/api), which is a favourite among the developers who use it.

Conclusion

These are just a few of the approaches we considered and eventually implemented when designing the scripting block to balance power with accessibility. We’ve already been blown away by the response, and have seen some amazing scripts built — many by people who aren’t programmers or perhaps haven’t written any code before. If you haven’t yet, please do check out the scripting block. We’d love to see what you build.

There’s a lot more we can do to make scripting even more accessible and even more powerful. There are a lot of exciting ideas in this area that we’d like to explore in scripting block, so watch this space! If you think this stuff sounds like fun to work on (it is, although maybe only if you have my incredibly niche & specific interests) we’re hiring!

(Also, sign up for the Airtable developer newsletter if you want us to send you more content like this!)

--

--