Building and Deploying GCP Cloud Pub/Sub trigger Function with Reusable Golang Code

Chazool
4 min readMay 31, 2024

--

Google Cloud Functions offer a serverless platform to build and deploy event-driven applications. Leveraging GoLang for this purpose can be efficient due to its concurrency support and performance. In this guide, we’ll walk through the process of building and deploying GCP Cloud Functions using reusable GoLang code.

Setup Environment

Ensure you have the Google Cloud SDK installed and authenticated. Install the Go programming language and set up your development environment. Also, make sure you have Go Modules enabled for dependency management.

Directory Structure

PUBSUB_CLOUD_FUNC
└── app
├── dto
│ └── item.go
├── repository
│ └── item_repo.go
├── service
│ └── item_service.go
└── cloud_func
└── new_item
├── go.mod
├── go.sum
├── key.json
└── new_item.go
└── pkg
└── util.go
└── go.mod

Implement reusable code

First, let’s create a new Go module for our project

mkdir pubsub_cloud_func
cd pubsub_cloud_func
go mod init github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc

repository Package

Define your data access logic in this package.

package repository

import (
"cmp"

"slices"

"github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc/app/dto"
)

var items = make([]dto.Item, 0, 100)

type ItemRepo interface {
New(i dto.Item)
Get(id uint) *dto.Item
}

type ItemRepoImpl struct {
}

func NewItemRepo() ItemRepo {
return ItemRepoImpl{}
}

func (repo ItemRepoImpl) Get(id uint) *dto.Item {
i, _ := slices.BinarySearchFunc(items, dto.Item{ID: id}, func(e, t dto.Item) int {
return cmp.Compare(e.ID, t.ID)
})
return &items[i]
}

func (repo ItemRepoImpl) New(i dto.Item) {
items = append(items, i)
}

service Package

Define your business logic in this package.

package service

import (
"errors"

"github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc/app/dto"
"github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc/app/repository"
)

type ItemService interface {
New(item dto.Item) error
}

type ItemServiceImpl struct {
ItemRepo repository.ItemRepo
}

func NewItemService() ItemService {
return ItemServiceImpl{
ItemRepo: repository.NewItemRepo(),
}
}

func (srv ItemServiceImpl) New(i dto.Item) error {
item := srv.ItemRepo.Get(i.ID)
if item != nil {
return errors.New("item already exists")
}

srv.ItemRepo.New(i)
return nil
}

utils Package

Define your utility functions in this package.

package pkg

import (
"encoding/json"
"log"
)

func StructBuilder[T any](data []byte) (T, error) {
var v T
err := json.Unmarshal(data, &v)
if err != nil {
log.Println("Failed to unmarshal JSON", err)
return v, err
}

return v, nil
}

Write Cloud Function

Now create the cloud_func folder and initialize a Go module for it:

mkdir cloud-function
cd cloud-function
go mod init github.com/chazool/go_medium_samples/pubsub_cloud_func/cloud_func/new_item

go.mod File

module github.com/chazool/go_medium_samples/pubsub_cloud_func/cloud_func/new_item

go 1.22.2

replace github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc => ../../

require (
github.com/GoogleCloudPlatform/functions-framework-go v1.8.1
github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc v0.0.0-00010101000000-000000000000
github.com/cloudevents/sdk-go/v2 v2.14.0

)

require (
cloud.google.com/go/functions v1.15.3 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/json-iterator/go v1.1.10 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
go.uber.org/atomic v1.4.0 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.10.0 // indirect
)

Implement the Cloud Function

Create a new Go file for your Cloud Function. Let’s call it new_item.go. This function will be triggered by Pub/Sub messages.

package new_item

import (
"context"
"fmt"
"log"

"github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc/app/dto"
"github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc/app/service"
"github.com/chazool/go_medium_samples/pubsub_cloud_func/pubsubcloudfunc/pkg"

_ "github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"github.com/cloudevents/sdk-go/v2/event"
)

func init() {
functions.CloudEvent("newItem", NewItem)
}

type MessagePublishedData struct {
Message PubSubMessage
}

type PubSubMessage struct {
Data []byte `json:"data"`
}

func NewItem(ctx context.Context, e event.Event) error {
var msg MessagePublishedData

// Convert event data to MessagePublishedData struct
if err := e.DataAs(&msg); err != nil {
log.Printf("error converting event data: %v", err)
return fmt.Errorf("event.DataAs: %w", err)
}

// Build an Item struct from the message data
i, err := pkg.StructBuilder[dto.Item](msg.Message.Data)
if err != nil {
log.Printf("error building item structure: %v", err)
return fmt.Errorf("struct builder: %w", err)
}

// Attempt to add the new item using the service
srv := service.NewItemService()
if err := srv.New(i); err != nil {
log.Printf("error occurred while adding new item: %v", err)
return fmt.Errorf("adding item: %w", err)
}

return nil
}

Deploying the Cloud Function

If you are using any third-party dependencies, you can vendor them using go mod vendor. This will create a vendor directory containing all the dependencies.

Create a Pub/Sub Topic

Before deploying the Cloud Function, let’s create a Pub/Sub topic.

gcloud pubsub topics create new_item

Deploy the Cloud Function

Now, it’s time to deploy your Cloud Function to Google Cloud Platform. Make sure you have the Google Cloud SDK installed and configured with your GCP project.

gcloud functions deploy new_item_test \
--gen2 \
--runtime go122 \
--trigger-topic new_item \
--source=. \
--entry-point=newItem \
--region us-central1

Conclusion

That’s it! You’ve now successfully built and deployed a Google Cloud Function triggered by Pub/Sub events using Go, with the ability to reuse common code across functions. This approach enhances scalability, maintainability, and efficiency in your event-driven architecture on Google Cloud Platform.

For further reference and to explore the code used in this tutorial, you can find the repository here.

— Happy coding! 💻😊 —

--

--

No responses yet