Skip to main content
HP
Back to All Projects

Fragments Microservice

Cloud-native content microservice with AWS infrastructure

Node.jsExpressAWSDockerJest

The Problem

Modern applications need a flexible content storage service that can handle multiple formats, convert between them on-the-fly, and scale with cloud-native infrastructure. Fragments is a microservice that stores user-scoped content fragments with authentication, format conversion, and extensible storage backends. Designed to run on AWS from day one.

Architecture

HTTPSJWTClient / UIParcel + OIDCExpressAPI ServerCognitoJWT AuthS3Object StorageDynamoDBMetadataConverterFormat Pipeline

Technical Decisions

Why three storage backends?

The storage layer is an abstraction. In development, fragments live in memory (fast, no setup). In staging, they go to S3 + DynamoDB. In production, same. But the point is the application code doesn't know or care which backend is active. Swap the strategy, not the code. This is the Strategy pattern applied to infrastructure.

How does content negotiation work?

When you request a fragment with a file extension (e.g., GET /v1/fragments/:id.html), the server checks if conversion is possible from the stored type to the requested type. Markdown to HTML? Sure. PNG to JPEG? Done via Sharp. JSON to YAML? No problem. The conversion pipeline is modular. Adding a new format means adding one function.

Why Cognito for auth?

AWS Cognito gives me JWT-based authentication that integrates natively with the rest of the AWS stack. The frontend uses Cognito's Hosted UI for OAuth flows. The backend validates JWTs on every request. This means user-scoped data isolation: you can only see your own fragments.

Code Spotlight

src/model/data/storage.js
class StorageStrategy {
  constructor(type) {
    switch (type) {
      case 'memory':
        this.backend = new MemoryStorage();
        break;
      case 's3':
        this.backend = new S3Storage();
        break;
      case 'dynamodb':
        this.backend = new DynamoDBStorage();
        break;
    }
  }

  async readFragment(ownerId, id) {
    return this.backend.readFragment(ownerId, id);
  }

  async writeFragment(fragment) {
    return this.backend.writeFragment(fragment);
  }
}

The storage abstraction layer. Application code calls readFragment() and writeFragment() without knowing if data lives in memory, S3, or DynamoDB. Swap environments, not code.

Key Features

Content Management

  • Create, read, update, delete user-scoped content fragments
  • Support for text, JSON, HTML, Markdown, images (PNG, JPEG, WebP, GIF)
  • Fragment tagging system for organization

Format Conversion

  • On-the-fly conversion: Markdown → HTML, PNG → JPEG, JSON → YAML
  • Content negotiation via file extension in URL
  • Modular conversion pipeline, easy to add new formats

Cloud Infrastructure

  • S3 for binary storage, DynamoDB for metadata
  • Cognito JWT authentication with user-scoped isolation
  • Docker containerized, deployed on AWS ECS

Testing

Full integration test suite ensuring every API endpoint works correctly across all storage backends.

Comprehensive integration tests with Supertest

  • Supertest for HTTP-level integration testing
  • Tests run against in-memory storage for speed
  • Hurl files for manual API testing workflows

CI/CD Pipeline

Docker build
Push to ECR
Deploy to ECS via GitHub Actions. Frontend deployed separately via Parcel build.

Results

01

3 interchangeable storage backends (memory, S3, DynamoDB)

02

On-the-fly format conversion between 8+ content types

03

User-scoped data isolation via Cognito JWT authentication

04

Cloud-native deployment on AWS ECS with Docker