Skip to content

Latest commit

 

History

History
421 lines (319 loc) · 9.63 KB

README.md

File metadata and controls

421 lines (319 loc) · 9.63 KB
title keywords description
Firebase Functions
firebase
functions
deployment
gcloud
cloud
Using Firebase Functions.

Deploying GoFiber Application to Firebase Functions

Github StackBlitz

Welcome to this step-by-step guide on deploying a GoFiber application to Firebase Functions. If you’re looking to leverage the power of GoFiber, a fast and lightweight web framework for Go, and host your application on Firebase, you’re in the right place. In this tutorial, we’ll walk through the process of setting up your GoFiber app to run seamlessly on Firebase Functions.

Prerequisites

  1. Go installed on your machine.
  2. Firebase CLI installed.
  3. A Firebase project created.
  4. Firestore and Cloud Functions enabled.

Create a GoFiber App

Start by initializing your GoFiber application. Use the following commands in your terminal:

go mod init example.com/GofiberFirebaseBoilerplate

Server Configuration

Create a server file (src/server.go) with a CreateServer function that sets up your GoFiber server.

package src

import (
 "example.com/GofiberFirebaseBoilerplate/src/routes"

 "github.com/gofiber/fiber/v2"
)

func CreateServer() *fiber.App {
 version := "v1.0.0"

 app := fiber.New(fiber.Config{
  ServerHeader: "Gofiber Firebase Boilerplate",
  AppName:      "Gofiber Firebase Boilerplate " + version,
 })

 app.Get("/", func(c *fiber.Ctx) error {
  return c.SendString("Gofiber Firebase Boilerplate " + version)
 })

 routes.New().Setup(app)

 return app
}

Routes Configuration

Now that your GoFiber application is initialized, let’s delve into setting up and configuring routes. This section is crucial for defining how your application handles incoming requests. Open the src/routes/routes.go file to manage your routes.

package routes

import (
 "example.com/GofiberFirebaseBoilerplate/src/database"
 "example.com/GofiberFirebaseBoilerplate/src/repositories"

 "github.com/gofiber/fiber/v2"
)

type Routes struct {
 mainRepository *repositories.MainRepository
}

func New() *Routes {
 db := database.NewConnection()
 return &Routes{mainRepository: &repositories.MainRepository{DB: db}}
}

func (r *Routes) Setup(app *fiber.App) {
 app.Post("message", r.insertMessage)
}

func (r *Routes) insertMessage(c *fiber.Ctx) error {
 return c.SendString("ok")
}

Database Configuration

Configure your Firestore database connection in the src/database/database.go file. Make sure to replace the placeholder credentials with your Firebase project's actual credentials.

package database

import (
 "context"
 "encoding/json"
 "log"

 "cloud.google.com/go/firestore"
 firebase "firebase.google.com/go"

 "google.golang.org/api/option"
)

type Config struct {
 Host     string
 Port     string
 Password string
 User     string
 DBName   string
 SSLMode  string
}

func NewConnection() *firestore.Client {

 ctx := context.Background()

 sa := option.WithCredentialsJSON(credentials())
 app, err := firebase.NewApp(ctx, nil, sa)
 if err != nil {
  log.Fatalf("functions.init: NewApp %v\n", err)
 }

 db, err := app.Firestore(ctx)
 if err != nil {
  log.Fatalf("functions.init: Database init : %v\n", err)
 }

 return db
}

func credentials() []byte {
 // TODO: Replace with your Credentials
 data := map[string]interface{}{
  "type":                        "",
  "project_id":                  "",
  "private_key_id":              "",
  "private_key":                 "",
  "client_email":                "",
  "client_id":                   "",
  "auth_uri":                    "",
  "token_uri":                   "",
  "auth_provider_x509_cert_url": "",
  "client_x509_cert_url":        "",
  "universe_domain":             "",
 }

 bytes, err := json.Marshal(data)
 if err != nil {
  panic(err)
 }

 return bytes
}

Repository Pattern

Implement the repository pattern in the src/repositories/main.repository.go file to interact with Firestore. This file includes an example of inserting a message into the database.

package repositories

import (
 "context"

 "cloud.google.com/go/firestore"
 "example.com/GofiberFirebaseBoilerplate/src/models"
 "github.com/google/uuid"
)

type MainRepository struct {
 DB *firestore.Client
}

func (r *MainRepository) InsertMessage(body *models.MessageInputBody) error {
 id := uuid.New().String()
 _, err := r.DB.Collection("messages").Doc(id).Set(context.Background(), body)
 return err
}

Model Definition

Define a message input model in src/models/message_input_body.go to structure the data you'll be working with.

package models

type MessageInputBody struct {
 From    string `json:"from"`
 To      string `json:"to"`
 Message string `json:"message"`
}

Functions for Cloud Integration

In functions.go, convert Google Cloud Function requests to Fiber and route them to your application. This file includes functions to facilitate the integration of Google Cloud Functions and GoFiber.

package app

import (
 "bytes"
 "context"
 "fmt"
 "io"
 "log"
 "net"
 "net/http"
 "strings"

 "github.com/gofiber/fiber/v2"
 "github.com/valyala/fasthttp/fasthttputil"
)

// CloudFunctionRouteToFiber route cloud function http.Handler to *fiber.App
// Internally, google calls the function with the /execute base URL
func CloudFunctionRouteToFiber(fiberApp *fiber.App, w http.ResponseWriter, r *http.Request) error {
 return RouteToFiber(fiberApp, w, r, "/execute")
}

// RouteToFiber route http.Handler to *fiber.App
func RouteToFiber(fiberApp *fiber.App, w http.ResponseWriter, r *http.Request, rootURL ...string) error {
 ln := fasthttputil.NewInmemoryListener()
 defer ln.Close()

 // Copy request
 body, err := io.ReadAll(r.Body)
 if err != nil {
  return err
 }

 url := fmt.Sprintf("%s://%s%s", "http", "0.0.0.0", r.RequestURI)
 if len(rootURL) > 0 {
  url = strings.Replace(url, rootURL[0], "", -1)
 }

 proxyReq, err := http.NewRequest(r.Method, url, bytes.NewReader(body))

 if err != nil {
  return err
 }

 proxyReq.Header = r.Header

 // Create http client
 client := http.Client{
  Transport: &http.Transport{
   DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
    return ln.Dial()
   },
  },
 }

 // Serve request to internal HTTP client
 go func() {
  log.Fatal(fiberApp.Listener(ln))
 }()

 // Call internal Fiber API
 response, err := client.Do(proxyReq)
 if err != nil {
  return err
 }

 // Copy response and headers
 for k, values := range response.Header {
  for _, v := range values {
   w.Header().Set(k, v)
  }
 }
 w.WriteHeader(response.StatusCode)

 io.Copy(w, response.Body)
 response.Body.Close()

 return nil
}

Main Application Entry

In main.go, initialize your GoFiber app and start the server. This file also includes an exported Cloud Function handler for deployment.

package app

import (
 "fmt"
 "net/http"
 "strings"

 "example.com/GofiberFirebaseBoilerplate/src"
 "github.com/gofiber/fiber/v2"
)

var app *fiber.App

func init() {
 app = src.CreateServer()
}

// Start start Fiber app with normal interface
func Start(addr string) error {
 if -1 == strings.IndexByte(addr, ':') {
  addr = ":" + addr
 }

 return app.Listen(addr)
}

// MyCloudFunction Exported http.HandlerFunc to be deployed to as a Cloud Function
func MyCloudFunction(w http.ResponseWriter, r *http.Request) {
 err := CloudFunctionRouteToFiber(app, w, r)
 if err != nil {
  fmt.Fprintf(w, "err : %v", err)
  return
 }
}

Development

For local development, utilize the cmd/main.go file. If you prefer hot reloading, the .air.toml configuration file is included for use Air.

cmd/main.go

package main

import (
 "log"
 "os"

 app "example.com/GofiberFirebaseBoilerplate"
)

func main() {

 port := "3001"
 if envPort := os.Getenv("PORT"); envPort != "" {
  port = envPort
 }

 if err := app.Start(port); err != nil {
  log.Fatalf("app.Start: %v\n", err)
 }
}

.air.toml

root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
  args_bin = []
  bin = "./tmp/main"
  cmd = "go build -o ./tmp/main ./cmd"
  delay = 1000
  exclude_dir = ["assets", "tmp", "vendor", "testdata"]
  exclude_file = []
  exclude_regex = ["_test.go"]
  exclude_unchanged = false
  follow_symlink = false
  full_bin = ""
  include_dir = []
  include_ext = ["go", "tpl", "tmpl", "html"]
  include_file = []
  kill_delay = "0s"
  log = "build-errors.log"
  poll = false
  poll_interval = 0
  post_cmd = []
  pre_cmd = []
  rerun = false
  rerun_delay = 500
  send_interrupt = false
  stop_on_error = false

[color]
  app = ""
  build = "yellow"
  main = "magenta"
  runner = "green"
  watcher = "cyan"

[log]
  main_only = false
  time = false

[misc]
  clean_on_exit = false

[screen]
  clear_on_rebuild = false
  keep_scroll = true

Deployment

Deploy your Cloud Function using the following commands, replacing <YourProjectID> with your Firebase project ID:

gcloud config set project <YourProjectID>
gcloud functions deploy MyCloudFunction --runtime go120 --trigger-http

Conclusion

Congratulations! You’ve successfully configured and deployed a GoFiber application on Firebase Functions. This powerful combination allows you to build fast and efficient serverless applications. Experiment further with GoFiber features and Firebase integrations to unlock the full potential of your serverless architecture. Happy coding!

Medium Post

https://medium.com/@kmltrk07/how-to-deploy-gofiber-app-to-firebase-functions-8d4d537a4464