A lightweight URL shortener backend built with Go, Fiber, and PostgreSQL.
This project provides:
- Short URL creation endpoint
- Redirect endpoint for short codes
- Optional expiration support per URL
- Basic click counting on redirects
- Auto-migration for the
urlstable on startup
- Go (module-based project)
- Fiber v2 (web framework)
- PostgreSQL
- pgx v5 (PostgreSQL driver)
- godotenv (environment variable loading)
cmd/
main.go # App bootstrap and server startup
internal/
database/
database.go # DB connection setup
migrate.go # Schema migration
query.go # DB query helpers
handlers/
shorten.go # POST /api/v1/shorten
redirect.go # GET /:code
models/
requests.go # Request DTOs
url.go # URL model
routes/
routes.go # Route registration
utils/
code.go # Short code generator
-
POST /api/v1/shorten- Accepts a target URL and optional expiration time in seconds
- Generates a random 6-character short code
- Stores the record in PostgreSQL
- Returns a full short URL (currently
http://localhost:3000/{code})
-
GET /:code- Looks up short code
- Rejects missing/expired links
- Increments click count
- Redirects to original URL with HTTP
301 Moved Permanently
- Go (latest stable recommended)
- PostgreSQL (local or remote)
- Git (for version control and GitHub push)
The app loads environment variables from .env in the project root.
Create a .env file with:
DB_USER=postgres
DB_PASSWORD=your_password
DB_HOST=localhost
DB_PORT=5432
DB_NAME=url_shortner
DB_SSLMODE=disableNotes:
DB_SSLMODE=disableis typical for local development.- For managed databases, use the provider-recommended SSL mode.
- Create the database:
CREATE DATABASE url_shortner;- Start the app. On startup, migration runs automatically and creates table
urlsif it does not exist.
Current schema created by migration:
CREATE TABLE IF NOT EXISTS urls(
id SERIAL PRIMARY KEY,
code TEXT NOT NULL UNIQUE,
target_url TEXT NOT NULL,
clicks INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
expire_at TIMESTAMP NOT NULL
);- Clone repository and enter it:
git clone <your-repo-url>
cd url_shortner- Install dependencies:
go mod tidy-
Configure environment variables in
.env. -
Run the API:
go run cmd/main.goServer starts on:
http://localhost:3000
- GET
/health
Response:
{
"status": "ok",
"message": "API is healthy"
}- POST
/api/v1/shorten - Content-Type:
application/json
Request body:
{
"url": "https://example.com/some/long/path",
"expire_in": 3600,
"redirect_type" : "301"
}Fields:
url(string, required): original URLexpire_in(int, optional): expiration in seconds from now
Success response example:
{
"short_url": "http://localhost:3000/Ab3XyZ"
}Possible errors:
400 Bad Requestfor invalid JSON body400 Bad Requestwhenurlis empty500 Internal Server Errorfor DB insert failure
- GET
/:code
Behavior:
- Finds URL by code
- Rejects if not found (
404) - Rejects if expired (
410) - Increments
clicks - Redirects to target URL (
301)
Possible errors:
400 Bad Requestfor missing code404 Not Foundif code does not exist410 Goneif URL is expired
Create short URL:
curl -X POST http://localhost:3000/api/v1/shorten \
-H "Content-Type: application/json" \
-d '{"url":"https://golang.org","expire_in":3600}'Open short URL in browser:
http://localhost:3000/<code>Check health:
curl http://localhost:3000/health- Generated short code length is fixed to 6 characters.
- Current code generation uses pseudo-random
math/rand. - No URL ownership/authentication yet.
- No custom alias support yet.
- Base URL is hardcoded (
http://localhost:3000) in response.
-
Error loading .env file- Ensure
.envexists in project root.
- Ensure
-
Unable to connect to Database- Verify DB credentials/host/port/db name in
.env. - Confirm PostgreSQL is running.
- Verify DB credentials/host/port/db name in
-
Migration Failed- Confirm DB user has permission to create tables.
-
API returns
410 URL Expiredunexpectedly- Check the
expire_invalue used when creating the link.
- Check the
- Add request validation middleware (URL format, expiration bounds)
- Add collision handling/retry for short code uniqueness
- Make base URL configurable via environment variable
- Add unit/integration tests
- Add Docker and docker-compose setup
- Add rate limiting and analytics endpoints
Choose a license before public release (for example: MIT).