Go URL Shortener 🔗
This is a high-performance URL shortener service built in Go, designed as an educational project to explore system design, API development, and database integration. It provides a simple API to create and resolve short URLs, with a pluggable repository layer supporting both in-memory and MongoDB persistent storage.
- API Endpoints: Provides
POST /shortento create links andGET /{code}to handle redirects. - Dual Storage Modes: A toggleable persistence layer, configurable via an environment variable.
- In-Memory: An ephemeral in-memory map for rapid development and testing.
- MongoDB: A persistent MongoDB backend for production use.
- Short Code Generation: Supports both user-defined custom codes and random 6-character alphanumeric code generation.
- Middleware: Includes an in-memory, token-bucket rate limiter and configurable CORS middleware for API hardening.
- Configuration: All settings are managed via environment variables for easy setup and deployment.
- Containerization: Fully containerized with a multi-stage
Dockerfilefor efficient and repeatable production builds.
- Backend: Go (v1.22.5)
- Router: Standard Library
http.ServeMux - Database: MongoDB (using
go.mongodb.org/mongo-driver) - Middleware:
github.com/rs/corsand a custom token-bucket rate limiter. - Deployment: Docker (Future implementations!!)
This project follows a clean, layered architecture to separate concerns and promote modularity.
- Handlers (
internal/handlers): Responsible for parsing HTTP requests, validating input, and serializing JSON responses. - Services (
internal/services): Contains the core business logic, such as validating a URL, generating a short code, and checking for expiration. - Repository (
internal/repository): An interface-based layer (Repository) that abstracts all data storage operations. This allows the service layer to be agnostic of the database. - Main (
cmd/server): The application entry point responsible for initializing the database connection, injecting dependencies (like theMongoRepoorMemoryRepointo theURLService), setting up the router, and starting the HTTP server.
- Go 1.22.5 or later
- Docker (for containerized deployment)
- A MongoDB Atlas account or a local MongoDB instance (if using
USE_MONGO=true)
- Copy the example environment file:
cp .env.example .env
- Edit the
.envfile with your settings:USE_MONGO: Set totrueto use MongoDB, orfalseto use the in-memory store.MONGO_URI: Your MongoDB connection string (required ifUSE_MONGO=true).MONGO_DB: Your MongoDB database name.RATE_LIMIT_REQUESTS: Max requests per window (e.g.,100).RATE_LIMIT_WINDOW: Duration of the window (e.g.,1h).
- Install dependencies:
go mod tidy
- Run the server (uses settings from
.envfile):The server will start on portgo run ./cmd/server/main.go
8000(or as specified by$PORT).
- Build the Docker image:
docker build -t url-shortener . - Run the container, passing in your environment file:
docker run -p 8000:8000 --env-file .env url-shortener
Creates a new short URL.
Request Body:
{
"url": "[https://github.com/google/go-cmp](https://github.com/google/go-cmp)",
"customCode": "go-cmp"
}url(string, required): The original URL to shorten.customCode(string, Optional): A desired custom short code. If not provided, a random 6-character code will be generated.
Success Response (200 OK):
{
"shortCode": "go-cmp",
"shortUrl": "http://localhost:8000/go-cmp",
"originalUrl": "https://github.com/google/go-cmp"
}Error Response (400 Bad Request):
{
"error": "short code already exists"
}Redirects a short code to its original URL.
code(string, path parameter): The short code to resolve.
Success Response (302 Found):
-
Redirects to the
OriginalURLstored for the code. -
Increments the
ClickCountfor the link.
Error Response (404 Not Found):
- Returns if the code does not exist or has expired.
A health check endpoint to confirm the service is running. Success Response (200 OK):
OK
url-shortener/
├── cmd/server/main.go # Main application entry point and dependency injection.
├── internal/
│ ├── handlers/ # HTTP handlers (handler.go) and router setup (router.go).
│ ├── middleware/ # HTTP middleware (ratelimit.go).
│ ├── models/ # Core data structures (url.go).
│ ├── repository/ # Data storage layer:
│ │ ├── interface.go # The core Repository interface.
│ │ ├── memory.go # In-memory map implementation.
│ │ └── mongo.go # MongoDB implementation.
│ └── services/ # Core business logic (url_service.go).
├── pkg/database/ # Database connection helpers (mongodb.go).
├── frontend/ # A simple static HTML/JS frontend for testing.
├── .env.example # Example environment variables.
├── Dockerfile # Multi-stage Docker build file.
├── go.mod # Go module dependencies.
└── Makefile # Helper commands for development (build, test, run).
-
Distributed Caching: Integrate a Redis cache (using the
pkg/database/redis.gostub) to sit in front of the MongoDB repository. This will reduce database load for popular links and improve redirect latency. -
Advanced Analytics: Expand the
IncrementClicksfunction to run in a goroutine, capturing and storing request metadata (User-Agent, Referrer, Geolocation) for a future analytics dashboard. -
Link Expiration: Implement TTL indexing in MongoDB to automatically purge expired links, or create a background job to handle cleanup based on the
ExpiresAtfield.