A lightweight self-hosted URL shortener written in Go.
Current implementation details:
- HTTP server built with Gorilla Mux
- SQLite storage (file at
internal/database/url-shortener.db) - Automatic SQL migrations on startup
- API-key protected create/delete endpoints
- Plain-text request bodies
- Go
- SQLite support via
github.com/mattn/go-sqlite3(CGO-enabled build environment)
The app requires a .env file in the project root.
PORT=5099
API_KEY=abc123
RAND_CHARS=6 # Optional, defaults to 8 when missing.If either value is missing, startup fails.
Using make:
make runOr directly:
go run ./cmd/url-shortener/main.goBuild binary:
make build
./bin/url-shortenerBase URL examples below use http://localhost:5099.
POST /create
Headers:
K: <API_KEY>
Body format (plain text):
urlurl|alias
Examples:
curl -H "K: <API_KEY>" http://localhost:5099/create -d 'example.com'curl -H "K: <API_KEY>" http://localhost:5099/create -d 'https://example.com/docs|docs'Behavior:
- If alias is omitted, an 8-character random alias is generated
- URLs without scheme are normalized to
http://... - Only
httpandhttpsURLs are accepted
Success response:
- Status:
201 Created - Body:
<host>/<alias>
Possible errors:
400 Bad Requestfor invalid body/URL401 Unauthorizedfor missing or invalid API key500 Internal Server Error(for example, duplicate alias insertion)
POST /delete
Headers:
K: <API_KEY>
Body format (plain text):
alias
Example:
curl -H "K: <API_KEY>" http://localhost:5099/delete -d 'docs'Success response:
- Status:
200 OK - Body:
<alias> deleted
Possible errors:
401 Unauthorizedfor missing or invalid API key500 Internal Server Errorif alias does not exist or delete fails
GET /{alias}
Example:
curl -i http://localhost:5099/docsBehavior:
- Redirects using
301 Permanent Redirect - Returns
404if alias is not found
- Database file:
internal/database/url-shortener.db - Migrations dir:
internal/database/migrations - Migrations run automatically when the server starts
Current state is a working core service with create, delete, and redirect endpoints backed by SQLite.
Known limitations in current behavior:
- No structured JSON API
- Duplicate alias attempts currently surface as generic http 500 errors
- Delete for missing alias currently returns 500 rather than 404