Skip to content

About

A link shortener application built with AWS Serverless services using SAM templates

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

29 Commits

Folders and files

Repository files navigation

Serverless URL Shortener

A serverless URL shortener built with Rust and AWS Lambda, featuring automatic metadata extraction and click tracking.

Features

  • URL Shortening: Generate short, unique links using CUID2
  • Metadata Extraction: Automatically fetches title, description, and content type from target URLs
  • Click Tracking: Increments click counter on each redirect
  • List URLs: Paginated listing of all shortened URLs with metadata
  • Serverless Architecture: Runs on AWS Lambda with ARM64 architecture for cost efficiency

Architecture

The application follows a trait-based dependency injection pattern for testability and maintainability.

graph TB
    Client[Client] --> APIGW[API Gateway HTTP API]
    APIGW --> Handler[Lambda Handler]
    Handler --> Core[UrlShortener Core Logic]
    Core --> UrlRepo[UrlRepository Trait]
    Core --> UrlInfo[UrlInfo Trait]
    UrlRepo --> DynamoDB[DynamoDbUrlRepository]
    UrlInfo --> HTTP[HttpUrlInfo]
    DynamoDB --> DB[(DynamoDB Table)]
    HTTP --> Internet[Internet]
    
    style Core fill:#90EE90
    style UrlRepo fill:#FFE4B5
    style UrlInfo fill:#FFE4B5
    style DynamoDB fill:#527FFF
    style HTTP fill:#87CEEB
    style Handler fill:#FF9900
Loading

Code Structure

├── shared/                      # Shared business logic
│   ├── src/
│   │   ├── core.rs             # Business logic + traits (UrlRepository, UrlInfo)
│   │   ├── adapters.rs         # DynamoDbUrlRepository implementation
│   │   ├── url_info.rs         # HttpUrlInfo implementation
│   │   └── utils.rs            # HTTP response helpers
├── lambdas/
│   ├── create_link/            # POST /links handler
│   ├── get_links/              # GET /links handler
│   └── visit_link/             # GET /{linkId} handler
└── integration-tests/          # End-to-end tests

Key Design Patterns

  • Trait Abstraction: UrlRepository and UrlInfo traits decouple business logic from infrastructure
  • Dependency Injection: Concrete implementations (DynamoDbUrlRepository, HttpUrlInfo) injected at runtime
  • Generic Core: UrlShortener<R: UrlRepository, I: UrlInfo> works with any implementation
  • Adapter Pattern: Infrastructure adapters implement trait interfaces

AWS Resources

  • AWS Lambda: Rust functions running on provided.al2023 runtime with ARM64 architecture
  • Amazon DynamoDB: NoSQL database for storing shortened URLs (PAY_PER_REQUEST billing)
  • Amazon API Gateway (HTTP API): RESTful API endpoints

API Endpoints

Method Path Description
POST /links Create a new shortened URL
GET /links List all shortened URLs (supports pagination)
GET /{linkId} Redirect to original URL and increment clicks

Data Model

Each shortened URL stores:

  • LinkId (Primary Key): Unique identifier generated with CUID2
  • OriginalLink: The target URL
  • Clicks: Number of times the link has been accessed
  • Title: Extracted page title (optional)
  • Description: Extracted meta description (optional)
  • ContentType: HTTP content type (optional)

Prerequisites

Installation

git clone https://github.com/ramtoearth/serverless-link-shortener.git
cd serverless-link-shortener

Development

Build

sam build --beta-features

Testing

The project uses a trait-based architecture with the mockall crate for comprehensive unit testing without external dependencies.

Run all tests:

cargo test

Test specific lambda:

cd lambdas/create_link
cargo test

Testing Architecture

The application separates concerns into three layers:

  1. Core Business Logic (shared/src/core.rs)

    • Pure business logic with trait abstractions
    • UrlRepository trait for data persistence operations
    • UrlInfo trait for URL metadata fetching
    • Fully testable with mocks, no AWS/network calls required
  2. Infrastructure Adapters (shared/src/adapters.rs, shared/src/url_info.rs)

    • DynamoDbUrlRepository: Real DynamoDB implementation
    • HttpUrlInfo: Real HTTP metadata scraping
    • Used in production, swapped with mocks in tests
  3. Lambda Handlers (lambdas/*/src/http_handler.rs)

    • Generic over trait implementations: function_handler<R: UrlRepository, I: UrlInfo>
    • Tests use mocks, production uses real adapters
    • Validates HTTP request/response handling

Example test with mocks:

#[tokio::test]
async fn test_shorten_url() {
    let mut mock_repo = MockUrlRepository::new();
    mock_repo.expect_store_short_url()
        .times(1)
        .returning(|url, id, details| Ok(ShortUrl::new(id, url, 0, details.title, details.description, details.content_type)));
    
    let mut mock_info = MockUrlInfo::new();
    mock_info.expect_fetch_details()
        .returning(|_| Ok(UrlDetails { title: Some("Test".to_string()), ..Default::default() }));
    
    let shortener = UrlShortener::new(mock_repo, mock_info);
    let result = shortener.shorten_url(request).await;
    assert!(result.is_ok());
}

Benefits:

  • Fast tests (no AWS credentials or network calls)
  • Test error scenarios easily
  • Zero production overhead (mocks compiled out in release builds)

For detailed testing architecture, see TESTING_ARCHITECTURE.md.

Deployment

Deploy to AWS:

sam deploy --guided

On first deployment, you'll be prompted to configure:

  • Stack name (e.g., shortener-link)
  • AWS Region
  • Confirmation prompts
  • Capabilities (IAM role creation)

Subsequent deployments can use:

sam deploy

Viewing Logs

After deployment, view Lambda function logs in real-time:

sam logs --stack-name shortener-link --name ShortenUrlFunction -t

The -t flag tails the logs, showing new entries as they arrive.

Usage

After deployment, SAM will output your API endpoint URL. Use it to interact with the service:

Create Short URL

curl -X POST {api_url}/links \
  -H "Content-Type: application/json" \
  -d '{"url_to_shorten": "https://example.com/very/long/url"}'

List URLs (with pagination)

curl {api_url}/links

Access Short URL

curl -L {api_url}/{linkId}

This will redirect to the original URL and increment the click counter.

Configuration

The Lambda function uses the following environment variable:

  • TABLE_NAME: DynamoDB table name (automatically set by SAM)

Cleanup

To delete all AWS resources:

sam delete

Note: The DynamoDB table has DeletionPolicy: Retain to prevent accidental data loss. Delete it manually from the AWS Console if needed.

About

A link shortener application built with AWS Serverless services using SAM templates

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages