Handling Netlify Form Submissions with GitLab Merge Request

Merge Request

Over the last few months, I work with Cipta Media Ekspresi. It is a grant for women working in the field of art in Indonesia, the program is supported by Wikimedia Indonesia.

In this post, I want to highlight on how we utilized GitLab's merge request feature for handling the grant request submissions.

The Stack

ciptamedia.org uses JAMStack architecture. It is built using Jekyll and hosted on Netlify.

JAM (Javascript, APIs and Markup) is just a simple terminology around the new way of making web projects, where you don’t have to host your own backend that builds the site every time you serve it..
Netlify Blog

Essentially, it has no backend. But if there is no backend, how can we use form in the first place?

Well obviously, I'm aware that service such as Google Forms and other alternatives do exist. But since our code is hosted on GitLab, why not try putting the submission on there as well? JAMStack best practices recommends that "Everything Lives in Git", and so we do our best to achieve that.

Netlify Form

Netlify has support for form handling, but the submission is stored on their end, obviously. They can have outgoing notification to Slack, Email, and Webhook (Send as POST request).

The webhook integration is the most useful, as we can practically do anything with the submission. In this case, we want it to make a GitLab merge request on each form submission.

Netlify-GitLab Bridge

I wrote a small script to bridge Netlify form outgoing notification to GitLab merge request API. It is written in Node.js and hosted on Auth0's Webtask, which is a serverless platform, so that our site still remains backendless.

It is not as simple as creating a merge request on every form submission, we first need to check for duplicate submission and then determine the format to store the form data, in this case we chose Markdown frontmatter so we can use it with Jekyll. The complete flow is as follows on each submission:

  1. A submitter submits the form and triggers Webtask endpoint.
  2. Create a hash from the submitter national ID number and create a git branch using the hash as the branch name. We create a hash because national ID isn't supposed to be published.
  3. On the frontend side, before submitting, we also hash the ID and utilize GitLab API to check if there is a branch with the hashed ID as the name, if a branch exists, request is blocked before even reaching Netlify.
  4. If for some reason the frontend validation is bypassed, the request is still rejected because a git branch with the same name is not allowed.
  5. A .md file is committed using the submitter's own name and email on the branch. The file is filled with the form data written using frontmatter/yaml format. Then, a merge request is created.
  6. Review process begins on the merge request, by humans. This is done just like a code review on a typical open source project. The reviewer can "merge" when the submission is approved.

You can see the resulting merge request on ciptamedia/ciptamedia.gitlab.io.

Further Validation

We also need to validate other form fields for common things such as character length, phone number, email, etc. We do this completely on the frontend.

Additionally, we need to make sure that the email is legit, so we used Netlify Identity which provides authentication without backend. The submitter is required to login with their Google account before filling the form so that we can extract their email.

Now, before someone screams out that it can obviously be bypassed, I'll remind you that the submitter is an artist, not a coder. Invalid submission isn't that big of a deal since it only creates a branch & MR we can easily delete.

Other Serverless Platform

At the time of this post is published, it is possible to deploy Lambda functions without an AWS account via Netlify. But it's not available at the time we come up with this solution.

Further improvement to the gitlab-submission script would be to incorporate something like zeit/now-cli. This makes the script platform agnostic and can even be deployed on non serverless platform, because the app is made to listen to a port just like a normal Node.js web application. However, Webtask is not supported on now.