A modern web framework forbuilt on standard libraries.
package main
import "github.com/0mjs/zinc"
func main() {
app := zinc.New()
app.Get("/", func(c *zinc.Context) error {
return c.String("Welcome to Zinc!")
})
app.Serve()
}
Quick Setup
Start building in seconds with minimal configuration and dependencies
Lightweight
221KB
API footprint resulting in an extremely lightweight framework with minimal
dependencies
Beginner-Friendly
Simple and intuitive API with a shallow learning curve, making it easy to get started
Our Philosophy
Aligned with Go Principles
Zinc embraces Go's approach to simplicity and explicitness. We don't hide complexity behind layers of abstraction. Instead, we provide a thin, powerful layer on top of Go's standard library, maintaining type safety and performance.
Performance First
Built specifically for Go 1.22+, Zinc takes advantage of the latest optimizations in the
net/http
package. Our benchmarks show we're consistently among the fastest Go web frameworks
available, with minimal overhead above raw net/http
.
Developer Experience
We believe building APIs should be enjoyable. Zinc's API is designed to be intuitive and predictable, with clear error messages and sensible defaults. Less time debugging, more time building.
Inspired by Express.js
We love the simplicity and elegance of Express.js—its intuitive API for routing, middleware, and parameter handling. Zinc brings that same developer experience to Go, with an API that feels familiar yet perfectly tailored to Go's strengths.
Made for Modern APIs
package user
import (
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/0mjs/zinc"
"github.com/0mjs/zinc/middleware"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/redis/go-redis"
"github.com/segmentio/kafka-go"
)
// User represents a user in our system
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
// Database connection
var db *pgxpool.Pool
var redisClient *redis.Client
var kafkaWriter *kafka.Writer
func main() {
// Initialize the app
app := zinc.New()
// Setup global middleware
app.Use(middleware.Logger())
app.Use(middleware.Recover())
app.Use(middleware.CORS())
// Setup database connections
setupDatabase()
setupRedis()
setupKafka()
// Schedule background jobs
setupCronJobs()
// Group routes with middleware
api := app.Group("/api/v1")
api.Use(AuthMiddleware)
// User routes
api.Get("/users", listUsers)
api.Get("/users/:id", getUserByID)
api.Post("/users", createUser)
api.Put("/users/:id", updateUser)
api.Delete("/users/:id", deleteUser)
// Authentication routes
auth := app.Group("/auth")
auth.Post("/login", handleLogin)
auth.Post("/register", handleRegister)
auth.Post("/refresh", handleRefreshToken)
// Real-time capabilities
api.Get("/events", handleSSE)
app.WebSocket("/ws", handleWebSocket)
// Start the server
app.Serve()
}
// Handles Server-Sent Events for real-time updates
func handleSSE(c *zinc.Context) error {
c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
c.Header("Connection", "keep-alive")
// Send events every second
for i := 0; i < 10; i++ {
c.SSEvent("message", map[string]interface{}{
"time": time.Now().Format(time.RFC3339),
"count": i,
})
c.Flush()
time.Sleep(1 * time.Second)
}
return nil
}
// User handlers
func listUsers(c *zinc.Context) error {
page := c.QueryInt("page", 1)
limit := c.QueryInt("limit", 10)
var users []User
// Query from database
rows, err := db.Query(c.Context(), "SELECT id, name, email, created_at FROM users LIMIT $1 OFFSET $2",
limit, (page-1)*limit)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(map[string]string{
"error": "Database error",
})
}
defer rows.Close()
// Process query results
for rows.Next() {
var user User
if err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt); err != nil {
log.Printf("Error scanning user: %v", err)
continue
}
users = append(users, user)
}
return c.JSON(http.StatusOK, map[string]interface{}{
"users": users,
"page": page,
"limit": limit,
})
}
func getUserByID(c *zinc.Context) error {
id := c.Param("id")
// Check redis cache first
userJSON, err := redisClient.Get(fmt.Sprintf("user:%s", id)).Result()
if err == nil {
// Return cached data
return c.Raw(http.StatusOK, "application/json", []byte(userJSON))
}
// Fetch from database if not in cache
var user User
err = db.QueryRow(c.Context(), "SELECT id, name, email, created_at FROM users WHERE id = $1", id).
Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt)
if err != nil {
return c.Status(http.StatusNotFound).JSON(map[string]string{
"error": "User not found",
})
}
// Cache for future requests
cacheUser(user)
return c.JSON(http.StatusOK, user)
}
// Authentication middleware
func AuthMiddleware(c *zinc.Context) error {
token := c.Header("Authorization")
if token == "" {
return c.Status(http.StatusUnauthorized).JSON(map[string]string{
"error": "No token provided",
})
}
// Validate token
// ...
return c.Next()
}
// File upload handler
func handleFileUpload(c *zinc.Context) error {
file, err := c.FormFile("file")
if err != nil {
return c.Status(http.StatusBadRequest).JSON(map[string]string{
"error": "No file uploaded",
})
}
// Save file to disk
dst := fmt.Sprintf("./uploads/%s", file.Filename)
if err := c.SaveUploadedFile(file, dst); err != nil {
return c.Status(http.StatusInternalServerError).JSON(map[string]string{
"error": "Failed to save file",
})
}
return c.JSON(http.StatusOK, map[string]string{
"filename": file.Filename,
"size": fmt.Sprintf("%d bytes", file.Size),
})
}
// Setup utilities
func setupDatabase() {
connString := os.Getenv("DATABASE_URL")
if connString == "" {
connString = "postgres://postgres:postgres@localhost:5432/app"
}
var err error
db, err = pgxpool.New(context.Background(), connString)
if err != nil {
log.Fatalf("Unable to connect to database: %v\n", err)
}
}
func setupRedis() {
redisClient = redis.NewClient(&redis.Options{
Addr: os.Getenv("REDIS_URL"),
})
// Test connection
_, err := redisClient.Ping().Result()
if err != nil {
log.Printf("Warning: Redis connection failed: %v\n", err)
}
}
func setupKafka() {
kafkaWriter = &kafka.Writer{
Addr: kafka.TCP(os.Getenv("KAFKA_BROKERS")),
Topic: "user-events",
Balancer: &kafka.LeastBytes{},
}
}
func setupCronJobs() {
c := cron.New()
// Run daily at midnight
c.AddFunc("0 0 * * *", func() {
log.Println("Running daily cleanup job")
// Clean up expired tokens, etc.
})
// Run every hour
c.AddFunc("@hourly", func() {
log.Println("Running hourly stats collection")
// Collect and aggregate statistics
})
c.Start()
}
func cacheUser(user User) {
// Cache user data in Redis
userJSON, _ := json.Marshal(user)
redisClient.Set(fmt.Sprintf("user:%s", user.ID), string(userJSON), 15*time.Minute)
}
func publishUserEvent(eventType string, userID string) {
// Publish event to Kafka
message := kafka.Message{
Key: []byte(userID),
Value: []byte(fmt.Sprintf(`{"type":"%s","user_id":"%s","timestamp":"%s"}`,
eventType, userID, time.Now().Format(time.RFC3339))),
}
if err := kafkaWriter.WriteMessages(context.Background(), message); err != nil {
log.Printf("Failed to publish Kafka message: %v", err)
}
}