Combine Go and React in single Docker container
Introduction
Recently, I created my personal blog and here is it. I read some articles about combine “Go and React” or “Serve static files on Go”, etc… They’re pretty good, but they lack some points. So I decided to write this article to show you “How can I combine Go and React in single docker container”.
Prerequisites
- Go: In this article I using Go 1.14 and echo-framework to create APIs.
- React: I using React 17.
- Docker: You need install docker to build image.
- Docker-compose (optional): You can use docker-compose to start you container. Docker-compose is convenient. You can start, stop, restart and trace log your container more easy than docker.
Let’s create a simple project
Run go mod init github.com/<username>/go-n-reactjs and create some files
go-n-reactjs/
├── frontend/
├── go.mod
├── docker-compose.yml
├── Dockerfile
├── main.go
├── post.go
└── utils.goCreate Go Server with Echo-framework
Let’s install some libraries:
go get github.com/labstack/echo/v4 // echo-framework
go get github.com/GeertJohan/go.rice // go riceWhat is Go rice?
go.rice is a Go package that makes working with resources such as html, js, css, images and templates easy. During development
go.ricewill load required files directly from disk. Upon deployment it's easy to add all resource files to a executable using thericetool, without changing the source code for your package. go.rice provides methods to add resources to a binary in different scenarios.
Now we are going to create server
This is a simple server I wrote by go using echo-framework .
Now create a simple API and call it’s post APIs.
and a utilities file.
After done 2 files. We going to register post APIs to group API /api/v1 created above.
/**
* Init group API and register API
*/
v1 := server.Group("/api/v1")
RegisterPostAPI(v1)Now you can start server by command to see the result:
go run main.goCreate ReactJS client
After create a simple server. Now we going to create a React project by run:
cd ./frontend && npx create-react-app .Note: You should go inside the frontend folder and create react app in there.
frontend
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
└── yarn.lockAfter you ran the command. You create some files follow the structure bellows
frontend
├── jsconfig.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.js
│ ├── components
│ │ ├── 404.jsx
│ │ ├── home.jsx
│ │ └── post.jsx
│ ├── index.css
│ ├── index.js
│ ├── reportWebVitals.js
│ └── utils
│ ├── api.js
│ └── history.js
└── yarn.lockYou can copy my files in source I place below.
Now is build time
yarn buildand you will receive and build folder. So we done with frontend.
Back to server side
In main.go you create a function is RegisterFrontend
/**
* Register frontend to Server
*/
func RegisterFrontend(e *echo.Echo) {
frontend := rice.MustFindBox("./frontend/build")
fe := http.FileServer(frontend.HTTPBox())
e.GET("/static/*", echo.WrapHandler(fe))
// IMPORTANT STEP
e.GET("/*", func(c echo.Context) error {
index, err := frontend.Open("index.html")
if err != nil {
return err
}
content, err := ioutil.ReadAll(index)
if err != nil {
return err
}
return c.HTMLBlob(http.StatusOK, content)
})
}the complete main.go
Try to run it.
And this is result
Docker time
This is Dockerfile I wrote
So we have to create 3 phases
- First, we build frontend
- Second, copy
buildfolder fromfrontendphases tobuilder. Rice will embedbuildfolder to single go file. - Finally, we copy binary file to
alpineimage and run it.
To run it with docker:
docker build -t go-n-react:latest .
docker run -p 8080:8080 go-n-react:latestWith docker-compose:
// docker-compose.yml
version: "3"
services:
app:
build: .
ports:
- 8080:8080Run it:
docker-compose up --buildYou can now visit http://localhost:8080 and enjoy it.
And this is my source code: Github
Thanks for reading.
P/S: I’m sorry about my english :(
