Skip to content

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

 
 
 

Contributors