Skip to content

A simple REST URL shortening service built with Go & MySQL. This service allows users to create short URLs, which then can be deleted by Admins using an API Key.

License

Notifications You must be signed in to change notification settings

prit342/url-shortner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

URL Shortener Service

A simple REST URL shortening service built with Go & MySQL. This service allows users to create short URLs, which then can be deleted by Admins using an API Key.

Features

  • Shorten long URLs with custom expiration times
  • Delete URLs by ID with API key authentication
  • RESTful API with JSON responses
  • MySQL database with migrations
  • Docker containerisation
  • Base62 encoding for short codes
  • URL expiration validation
  • Comprehensive test coverage with mocks

Architecture

The project follows clean architecture principles with clear separation of concerns:

internal/
├── app/             # Application layer coordination
├── config/          # Configuration management
├── domain/          # Core business entities (URL)
├── service/         # Business logic layer
├── store/           # Data persistence layer
├── mocks/           # Generated mock implementations for testing
└── transport/       # Transport layers
    └── httpapi/     # HTTP transport layer with handlers, routing, and middleware

Quick Start

Prerequisites

  • Docker
  • Go 1.21+ (for development)
  • Make (for using Makefile commands)
  • bash

Environment Setup

Create a .env file in the project root (or copy the env-sample to .env) with the following values:

# Database Configuration
MYSQL_ROOT_PASSWORD=password
MYSQL_DATABASE=url_shortener
MYSQL_USER=url_shortener_user
MYSQL_PASSWORD=url_shortener_pass
DB_USER=url_shortener_user
MYSQL_HOST=localhost
API_KEY='sample_secure_api_key'  # API key for authenticating delete requests

#
DSN='url_shortener_user:url_shortener_pass@tcp(localhost:3306)/url_shortener?parseTime=true'
# Port Configuration
MYSQL_PORT=3306
APP_PORT=12345

BASE_URL=http://localhost:12345

Running the app

make db-up          # Start MySQL container
make migrate-up     # Run database migrations
make app-up         # Start application locally

Endpoints

Public endpoints:

  • POST /v1/urls - Create short URL (no auth needed)
  • GET /{shortcode} - Redirect to original URL (no auth needed)

Protected endpoints (API key required):

  • GET /v1/urls/{uuid} - Get URL metadata for admin/management
  • DELETE /v1/urls/{uuid} - Delete URL by UUID

Testing basic functionality with curl

  • Create a new short URL:
curl -d '{ "long_url": "https://www.example3.com", "expires_after": "24h" }' http://localhost:12345/v1/urls

{"short_code":"qBdSY4","id":"0198f68d-a873-742d-831f-cd3d61e78bda"}
  • You should now be redirected to https://www.example3.com when you curl the following:
curl -v http://localhost:12345/qBdSY4

...
...
...
< HTTP/1.1 302 Found
< Content-Type: text/html; charset=utf-8
< Location: https://www.example3.com
< Vary: Origin
< Date: Fri, 29 Aug 2025 16:00:44 GMT
< Content-Length: 47
<
<a href="https://www.example3.com">Found</a>.
  • Using the API Keys you set up in the .env file, you should be able to get the URL details:
curl -H "X-API-Key: sample_secure_api_key" http://localhost:12345/v1/urls/0198f68d-a873-742d-831f-cd3d61e78bda

{"id":"0198f68d-a873-742d-831f-cd3d61e78bda","code":"qBdSY4","long_url":"https://www.example3.com","created_at":"2025-08-29T15:58:56Z","expires_at":"2025-08-30T15:58:56Z"}
  • Using the API Key you set up in the .env file, you can delete the record from the DB (note the UUID from the previous response):
# This will succeed with correct API key from .env file
curl -X DELETE -H "X-API-Key: sample_secure_api_key" http://localhost:12345/v1/urls/0198f68d-a873-742d-831f-cd3d61e78bda
{}

API Documentation

Base URL

http://localhost:12345

Public Endpoints (No Authentication Required)

Create Short URL

POST /v1/urls
Content-Type: application/json

{
  "long_url": "https://example.com/very-long-url-that-needs-shortening",
  "expires_after": "168h"  // Duration string (e.g., "1h", "30m", "24h", "7d")
}

Response:

{
  "short_code": "AbC123",
  "id": "550e8400-e29b-41d4-a716-446655440000"
}

Redirect to Original URL

GET /{short_code}

Response:

HTTP 302 Found
Location: https://example.com/original-long-url

Error Responses:

  • 400 Bad Request: Invalid short code format
  • 404 Not Found: Short code not found
  • 410 Gone: URL has expired

Protected Endpoints (API Key Required)

All protected endpoints require the X-API-Key header with a valid API key.

Get URL Metadata

GET /v1/urls/{uuid}
X-API-Key: your-secret-api-key

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "code": "AbC123",
  "long_url": "https://example.com/original-long-url",
  "created_at": "2025-08-29T15:58:56Z",
  "expires_at": "2025-08-30T15:58:56Z"
}

Delete URL by ID

DELETE /v1/urls/{uuid}
X-API-Key: your-secret-api-key

Response:

{
  "ok": "true"
}

Error Responses for Protected Endpoints:

  • 400 Bad Request: Invalid UUID format
  • 401 Unauthorized: Missing or invalid API key
  • 404 Not Found: URL not found
  • 500 Internal Server Error: Server error

Request/Response Details

Supported Duration Formats:

  • Hours: 1h, 24h, 168h
  • Minutes: 30m, 90m
  • Days: 1d, 7d (converted to hours)
  • Combinations: 1h30m, 2h45m

URL Validation:

  • Must include valid HTTP/HTTPS scheme
  • Maximum request body size: 256KB
  • Short codes are exactly 6 characters (Base62 encoded)
  • UUIDs follow standard UUID v4 format

Authentication:

  • API key must be provided in X-API-Key header
  • Only admin/management endpoints require authentication
  • Public creation and redirect endpoints are open

Configuration

The application uses environment variables for configuration:

Variable Description Example Required
BASE_URL Base URL for shortened URLs http://localhost:12345 Yes
APP_PORT Application port 12345 Yes
API_KEY API key for DELETE operations your-secret-key Yes
MYSQL_* MySQL configuration for Docker See env example Yes

Development

Project Structure

├── cmd/server/              # Application entry point
├── internal/
│   ├── app/                # Application coordination layer
│   ├── config/             # Configuration management
│   ├── domain/             # Core entities (URL)
│   ├── service/            # Business logic (URL service)
│   ├── store/              # Data access layer
│   ├── mocks/              # Generated test mocks
│   ├── transport/httpapi/  # HTTP handlers, middleware, routing
│   └── db-migrations/mysql/ # Database migrations
├── Makefile                # Build and development commands
├── go.mod                  # Go modules
└── README.md              # This file

Running Tests

# Run all tests
go test ./...

# Run tests for specific package
go test ./internal/transport/httpapi
go test ./internal/service

# Run tests with coverage
go test -cover ./...

# Run specific test case
go test -run TestCreateShortURLHandler ./internal/transport/httpapi

Working with Mocks

# Generate mocks from interfaces
make gen-mocks

# This generates mocks for:
# - internal/store/store.go -> internal/mocks/store_mock.go
# - internal/service/url_service.go -> internal/mocks/url_service_mock.go

Authentication for DELETE operation

  • DELETE operations require API key authentication
  • API key must be provided in X-API-Key header
  • Only DELETE requests are protected (create operations are public)

Validation

  • URLs must have valid HTTP/HTTPS schemes
  • UUID validation for resource IDs
  • Request body size limited to 256KB
  • Duration strings validated using Go's time.ParseDuration

License

This project is licensed under the MIT License.

Enhancements

  • Introduce custom logging using log/slog
  • Update error handling by polishing transport/httpapi/errors.go
  • Add E2E testing using test containers

About

A simple REST URL shortening service built with Go & MySQL. This service allows users to create short URLs, which then can be deleted by Admins using an API Key.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published