GSD

How to Add E-commerce Landing Pages to Your Go App Using ButterCMS

Posted by Ikeh Akinyemi on May 2, 2023

n today's fiercely competitive online marketplace, having a strong online presence is crucial for businesses to succeed in e-commerce. However, creating an e-commerce website can be challenging, particularly when it comes to designing effective landing pages that convert visitors into customers. Failing to build high-performing landing pages could result in missed sales opportunities and stunted business growth.

Fortunately, there is a way to streamline the process of building landing pages that are both visually appealing and optimized for conversions. By using Golang and ButterCMS, a headless content management system (CMS), brands and business owners can quickly and easily create compelling e-commerce landing pages that drive sales. 

With this combination, developers can leverage Golang's efficiency and scalability to build robust e-commerce applications, while ButterCMS provides a simple and flexible platform for managing content and creating landing pages. Ultimately, this approach helps businesses get to market faster and stay ahead of the competition in the fast-paced world of e-commerce.

Why use Golang?

Golang, also known as Go, is a modern programming language created by Google in 2009. It was designed to be fast, efficient, and easy to use, with a focus on concurrent programming and large-scale distributed systems. Golang has gained popularity for its simple syntax, powerful features, and strong performance, making it a popular choice for building web servers and network applications.

Golang is an excellent choice for building e-commerce landing pages because of benefits like:

  • Speed: Golang's compiled code executes faster than interpreted languages. This results in faster page load times and a better user experience. Faster compilation time is also useful for developers who need to iterate quickly and deploy changes to their landing pages rapidly.
  • Error handling: Golang's error-handling mechanism is unique and efficient. It allows developers to handle errors quickly and efficiently, without interrupting the program's execution flow. This is useful for e-commerce landing pages, where developers need to identify and resolve issues quickly to ensure smooth operation.
  • Concurrency: Golang's concurrency features make it easy to handle multiple processes simultaneously. This is crucial for e-commerce landing pages, which often need to retrieve, process, and serve data from multiple users simultaneously. The use of constructs like goroutines and channels in Golang allows developers to write highly efficient and concurrent code, resulting in faster page loading times and better performance.
  • Cloud-native: Golang is the language of choice for many cloud-based applications and tools. This is because of its faster compilation time and ability to handle large-scale distributed systems. E-commerce landing pages that are deployed on cloud-based infrastructures can benefit from Golang's cloud-native features like native concurrency support, fast compilation, and startup times, resulting in faster deployment times, better scalability, and improved reliability.

Looking at these features in action

But how do these benefits work together? Let’s use an example by considering an e-commerce landing page that needs to fetch and display product data from a database. With Golang's concurrency features, developers can write highly efficient code that fetches and processes data from multiple users simultaneously, resulting in faster page load times. Additionally, Golang's error-handling mechanism allows developers to quickly identify and resolve issues, ensuring a smooth user experience. Finally, deploying the landing page on a cloud-based infrastructure written in Golang can provide the necessary scalability and reliability needed for an e-commerce site.

See how ButterCMS melts into your Go app in minutes

Why use ButterCMS?

ButterCMS is a headless CMS that simplifies website creation by providing an easy-to-use dashboard and lightning-fast content API. It offers language SDKs for several languages, including Golang and Python, which make it easier to interact with core ButterCMS features.

For this e-commerce landing page tutorial, we'll be using ButterCMS Pages. This visual page builder tool allows users to create custom page layouts, add and customize various elements such as images, text, videos, and forms, and publish these pages to their website or app. 

Other features we’ll be using in this tutorial include:

  • Collections: Utilized as data tables that can be referenced by Pages, thereby expanding the range of possible use cases.

  • Components: Enable us to logically group related content fields together and order flexible page layouts easily.

Tutorial prerequisites & objective

To follow along with this tutorial, you will need:

  • A basic understanding of Golang and HTML is required

  • Golang installed locally on your machine 

  • A ButterCMS account

  • A text editor of choice

  • Access to a POSIX-compatible terminal

The image below is the finished landing page we’ll build over the course of this tutorial. For the rest of the tutorial, we’ll focus on creating and curating the content of the UI. In the latter part, we will integrate the ButterCMS API into a Golang application to retrieve the landing page.

Rendered Go Ecommerce Landing Page

Set up the landing page in ButterCMS

Before we write any sample code, let’s set up and create the necessary components of the landing page using ButterCMS Pages. If you don’t have an existing ButterCMS account, click here to create one.

Getting started

To access the Pages section, first click on Pages in the left-side menu. This will direct you to a page called Pages. Next, click the New Page button at the top-right corner of the page. Then select Create New Page from the drop-down menu to begin defining our page’s schema.

Click create new page

After doing so, you will land in the page schema editor. On the right, you will see various ButterCMS fields that can be used on their own or together to compose various components that will make up the different sections of the e-commerce landing page. 

Let’s first add a Short Text field for the page title.

Select Short Text field

To provide more meaning to the Title data when retrieving it within our project, I updated the JSON field identifier to page_title.

Building the Hero section

Next, we will create the Hero section of the landing page by grouping the necessary fields together using the Component feature. To achieve this, click on Component and then select the Create Component button.

For the Hero section, add the following fields: 

  • Short text named “Headline”

  • Long text named “Description”

  • Media named “Image”

  • Short text named “Call to action button label”

Hero component configuration

Building the Features section

To create the Features section, we'll use a repeater field. With a repeater, we can create a list containing a set of identical objects within it.

For the Features section, add the following fields to the repeater to hold the different content needed:

  • Short text named “Headline”

  • Long text named “Description”

Features repeater configuration

Once you have defined the Features, simply click the Save button and you will be automatically redirected back to the page editor. Here, we will be prompted to enter the Page Title and its unique API slug, which will be used to identify the page when retrieving it within our project. For this tutorial, we’ll use Fruit & Veggie Landing Page and fruit-veggie-landing-page as the values for the Page Title and its unique API slug, respectively.

Name landing page

Creating the Products section

To create the Product data for the Featured Products section, we will use the ButterCMS Collections feature. Collections in ButterCMS are tables of data to be referenced by Pages, they allow users to group data together under a common category. 

Within the New Collection Configuration page, we’ll add the following fields:

  • Short text named Product Name

  • Short text named Product Price

  • Media named Image URL

  • Short text named Image ALT Text

Save the collection configuration using the unique string, “products”, as the Collection API Key. With this unique string, we can retrieve only items needed for the Products section.

Products collection configuration

Adding the collections to our landing page

For the next step, let’s return back to the new page we created earlier. Open the page, click on the ellipse at the top right, and select Edit Schema. Here we will add a reference field to add the items within the products collection we just created:

Add Reference field to ecommerce landing page schema

Adding dynamic content to our landing page

Now that we’re done defining the schema of the page, go back to the page editor, click on the Fruit & Veggie Landing Page tile, go through all the components that have been created, and populate them with the required text, images, and descriptions.

Let’s add dynamic content for the Features section by clicking on the + Feature button, and adding a value for the Headline and Descriptions fields. In the below screenshot, we have three items in the Features section, meaning we clicked on the + Feature button three times to create three distinct items.

Create feature repeater items

In order for us to add items via the Products reference, we will need to create a few items within that collection. So, visit the Collections page, click the New Item button in the top right corner, and then select Products.

Add items to products collection

Fill out the fields within the Add items to products page and click Publish. Please do this two more times so you have a total of three items. 

After creating the collection items, return back to the landing page, select Add Reference under Products, and then add all the items that show up.

Add collection items to our Go ecommerce landing page

After selecting the items, click on Use Selected to add them to the landing page.

Always remember to publish new changes whenever you add/update data.

Setting up the Go project

To begin, you can clone the completed version of this project, which is available for download. Throughout the article, we will describe various components of the Go project in detail.

# bash 
git clone https://github.com/Ikeh-Akinyemi/CMS-Landing-Page.git

We proceed to download the necessary dependencies listed in the go.mod file in the root directory:

# bash
go mod tidy

To download the ButterCMS SDK and pat, a lightweight pattern muxer for Go's net/http library, run the Go command provided above. 

Define the data model

After installing the required packages, the next step is to define the data model for the returned data from the API call to ButterCMS.

// File: ./server/internal/model
type Product struct {
  // Name of the product
  Name        string `json:"name"`
  // Price of the product
  Price       string `json:"price"`
  // URL for the product's image
  ImageURL    string `json:"image_url"`
  // Alternative text for the product's image
  ImgAltText  string `json:"image_description"`
}

type Hero struct {
  // Headline for the hero section
  Headline    string `json:"headline"`
  // Description for the hero section
  Description string `json:"description"`
  // URL for the hero section's image
  ImageURL    string `json:"image_url"`
  // Label for the hero section's button
  ButtonLabel string `json:"btn_label"`
}

type Feature struct {
  // Headline for the feature
  Headline    string `json:"headline"`
  // Description for the feature
  Description string `json:"description"`
}

type LandingPage struct {
  // Title of the landing page
  PageTitle string `json:"page_title"`
  // Data for the hero section
  Hero Hero `json:"hero"`
  // List of features for the landing page
  Features []Feature `json:"features"`
  // List of products for the landing page
  Products []Product `json:"products"`
}

Setup the server and its endpoints

To retrieve, parse the returned data, and render the landing page with the returned data, we will set up a server, an endpoint route, and a handler that uses the Go HTML package.

package main

import (
 "log"
 "net/http"

 "github.com/bmizerany/pat"
)

func main() {
  port := ":8080"
  log.Printf("starting server on port %s\n", port)

  mux := pat.New()

  mux.Get("/landing-page", http.HandlerFunc(landingPage))

  // Create a file server which serves files out of the "./ui/static"   directory.
  fileserver := http.FileServer(http.Dir("./ui/static/"))
  mux.Get("/static/", http.StripPrefix("/static", fileserver))

  http.ListenAndServe(port, mux)
}

The above snippet sets up a server with endpoint routes and handlers to serve a landing page and static files.

The pat package is used to create a new router (called mux) that will match incoming requests to their corresponding handler functions. In this case, there is only one route defined, which is for the landing page at /landing-page.

The http.HandlerFunc is used to wrap the landingPage function which will handle the incoming request to the /landing-page endpoint.

The next block of code creates a file server that serves static files from the ./ui/static directory. The http.Dir function is used to get an http.FileSystem object that is used to serve static files. The http.FileServer is used to serve files from the http.FileSystem object. The http.StripPrefix is used to remove the /static prefix from the incoming request URL to correctly match the file path with the file location.

Finally, http.ListenAndServe is used to start the server and listen for incoming requests on port 8080. The mux object is passed as a parameter to handle incoming requests.

See how ButterCMS melts into your Go app in minutes

Define the endpoint handler

Before we start the server, let’s define the landingPage function. This function will have the function signature of func(w http.ResponseWriter, r *http.Request), as below:

// File: ./server/cmd/landing_page.go
func landingPage(w http.ResponseWriter, r *http.Request) {
  ButterCMS.SetAuthToken(os.Getenv("BUTTERCMS_API_TOKEN"))
  data := map[string]string{}

  result, err := ButterCMS.GetPage("*", "fruit-veggie-landing-page", data)
  if err != nil {
    log.Fatal(err)
  }

  templateData := model.LandingPage{}
  templateData.PageTitle, _ = utils.GetValue[string](result.Page.Fields, "page_title")
  templateData.Features = utils.ProcessFeaturesSection(result.Page.Fields)
  templateData.Products = utils.ProcessProductsSection(result.Page.Fields)
  templateData.Hero = utils.ProcessHeroSection(result.Page.Fields)
  render(w, r, "./ui/html/template.html", &templateData)
}

func render(w http.ResponseWriter, r *http.Request, name string, td *model.LandingPage) {
  t, err := template.ParseFiles(name)
  if err != nil {
    log.Fatal(err)
  }
  buf := new(bytes.Buffer)
  err = t.Execute(buf, td)
  if err != nil {
    log.Fatal(err)
  }
  buf.WriteTo(w)
}

The above snippet defines a handler function for the endpoint /landing-page, which retrieves data from the ButterCMS API and renders the landing page HTML template using the returned data.

The landingPage function first sets the authentication token for ButterCMS using an environment variable. It then initializes an empty map for the data, which will be passed to the ButterCMS API.

Next, it calls the GetPage methods of the ButterCMS client to retrieve all the data for the "fruit-veggie-landing-page" slug. The * in the first parameter of the GetPage method tells the API to return all fields for the page. If there's an error retrieving the data, it logs the error and exits.

The utils package provides the functions that help parse the returned data from the API into the LandingPage struct defined in the data model. The templateData variable is then populated with the processed data.

Finally, it calls the render function, which uses the html/template package to render the HTML template with the data passed in. The resulting HTML is then written to the response writer. See the definition of the template on GitHub.

As mentioned above, we have utility functions to parse data returned from the API call to ButterCMS. Let’s define and explain the internal implementation of these functions next.

// File: ./server/internal/utils/utilities.go
func GetValue[T any](input map[string]interface{}, name string) (T, error) {
  field, ok := input[name].(T)
  if !ok {
    var null T
    return null, fmt.Errorf("unable to get property %s from map", name)
  }
  return field, nil
}

The GetValue function is a generic function that returns a value of any type T from a map of string keys and interface{} values. It takes two arguments: input, which is the input map, and name, which is the key in the map to retrieve the value for. The function attempts to retrieve the value from the input map using the given key, and then converts it to the desired type T. If the conversion is successful, it returns the converted value along with no error. Otherwise, it returns an error.

Next, let’s see the internal implementation of ProcessFeaturesSection below:

// File: ./server/internal/utils/utilities.go
func ProcessFeaturesSection(section map[string]interface{}) []model.Feature {
  unparsedFeatures, _ := GetValue[[]interface{}](section, "features")
  features := []model.Feature{}
  for _, unparsedFeature := range unparsedFeatures {
    typedFeature := unparsedFeature.(map[string]interface{})
    headline, _ := GetValue[string](typedFeature, "headline")
    description, _ := GetValue[string](typedFeature, "description")
    feature := model.Feature{
      Headline: headline,
      Description: description,
    }
    features = append(features, feature)
  }
  return features
}

The ProcessFeaturesSection function is used to extract and process the "features" section of the page data returned from the ButterCMS API.

It takes in a map section which represents the entire page data as a collection of key-value pairs. The function first extracts the features key from this map and stores its value in the unparsedFeatures variable.

Next, it initializes an empty slice of model.Feature structs called features. It then iterates over each item in unparsedFeatures, which is a slice of interface{} types. For each item, it casts it to a map[string]interface{} type, representing the feature data as a collection of key-value pairs.

The function then extracts the headline and description keys from this map using the GetValue utility function and creates a new model.Feature struct with those values. This model.Feature struct is then appended to the features slice, and returned to the calling environment.

Define the utility functions

Next, let’s see the internal implementation of ProcessProductsSection below:

// File: ./server/internal/utils/utilities.go
func ProcessProductsSection(section map[string]interface{}) []model.Product {
  unparsedProducts, _ := GetValue[[]interface{}](section, "products")
  products := []model.Product{}
  for _, unparsedProduct := range unparsedProducts {
    typedProduct := unparsedProduct.(map[string]interface{})
    name, _ := GetValue[string](typedProduct, "name")
    price, _ := GetValue[string](typedProduct, "price")
    imageURL, _ := GetValue[string](typedProduct, "image_url")
    imgAltText, _ := GetValue[string](typedProduct, "image_description")
    product := model.Product{
      Name: name,
      Price: price,
      ImageURL: imageURL,
      ImgAltText: imgAltText,
    }
    products = append(products, product)
  }
  return products
}

The ProcessProductsSection function is used to extract and parse product data from the section map, and it is similar to ProcessFeaturesSection in that it loops through an array of unparsed data, extracts the relevant fields, and creates a slice of model.Product structs.

The unparsedProducts variable is obtained using the GetValue utility function, which extracts the array of product data from the section map using the key "products". The function then loops through the array of unparsedProduct values and casts each value to a map[string]interface{} using a type assertion. From there, the name, price, image_url, and image_description fields are extracted using the GetValue function and assigned to a new model.Product struct. This struct is then appended to the products slice, which is ultimately returned by the function.

Next, let’s see the internal implementation of ProcessHeroSection below:

// File: ./server/internal/utils/utilities.go
func ProcessHeroSection(section map[string]interface{}) model.Hero {
  unparsedHero, _ := GetValue[map[string]interface{}](section, "hero")
  headline, _ := GetValue[string](unparsedHero, "headline")
  description, _ := GetValue[string](unparsedHero, "description")
  imageURL, _ := GetValue[string](unparsedHero, "image_url")
  buttonLabel, _ := GetValue[string](unparsedHero, "btn_label")
  return model.Hero{
    Headline: headline,
    Description: description,
    ImageURL: imageURL,
    ButtonLabel: buttonLabel,
  }
}

The ProcessHeroSection function is a utility function used in the landingPage function to extract and process data for the Hero section of the landing page. It takes a map of the section data as an input and returns a model.Hero object.

The function first extracts the hero section data from the input map using the GetValue function. It then further extracts the individual fields of the hero section, such as headline, description, image_url, and btn_label, using the GetValue function again. Finally, it returns a model.Hero object with the extracted field values.

The ProcessHeroSection function is similar to the ProcessFeaturesSection and ProcessProductsSection functions, but instead of processing a list of items, it processes a single object.

Start the server

Now that we have all the different components of the project explained, let’s start the server by running the command go run followed by the directory path of the main.go file. For example:

# bash
go run server/cmd/*

This command will start the server and make it available on localhost:8080. To access the landing page, navigate to the URL localhost:8080/landing-page on your browser. 

Congratulations on your new landing page! 

Congratulations! You've successfully created your new landing page with our platform. We are thrilled to have played a part in helping you achieve this important milestone for your business.

Here is the screenshot of the live version:

Rendered Go Ecommerce Landing Page

Final thoughts

By leveraging ButterCMS and Go, we have demonstrated how easy it is to build an e-commerce landing page. With ButterCMS handling content management and Go powering the backend, we have been able to create a dynamic, and responsive landing page in a matter of hours.

This approach provides several benefits for building landing pages quickly and efficiently. By separating the front-end from the back-end, we can easily make changes to the content and design without affecting the functionality of the site. Additionally, using ButterCMS as a headless CMS allows us to use any programming language or platform to consume the content API.

However, an e-commerce site requires more than just a landing page. Additional functionality such as a shopping cart, checkout system, and user account management are crucial for a complete online shopping experience. Fortunately, the modular nature of our approach makes it easy to expand the functionality of our site.

To add a shopping cart, for example, we can create a new endpoint that handles adding, updating, and deleting items from the cart. We can then modify our HTML template to display the cart contents and integrate with a payment gateway to enable checkout. Similarly, we can create an authentication system that allows users to create accounts, save their shipping and billing information, and view their order history.

In conclusion, our landing page is just the beginning of what is possible with ButterCMS and Go. With these powerful tools at our disposal, we can build a fully-functional e-commerce site with all the features required for a seamless shopping experience.

This project serves as a great example and if you want to learn more about using ButterCMS, we can check out their documentation. All the code for this tutorial can be found in this GitHub repo.

To learn more about how you can use headless CMS and/or a headless architecture for e-commerce, check out the following articles:

Make sure you receive the freshest Golang tutorials and Butter product updates.
Ikeh Akinyemi

Ikeh Akinyemi is a software engineer based in Rivers State, Nigeria. He's passionate about learning pure and applied mathematics concepts, open source, and software engineering.

ButterCMS is the #1 rated Headless CMS

G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award

Don’t miss a single post

Get our latest articles, stay updated!