Skip to content

Instantly share code, notes, and snippets.

@arun0009
Last active June 15, 2025 06:59
Show Gist options
  • Save arun0009/c3017fdc15ebbc6063fd3cd77f8749cd to your computer and use it in GitHub Desktop.
Save arun0009/c3017fdc15ebbc6063fd3cd77f8749cd to your computer and use it in GitHub Desktop.
root = "."
tmp_dir = "tmp"
build_bin = "main"
build_cmd = "dlv debug --headless --listen=:40000 --api-version=2 --accept-multiclient --log --output=./tmp/main"
run_cmd = "./tmp/main"
services:
app:
build:
context: .
target: dev
ports:
- "8080:8080"
- "40000:40000"
volumes:
- .:/app
# Stage 1: Base image with common dependencies
FROM golang:1.23 AS base
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Stage 2: Development image with dlv and air
FROM base AS dev
RUN go install github.com/go-delve/delve/cmd/dlv@latest
RUN go install github.com/air-verse/air@latest
# Expose port 40000 for dlv debugging
EXPOSE 40000
EXPOSE 8080
# Use air for hot reload, with dlv for debugging
CMD ["air", "-c", ".air.toml"]
# Stage 3: Production image
FROM base AS prod
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
EXPOSE 8080
CMD ["./main"]
package main
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/your/module/middleware" // replace with your actual import path
"github.com/go-playground/validator/v10"
)
type UserRequest struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"gte=18,lte=99"`
}
func main() {
e := echo.New()
// Plug in validator
e.Validator = &middleware.ValidatorWithEcho{Validator: validator.New()}
e.POST("/users", func(c echo.Context) error {
req := middleware.GetValidated[UserRequest](c)
return c.JSON(http.StatusOK, echo.Map{"message": "Welcome " + req.Name})
}, middleware.ValidateBody[UserRequest]())
e.Start(":8080")
}
package middleware
import (
"net/http"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
)
type ValidatorWithEcho struct {
Validator *validator.Validate
}
func (v *ValidatorWithEcho) Validate(i interface{}) error {
return v.Validator.Struct(i)
}
func ValidateBody[T any]() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
var req T
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, echo.Map{"error": "Invalid JSON body"})
}
if err := c.Validate(&req); err != nil {
if ve, ok := err.(validator.ValidationErrors); ok {
errors := make(map[string]string)
for _, fe := range ve {
errors[fe.Field()] = simpleMessage(fe)
}
return c.JSON(http.StatusBadRequest, echo.Map{"errors": errors})
}
return c.JSON(http.StatusBadRequest, echo.Map{"error": err.Error()})
}
c.Set("validated", req)
return next(c)
}
}
}
func GetValidated[T any](c echo.Context) T {
v := c.Get("validated")
if typed, ok := v.(T); ok {
return typed
}
var zero T
return zero
}
func simpleMessage(fe validator.FieldError) string {
switch fe.Tag() {
case "required":
return "is required"
case "email":
return "must be a valid email"
case "gte":
return "must be at least " + fe.Param()
case "lte":
return "must be at most " + fe.Param()
default:
return "is invalid"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment