Skip to content

jose-lico/go-plate

Repository files navigation

go-plate

Opinionated Go REST Backend Boilerplate. Using go 1.22.0.

As I've developed my Glassdoor clone Compared and a few other smaller apps, I've tinkered around with Go a bit. I've tried quite a few different project structures and techniques, experimenting to find what works best for me.

After all that trial and error, I decided to put together this Go backend boilerplate project. It is supposed to serve as a distillation of all the lessons I've learned and a starting point that incorporates the patterns and practices I've found to be most effective. Something that allows me (and maybe you) to spin up a new project fast and be productive.

It also includes some examples of how to use it and how I like to structure my backends.

Install

go get github.com/jose-lico/go-plate

Overview & Features

  • Routing with chi (Lightweight, 100% compatible with net/http)
  • Environment variable management using godotenv
  • CORS handling with cors
  • Containerization with Docker
  • API versioning via URL paths (/api/v2/posts) and custom headers (X-API-Version: v1)
  • Request payload validation using validator
  • SQL (PostgreSQL) integration with gorm ORM
  • Database schema management with migrate for version-controlled and reproducible migrations
  • Redis caching implementation with go-redis
  • Secure password hashing and verification
  • Authentication middleware with session management (Redis-backed)
  • Rate Limiting (Implemented Token Bucket & Sliding Window, with in-memory storage for local rate limiting, and Redis for distributed systems across multiple server instances)
  • CI/CD pipeline for GCP Cloud Run service
  • CI/CD pipeline for AWS App Runner service
  • Example endpoints to showcase functionality and use
  • Documentation generation with Swagger

How to use

To get started, create a new APIServer object:

import "github.com/jose-lico/go-plate/api"

func main() {
	cfg := config.NewAPIConfig()
	api := api.NewAPIServer(cfg)
	api.UseDefaultMiddleware()

	api.Run()
}

Register some endpoints:

import "github.com/jose-lico/go-plate/api"

func main() {
	...

	api.Router.Get("/", hello)
}

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, World!")
}

Connect to Redis and PostgreSQL:

import "github.com/jose-lico/go-plate/database"

func main() {
	redisCFG := config.NewRedisConfig()
	redis, err := database.NewRedis(redisCFG)
	if err != nil {
		...
	}

	sqlCFG := config.NewSQLConfig()
	sql, err := database.NewSQLGormDB(sqlCFG)
	if err != nil {
		...
	}
}

Create some server-wide and endpoint specfic middleware:

import (
	"github.com/jose-lico/go-plate/api"
	"github.com/jose-lico/go-plate/middleware"
	"github.com/jose-lico/go-plate/ratelimiting"
)

func main() {
	...

	api.Router.Group(func(r chi.Router) {
		r.Use(middleware.RateLimitMiddleware(ratelimiting.NewInMemoryTokenBucket(0.05, 3, 10*time.Minute)))

		r.Get("/rate-limited", hello)
	})
}

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, World!")
}

Structure

.
├── api
│   ├── api.go				// Server
├── auth
│   ├── password.go			// Hash and compare password
│   ├── token.go			// Generate random 32 byte token
├── config
│   ├── api_config.go			// API configuration
│   ├── redis_config.go			// Redis configuration
│   └── sql_config.go			// SQL configuration
├── database
│   ├── redis.go			// Redis interface, implemented with go-redis
│   └── sql_gorm.go			// SQL interface, using gorm
├── middleware
│   ├── rate_limit.go			// Rate litiming with algorithm of choice
│   ├── session.go			// Session with redis
│   └── versioning.go			// API versioning
├── ratelimiting
│   ├── mem_sliding_window.go		// In-memory Sliding Window
│   ├── mem_token_bucket.go		// In-memory Token Bucket
│   ├── rate_limiter.go			// Rate Limiter interface
│   └── redis_token_bucket.go		// Redis Token Bucket
├── utils
│   └── utils.go			// Utils functions

Examples

To run the examples in /examples, copy .env.example to .env with your variables or otherwise inject them. For docker, a sample docker-compose.yml is at the root with these variables.

At the root of the project there is a Makefile with some util commands to run these examples.

Go commands

make dev -> go run with live reload thru air

make run -> go run

make build -> go build

make gen-docs -> generates API docs

Docker commands

make up -> Spins up docker containers with docker-compose

make down -> Spins down docker containers with docker-compose

make down -v -> Spins down docker containers and volumes with docker-compose

Migrations

Local

make migration name=<migration_name> -> Create a new migration

make migrate-up -> Runs up migrations

make migrate-down -> Runs down migrations

Docker

migrate-up-docker -> Runs up migrations

migrate-down-docker -> Runs down migrations

About

Opinionated Go REST Backend Boilerplate

Resources

License

Stars

Watchers

Forks