👋

Welcome!

A modern web framework forbuilt on standard libraries.

High Performance Go Idiomatic Minimal API
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)
	}
}
Intuitive routing
Simple middleware
JSON handling
Type-safe
Route groups
Fast performance