The primary downfall of Ghost's laser focused simplicity is that users have to figure out how to fill the gap for any missing features they require. A common example, a feature that I wanted for my own blog, is a contact form. For those with a bit of technical savvy, this is a great excuse to leverage Azure Functions.

This is the first post in a short series that will outline the ins and outs of using Azure Functions to extend the functionality of the Ghost platform.

Why Ghost? What is Ghost?

I recently started working on a new blog website. I was torn between using WordPress or Ghost, but ultimately decided on Ghost due to my aversion for PHP. Plus, the thought of having to meticulously concoct the perfect balance of WordPress extensions and theme modifications in order to get WordPress to perform and function the way I want sounded incredibly draining. Although I’ve had great success with the WordPress platform over the years, I’ve ultimately grown tired of fighting it.

Ghost is known for its impressive performance and simplicity. Ghost is a blogging platform and only a blogging platform. Its creators are purposely avoiding the addition of features that turn Ghost into more than a publishing platform. Given that information, it would likely not be in your best interest to run an entire online business on Ghost. That’s not what it’s for.

WordPress, on the other hand, is an everything platform. It can be configured and extended as much as you want. Need a widget to do XYZ? The community already developed one for you. Just install it and enjoy your bloated pile of scripts and style sheets.

Ghost Contact Form Dilemma

Given that the Ghost platform has no support for platform extensions, folks need to come up with their own solutions for extraneous features. For instance, I wanted a simple contact form on my blog. I should have at least one call-to-action, right?

So how do we extend the functionality of Ghost to accomplish a contact form? I broke this down into several options.

Fork Ghost

Create a fork of the Ghost platform source and implement the contact form feature ourselves. You’d have to get your hands dirty with Node.js, but that’s part of the fun, right?

My main concern with this strategy is that we would now be responsible for updating our forked branch of Ghost every time they come out with an update. Most of the time this would be a simple, mindless merge. Hopefully they never do a complete overhaul and leave you with a hazardous merge puzzle.

I don’t want to spend any of my time manually keeping my blog platform up to date with the latest version. So, this forking solution isn’t great for me.

Third-Party Form Service

We could use a service like Formspree, Typeform, or formlets and that would work great. We’d have a contact form without having to maintain a fork of the Ghost platform. We wouldn’t even have to write a line of code.

The reason I’m not in love with this option is the lack of seamless integration. I don’t want to have to send the user to a different website to complete the contact form. Although these form services offer tons of style settings that allow us to make the transition from our website to their form website much less obtrusive, I’d much rather have the contact form baked right into my blog website.

For folks that are less technical, this is a fantastic option. You can outsource the form feature in a matter of minutes.

Azure Functions

We can implement an Azure Function tied to an HTTP trigger that emails the contact form details on behalf of our Ghost blog. Then we add a "page" or modify our Ghost theme to include a simple contact form that posts data to our new HTTP endpoint hosted in Azure Functions.

We won’t have to modify the Ghost platform and the contact form will be seamlessly integrated into our blog website. Another plus is that Azure Functions is quite cost effective. At the time of writing this, their pricing page explains that the "first million executions are included free each month."

Functions are billed based on total number of requested executions each month for all functions. Executions are counted each time a function is executed in response to an event, triggered by a binding. The first million executions are included free each month.

After the first million requests, you're on the hook for paying 20 cents per million. Realistically, I’ll probably never have to pay a penny with the insignificant amount of traffic my blog receives.

This is a great choice for bloggers who are comfortable writing code and working with PaaS offerings. For those who have absolutely no interest in creating an Azure Function or modifying the Ghost platform source code, I would suggest sticking with the form service option.

Choosing Azure Functions

In this short blog series, I’m going to cover my journey with the last option I’ve outlined: implementing an Azure Function to facilitate emailing my blog’s contact form. I should mention that I’m often looking for another excuse to play with Azure Functions, so there is certainly bias present in this solution.

Although I am going to specifically outline how to implement a custom contact form for a Ghost application using Azure Functions, a similar strategy can be taken for any feature requiring server-side processing:

  • Perhaps you need an HTTP endpoint to facilitate payment processing.
  • Maybe there’s information in a database that you need to expose to your blog application in order to display some charts or tables.
  • The subscriber management features built in to Ghost may not be robust enough for your needs, so you’d like to implement your own subscriber management features outside of the platform.

The list of possibilities is endless and perhaps we’ll explore more of these Azure integrations in the future. For now, though, we’ll be focusing on a common need for bloggers: the contact form.

In the next post I'll be covering the initial implementation of the Azure Function with an HTTP trigger that will be responsible for emailing the contents of our contact form.

If you have any questions or comments so far, feel free to add them below.