Skip to main content

Gatsby Blog with a MeiLi Search Backend

Shenzhen, China

I already looked into how to:

The next step is to wrap the pre-rendered build inside a Docker container that uses goFiber to serve the generated static HTML/CSS/JS/JSON code.

Prepare the goFiber Webserver

The code for the webserver is fairly simple - very similar to Express.js:

package main

import (
	"flag"
	"log"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/fiber/v2/middleware/recover"
)

var (
	port = flag.String("port", ":8888", "Port to listen on")
	prod = flag.Bool("prod", false, "Enable prefork in Production")
)

func main() {

	// Create fiber app

	// Development
	// app := fiber.New(fiber.Config{
	// 	Prefork: *prod, // go run app.go -prod
	// })

	// Production
	app := fiber.New(fiber.Config{
		Prefork: true,
	})

	// Middleware
	app.Use(recover.New())
	app.Use(logger.New())

	// Setup static files
	app.Static("/", "./data/public")

	// Listen on port 8888
	log.Fatal(app.Listen(*port)) // go run app.go -port=:8888
}

The webserver expects our static code - generated from our React.js MeiLi search interface and pre-rendered by Gatsby.js - in the public folder. It will then serve it on / with port 8888.

Building the Go App

We can now use Docker to build the webserver inside a Golang Container and then transfer our website code and the build binary into a tiny Alpine Container:

# Building the binary of the App
FROM golang:alpine AS build

# Project labels
LABEL maintainer="m.polinowski@gmail.com"

# `build` can be replaced with your project name
WORKDIR /go/src/build

# Copy all the Code and stuff to compile everything
COPY ./container/* ./

# Downloads all the dependencies in advance (could be left out, but it's more clear this way)
RUN go mod download

# Builds the application as a staticly linked one, to allow it to run on alpine
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o app .

# Moving the binary to the 'final Image' to make it smaller
FROM alpine:latest

WORKDIR /app

# Create the `public` dir and copy all the assets into it
RUN mkdir ./data
COPY ./container/data ./data

# `build` can be replaced here as well
COPY --from=build /go/src/build/app .

# Exposes port 8888 because our program listens on that port
EXPOSE 8888

# CMD ["./app"]
RUN chmod +x ./data/run.sh
CMD ["ash", "./data/run.sh"]

CI Pipeline

Now I would use a Gitlab CI pipeline to do my Gatsby.js build as well as the build described above. But this also works locally with a npm script that we can add to the package.json file of our Gatsby.js app:

"scripts": {
    "build": "node --max-old-space-size=8192 node_modules/gatsby/dist/bin/gatsby build",
    "develop": "node --max-old-space-size=8192 node_modules/gatsby/dist/bin/gatsby develop",
    "start": "npm run develop",
    "serve": "gatsby serve",
    "clean": "gatsby clean",
    "docker": "mv public/* docker/container/data/public && docker build -t my_blog docker/. && mv docker/container/data/public/* public"
  }

So now we can enter the root dir of our Gatsby.js app and execute:

npm run build
npm run docker

Now we have both container images ready to be served:

REPOSITORY             TAG      SIZE
my_blog                latest   23.4MB
getmeili/meilisearch   latest   258MB

Docker Compose

Now we can write a docker-compose.yml that brings it all together for us - Note that I ran into an issue here. I am building the Gatsby.js page while starting the MeiLi Search container manually (without Compose). I am connecting to the service on localhost. If I now use a virtual network for those container I will have to change the connection URL from localhost:7700 to meilisearch:7700. But to keep this simple I just run the container on the host network stack here - so nothing needs to be changed:

version: '3'

services:
  meilisearch:
    container_name: meilisearch
    image: getmeili/meilisearch:latest
    environment:
      - MEILI_MASTER_KEY=RhTX1pLPSKSn7KW9yf9u_MNKC0v1YKkmx2Sc6qSwbLQ
      - MEILI_NO_ANALYTICS=true
      # - MEILI_ENV=development
      - MEILI_ENV=production
    network_mode: "host"
    # ports:
    #   - ${MEILI_PORT:-7700}:7700
    # networks:
    #   - meilisearch
    volumes:
      - /opt/meili_data:/meili_data
    restart: unless-stopped

  gatsby_frontend:
    container_name: my_blog
    image: my_blog:latest
    network_mode: "host"    
    # ports:
    #   - 8888:8888
    # networks:
    #   - meilisearch
    restart: unless-stopped

# networks:
#   meilisearch:
#     driver: bridge

You can now bring the application up with:

docker-compose up

and visit http://localhost:8888 and should see the Gatsby.js frontend being served by your goFiber webserver connecting to your MeiLi Search backend:

Gatsby Blog with a MeiLi Search Backend

👍