If you work on projects, there’s a strong likelihood that you will do similar projects again and again. For example, a software development project will often follow the cycle of:

And each of these high-level tasks will probably have a consistent set of lower-level tasks.

In this post we’re going to create an Airtable script that creates a set of tasks for a project from a predefined template. Here’s our base set-up:

We’ve got 3 tables:

We want the tasks table to end up like this:

with each task from our template linked to a project.

In our script, let’s start by defining all 3 tables:


let projectsT = base.getTable('Projects');
let tasksT = base.getTable('Tasks');
let taskTemplateT = base.getTable('Task template');

We’re going to make use of Airtable’s “button” field type later on, so our next step is to get some input from the user to choose a project to have tasks created against:


let project = await input.recordAsync('Pick a project', projectsT);

It is good practice to check that a project has actually been selected by the user before continuing with other script operations, so let’s wrap the rest of the code in a conditional statement that checks that we do have a project record:


if (project) {
  // we have a project, so do something here
}

The first thing we want to do is give some output to the user, so that they know that the script is doing something:


output.text(`You picked ${project.getCellValueAsString('Name')}`);
output.text(`Creating tasks for ${project.getCellValueAsString('Name')} ... please wait`);
		

We then want to get the standard tasks from the task template table:


let taskTemplateQ = await taskTemplateT.selectRecordsAsync();

We’re going to use the createRecordsAsync method later on to add the tasks to the Tasks table - see here for a refresher on this. Our next task is to create an empty array to hold the data we’re going to push into the tasks table, so let’s declare that now:


let projectTasks = [];

createRecordsAsync takes an array of objects so we’ll push data into the projectTasks array in the correct format as we loop over the task template records:


for (let task of taskTemplateQ.records) {
    projectTasks.push(
        {
            fields: {
                'Task': task.name,
                'Project': [ {id: project.id} ]
            }
        }
    )
}
		

Two things to note here:

Now that we have built the array, we can pass this to the createRecordsAsync method like this:


await tasksT.createRecordsAsync(projectTasks);

And, finally, let’s tell the user that the script has completed:


output.text('Tasks created!')

Here’s the final script:


let projectsT = base.getTable('Projects');
let tasksT = base.getTable('Tasks');
let taskTemplateT = base.getTable('Task template');

let project = await input.recordAsync('Pick a project', projectsT);
if (project) {
    output.text(`You picked ${project.getCellValueAsString('Name')}`);
    output.text(`Creating tasks for ${project.getCellValueAsString('Name')} ... please wait`);

    let taskTemplateQ = await taskTemplateT.selectRecordsAsync();

    let projectTasks = [];

    for (let task of taskTemplateQ.records) {
        projectTasks.push(
            {
                fields: {
                    'Task': task.name,
                    'Project': [ {id: project.id} ]
                }
            }
        )
    }

    await tasksT.createRecordsAsync(projectTasks);

    output.text('Tasks created!')
}


To make the process a little more robust, we can check if tasks already exist against a project record (as we don’t want to create them twice) with a conditional statement:


if (!project.getCellValue('Tasks')) {
    output.text(`Creating tasks for ${project.getCellValueAsString('Name')} ... please wait`);

    let taskTemplateQ = await taskTemplateT.selectRecordsAsync();

    let projectTasks = [];

    for (let task of taskTemplateQ.records) {
        projectTasks.push(
            {
                fields: {
                    'Task': task.name,
                    'Project': [ {id: project.id} ]
                }
            }
        )
    }

    await tasksT.createRecordsAsync(projectTasks);

    output.text('Tasks created!')        
    
} else {
    output.text('Tasks already exist!!');
}
		

With the script now working and tested, we can assign it to a button the Projects table so that users can execute the script and select the project to run it against in one go: