In this article, we will walk through the process of deploying a simple Go REST API with a Postgres database using Docker Compose.
Docker Compose allows us to define and manage multi-container Docker applications.
We will set up the Go REST API server, configure the Postgres database, and run them together in a Docker environment.
Prerequisites
Before proceeding, make sure you have the following prerequisites installed on your machine:
- Docker: Install Docker
- Docker Compose: Install Docker Compose
Setting up the Go REST API
First, let's create a directory for our project and navigate into it:
mkdir go-api cd go-api
Create a new Go module:
go mod init github.com/your-username/go-api
Next, let's create a main file
main.go
and define our simple REST API:package main import ( "database/sql" "encoding/json" "log" "net/http" "os" "github.com/gorilla/mux" _ "github.com/lib/pq" ) // Book struct type Book struct { ID int `json:"id"` Title string `json:"title"` Author string `json:"author"` } var db *sql.DB func main() { // Get database connection details from environment variables dbHost := os.Getenv("DB_HOST") dbPort := os.Getenv("DB_PORT") dbUser := os.Getenv("DB_USER") dbPassword := os.Getenv("DB_PASSWORD") dbName := os.Getenv("DB_NAME") // Connect to the Postgres database dbInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", dbHost, dbPort, dbUser, dbPassword, dbName) var err error db, err = sql.Open("postgres", dbInfo) if err != nil { log.Fatal(err) } defer db.Close() // Initialize the router router := mux.NewRouter() // Define API routes router.HandleFunc("/books", getBooks).Methods("GET") router.HandleFunc("/books/{id}", getBook).Methods("GET") router.HandleFunc("/books", createBook).Methods("POST") router.HandleFunc("/books/{id}", updateBook).Methods("PUT") router.HandleFunc("/books/{id}", deleteBook).Methods("DELETE") // Start the server log.Fatal(http.ListenAndServe(":8000", router)) } // Get all books func getBooks(w http.ResponseWriter, r *http.Request) { rows, err := db.Query("SELECT * FROM books") if err != nil { log.Fatal(err) } defer rows.Close() books := []Book{} for rows.Next() { book := Book{} err := rows.Scan(&book.ID, &book.Title, &book.Author) if err != nil { log.Fatal(err) } books = append(books, book) } json.NewEncoder(w).Encode(books) } // Get a single book by ID func getBook(w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] row := db.QueryRow("SELECT * FROM books WHERE id = $1", id) book := Book{} err := row.Scan(&book.ID, &book.Title, &book.Author) if err != nil { log.Fatal(err) } json.NewEncoder(w).Encode(book) } // Create a new book func createBook(w http.ResponseWriter, r *http.Request) { var book Book json.NewDecoder(r.Body).Decode(&book) _, err := db.Exec("INSERT INTO books (title, author) VALUES ($1, $2)", book.Title, book.Author) if err != nil { log.Fatal(err) } w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(book) } // Update a book func updateBook(w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] var book Book json.NewDecoder(r.Body).Decode(&book) _, err := db.Exec("UPDATE books SET title = $1, author = $2 WHERE id = $3", book.Title, book.Author, id) if err != nil { log.Fatal(err) } json.NewEncoder(w).Encode(book) } // Delete a book func deleteBook(w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] _, err := db.Exec("DELETE FROM books WHERE id = $1", id) if err != nil { log.Fatal(err) } w.WriteHeader(http.StatusNoContent) }
This is a basic Go REST API that handles CRUD operations for books. We use the
gorilla/mux
package for routing and the lib/pq
package for connecting to the Postgres database.Setting up Docker Compose
Create a new file named
docker-compose.yml
in the project directory and add the following configuration:version: '3' services: api: build: context: . dockerfile: Dockerfile ports: - 8000:8000 environment: - DB_HOST=postgres - DB_PORT=5432 - DB_USER=postgres - DB_PASSWORD=secret - DB_NAME=books depends_on: - postgres postgres: image: postgres:latest environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=secret - POSTGRES_DB=books
The
docker-compose.yml
file defines two services: api
and postgres
. The api
service is built from the current directory using the Dockerfile
(which we will create next). It exposes port 8000 for the Go API and sets environment variables for the database connection. The postgres
service uses the latest Postgres image and sets environment variables for the default Postgres user, password, and database.Creating the Dockerfile
Create a new file named
Dockerfile
in the project directory and add the following content:# Build stage FROM golang:latest AS builder WORKDIR /app COPY go.mod . COPY go.sum . RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . # Final stage FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/main . CMD ["./main"]
The Dockerfile sets up a multi-stage build. In the first stage, it downloads the Go dependencies and builds the Go binary. In the second stage, it copies the binary from the builder stage and sets up the final container.
Build and Run the Application
To build and run the application, open a terminal in the project directory and run the following command:
docker-compose up --build
This command will build the Go API and Postgres containers and start them. You should see the logs for both services in the terminal.
Test the API
Once the containers are up and running, you can test the API using a tool like Postman or cURL. Here are some example requests:
- GET http://localhost:8000/books - Get all books
- GET http://localhost:8000/books/{id} - Get a book by ID
- POST http://localhost:8000/books - Create a new book
- PUT http://localhost:8000/books/{id} - Update a book by ID
- DELETE http://localhost:8000/books/{id} - Delete a book by ID
Conclusion
In this article, we learned how to deploy a simple Go REST API with a Postgres database using Docker Compose. Docker Compose allows us to define and manage multi-container applications easily. We set up the Go API server, configured the Postgres database, and ran them together in a Docker environment.
You can find the complete source code for this example on GitHub.
For more information on Docker Compose, refer to the official documentation.
For more information on Go, refer to the official documentation.
For more information on Postgres, refer to the official documentation.