A flexible, framework-agnostic Go package for handling idempotency in HTTP APIs.
- β
Framework Agnostic: Works with Gin, standard
net/http, Echo, and more. - β Multiple Storage Backends: In-memory, Redis, SQL, and GORM support.
- β Database Agnostic: Use any DB with GORM (PostgreSQL, MySQL, SQL Server, SQLite).
- β Distributed Locking: Built-in support for multiple instances.
- β Production Ready: Comprehensive testing, benchmarks, and CI/CD.
go get github.com/fco-gt/gopotencypackage main
import (
"github.com/fco-gt/gopotency"
ginmw "github.com/fco-gt/gopotency/middleware/gin"
"github.com/fco-gt/gopotency/storage/memory"
"github.com/gin-gonic/gin"
"time"
)
func main() {
store := memory.NewMemoryStorage()
manager, _ := idempotency.NewManager(idempotency.Config{
Storage: store,
TTL: 24 * time.Hour,
})
r := gin.Default()
r.Use(ginmw.Idempotency(manager))
r.POST("/orders", func(c *gin.Context) {
c.JSON(201, gin.H{"order_id": "ORD-123", "status": "created"})
})
r.Run(":8080")
}package main
import (
"github.com/fco-gt/gopotency"
httpmw "github.com/fco-gt/gopotency/middleware/http"
"github.com/fco-gt/gopotency/storage/memory"
"net/http"
"time"
)
func main() {
store := memory.NewMemoryStorage()
manager, _ := idempotency.NewManager(idempotency.Config{
Storage: store,
TTL: 24 * time.Hour,
})
mux := http.NewServeMux()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"status": "processed"}`))
})
mux.Handle("/process", httpmw.Idempotency(manager)(handler))
http.ListenAndServe(":8080", mux)
}type Config struct {
Storage Storage // Required: Memory, Redis, SQL, or GORM
TTL time.Duration // Default: 24h
LockTimeout time.Duration // Default: 5m
KeyStrategy KeyStrategy // Default: HeaderBased("Idempotency-Key")
AllowedMethods []string // Default: ["POST", "PUT", "PATCH", "DELETE"]
RequireKey bool // If true, returns 400 if key is missing (Default: false)
ErrorHandler func(error) (int, any)
}GoPotency allows you to be granular. If you provide an Idempotency-Key in the request, the middleware will process it regardless of the method.
For critical routes, you can enable RequireKey: true to ensure no one accidentally skips idempotency.
import "github.com/fco-gt/gopotency/storage/memory"
store := memory.NewMemoryStorage()import "github.com/fco-gt/gopotency/storage/redis"
store, err := redis.NewRedisStorage(ctx, "localhost:6379", "password")import (
idempotencyGorm "github.com/fco-gt/gopotency/storage/gorm"
"gorm.io/gorm"
)
store := idempotencyGorm.NewGormStorage(db)import idempotencySQL "github.com/fco-gt/gopotency/storage/sql"
store := idempotencySQL.NewSQLStorage(db, "idempotency_records")We use a Makefile to streamline development:
make test # Run all tests
make bench # Run performance benchmarks
make build # Build all examplesGoPotency is optimized for high-performance APIs.
| Operation | Time |
|---|---|
| Idempotency Check | ~520 ns/op |
| Full Flow (Lock/Store) | ~1500 ns/op |
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Distributed under the MIT License. See LICENSE for more information.