Airtable automation: How I streamlined submissions to the Developer Showcase
Tags: automation, contentful, airtable, javascript
Published: 2022-10-13 14:28:58

**NOTE:** This post was [first published](https://www.contentful.com/blog/2022/10/13/airtable-automation-developer-showcase-submissions/) on the Contentful Blog.

In April 2022, we launched the [Developer Showcase](https://www.contentful.com/developers/showcase) to highlight the work of the community. Developers share their projects, apps, videos, or blog posts they’ve created around Contentful. The developer showcase has become the go-to place to see what others are building and get inspired!

If you’ve worked on a project that you believe can help the community, you can go to the Developer Showcase page, and submit the [form](https://www.contentful.com/developers/showcase/submit/). If your project meets the criteria, you get an email from us for the next step. In this step, you share more information about the project and yourself. This information is then used on the Developer Showcase page.

The process seems straightforward, but there are a lot of repetitive tasks that are being performed behind the scenes. In this tutorial, I’ll share how automated processes using Airtable and a few other integrations  got myself some time back!

## The manual process and challenges

As mentioned above, if a developer is interested in adding their project to the showcase, they submit a form on the website with initial details. Once the form is submitted, the developer relations (DevRel) team gets a notification over email with the submission details. We then manually enter the details into a Google Sheet.

Imagine, getting submissions every day, and you have to fill out entries in the Google Sheet manually! Yes, data entry is monotonous and time-consuming. I knew it could be automated and save us time.

![Stickman Submit Entry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ntlwddqrkb8yxo0wa5b.png)

After the details were entered in the Google Sheet, team members would review the submissions weekly. We’d check and decide if the submissions met the criteria. If the submission was a good fit, the developer would receive an email from us asking them to fill out another form with more details. The information shared via this form would get added to a Contentful space, which was being used to manage the content on the Developer Showcase page (yes, we use Contentful at Contentful!).


![Stickman Contentful](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hh03hbv8wzqx233mrxn4.png)

The above process was also manual. We had to manually add the relevant information shared via the form to the Contentful space. This again cost us time. I recognized this as another task that could be automated.

Both the tasks were manual, which meant that the chances of human error were high. There were situations where I would enter the information in the wrong columns when I was in a hurry. This was also becoming monotonous.

## Automating the boring parts

I take pride in being lazy! My laziness helps me find solutions that make my tasks joyful. Now that I recognized two tasks that could be automated, I started looking for solutions.

My first step was to migrate to a spreadsheet-like tool that provided better features than Google Sheets. I was looking for a tool that would allow me to easily create automation workflows, configure different views, and also have an easy-to-use API.  

Luckily for me, we already use [Airtable](https://airtable.com/) at Contentful for various projects. I created a base in Airtable for the Developer Showcase which would contain all the details — from the initial form submission details to the final details.

Everyone in the DevRel team is involved in the showcase. All of us decide if a submission should be added to the Developer Showcase. We handle this by voting. For every member of the team, I created a view that would list all the submissions that they haven’t viewed. Having an individual view made it easy for my team to go in, view the project, leave their thoughts, and vote. It hardly takes them 15 minutes now!

The migration to Airtable also involved creating automation to handle the form submissions. After setting up the table and the individual views, I created an automation workflow in [Zapier](https://zapier.com/). 

This workflow would get triggered whenever there was a submission made to the Developer Showcase form. The workflow would then add the new record to the Airtable base, and also send a Slack message to notify us. All I had to do now was to check if the submission was valid and if it was, update the Valid column in Airtable.

![Zapier](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w78rjuubvnzevq99fa6n.png)

The above workflow replaced the manual labor of entering the data into Google Sheets. The first task was now automated!

## Connecting Airtable to Contentful

The other time-consuming task was entering the final details of the submissions to Contentful, and I wanted to automate it next. Now that we were using Airtable to manage the data, I re-created the template for the final submission form in Airtable. This enabled us to capture the data in Airtable itself. Whenever a developer submitted this form with the details, it now got saved in Airtable. There was no need to create an automation workflow to handle it!

The data from the final form submission was in Airtable, and not Contentful, yet. To get this data added automatically to Contentful, I created a workflow in Airtable. If you’re using Airtable, you might know that Airtable doesn’t have webhooks (at the time of writing this article). This means that whenever data is created or updated, you can’t trigger external events. 

To overcome this, you can either create automation workflows that run at a certain interval of time, or check for new data and then process that data. A better and faster solution is to use automation within Airtable. It allows you to trigger actions when data is entered or updated.

I created a workflow in Airtable to handle the above scenario. Every time a developer submitted the final form, this workflow would get triggered. It is out of the scope of this article to walk you through all the steps of creating the automation workflow. But, below I’m sharing the code snippet that populates Contentful with the data.

Once the form is submitted, the workflow gets triggered. The next step is to create an action that will add the content to Contentful. Airtable doesn’t have an in-built action for Contentful, so there isn’t a no-code or even a low-code solution available just yet. However, it has an action that allows you to run custom JavaScript code. 

While working with this action, I learned that you can’t import npm packages. Hence, using the Contentful Management JavaScript SDK was not possible. Fortunately, Contentful has a REST API to interact with the [Management API](https://www.contentful.com/developers/docs/references/content-management-api/)! I wrote the following code in the “Run a Script” action. This script populated the Contentful space with the data from Airtable.

```ts
// @ts-ignore
const SPACE_ID=SPACE_ID
const URL = `https://api.contentful.com/spaces/${SPACE_ID}`
const inputConfig = input.config();
const AUTHORLINK = 'authorLink';
const AUTHOR = 'author';
const ENTRY = 'entry';

const linkId = [];
let authorId;

const makeCall = async (endpoint, method, contentType, body) => {
  return fetch(URL+endpoint, {
    method: method,
    headers: {
      Authorization: 'Bearer MANAGEMENT_TOKEN',
      'Content-Type': 'application/vnd.contentful.management.v1+json',
      'X-Contentful-Content-Type': contentType
    },
    body
  })
}

const checkLinkType = (link) => {
  if(link.includes('github')) {
    return 'Github'
  } else if (link.includes('linkedin')) {
    return 'LinkedIn'
  } else if (link.includes('twitter')) {
    return 'Twitter'
  } else if (link.includes('youtube')) {
    return 'YouTube'
  } else {
    return 'Website'
  }
}

const addAuthorLink = async () => {
    const links = inputConfig.authorLink.split(',');
    for (var link of links) {
        let linkType = checkLinkType(link)
        const response = await makeCall('/environments/master/entries', 'POST', AUTHORLINK, JSON.stringify(
            {
                "fields": {
                    "internalTitle": {
                        "en-US": `${inputConfig.authorName}'s ${linkType}`
                },
                "type": {
                    "en-US": `${linkType}`
                },
                "link": {
                    "en-US":`${link}`
                }
            }
        }))
        const res = await response.json();
        linkId.push(await res.sys.id)
    }
    return linkId
}

const addAuthor = async () =>{
    const links = linkId.map(link => {
        return {
            "sys": {
                "type": "Link",
                "linkType": "Entry",
                "id": link
            }
        }
    })

    const response = await makeCall('/environments/master/entries', 'POST', AUTHOR, JSON.stringify({
        "fields":{
            "name": {
                "en-US": `${inputConfig.authorName}`
            },
            "bio":{
                "en-US": `${inputConfig.authorBio}`
            },
            "links": {
                'en-US': links
            }
        }
    }))
    const res = await response.json();
    authorId = await res.sys.id;
}

const addEntry = async() =>{ 
    const response = await makeCall('/environments/master/entries', 'POST', ENTRY, JSON.stringify({
        "fields": {
            "title": {
            "en-US": `${inputConfig.projectTitle}`
            },
            "description": {
            "en-US": `${inputConfig.projectDesc}`
            },
            "link": {
            "en-US": `${inputConfig.projectLink}`
            },
            "author": {
                "en-US": {
                    "sys": {
                    "type": "Link",
                    "linkType": "Entry",
                    "id": authorId
                    }
                }
            },
            "type": {
              "en-US": {
                "sys" : {
                  "type": "Link",
                  "linkType": "Entry",
                  "id": "FIELD_ID"
                }
              }
            }
        }
    }))
    const res = await response.json();
    console.log(await res)
}

(async () => {
    await addAuthorLink()
    await addAuthor()
    await addEntry()
})()
```

## Walking through the code

Let’s break down the code to understand what is happening. The first few lines are defining some variables that we need later in the code. This includes the space ID, URL of the Management API, content type IDs, as well as the input object from Airtable.

We then define a reusable function `makeCall` that takes the API endpoint, HTTP method, content type, and the body as the parameters. We use this function to call the Management API.

An author of a Developer Showcase submission can share social links. These links are displayed on the respective author page, e.g., [one of our top contributors Teemu Tammela](https://www.contentful.com/developers/showcase/author/teemu-tammela/). To identify the type of this link, we are using the `checkLinkType` function. It returns the type that we have configured in Contentful.

Next, we define the `addAuthorLink` function. This function creates an array of links shared by the author, gets the link type, and adds them to Contentful. It also returns an array of link IDs that get used in the `addAuthor` function.

The `addAuthor` function adds all the information about the author, as well as adds references to the links. The final result, a new entry of the author, is now available with references to the author’s social media accounts.

Lastly, the `addEntry` function adds the project details and references the author. Since the type of project has not been asked in the form, it is hard-coded. The type is manually updated before the entries are published.

The last action in this automation workflow is sending a message on Slack. Since Airtable has an built-in app for Slack, it was straightforward to set it up!

To summarize the above workflow, every time the Airtable form was submitted, the automation would be triggered. It will execute the “Run a script” action, that runs the above code and adds the content to Contentful. Once the content is successfully added, a message is sent on Slack.

All we have to do now is log in to the Contentful space, review the newly created entries, add the missing assets, and publish the changes!

## What’s next?

These workflows have saved us a huge amount of time! We don’t have to manually add data, and we can focus more on highlighting the amazing work the community is doing!

The code currently doesn’t handle images, however. We manually have to add them to Contentful. I couldn’t find a good solution to handle images in Airtable, and I am still on the lookout. If you have worked with images in Airtable and have a solution, please let me know!

There are a lot of other tasks that can be automated in the whole process. But my experience with automation has taught me one thing — start small and automate the things you do often. It has helped me not over-engineer a simple solution.

Now that I have more free time, why don’t you share what you’ve created with Contentful? I would love to see it and share it with the community. Also, let me know which Developer Showcase entries you find the most interesting!