A high-performance image processing pipeline built with the Motia framework that automatically resizes uploaded images into multiple formats for different use cases (mobile, desktop, and low-quality variants).
- Cloud Storage: Uses AWS S3 for scalable file storage with adapter pattern for easy switching
- Parallel Processing: Simultaneous resize operations for optimal performance
- Multiple Output Formats: Desktop (1920px), Mobile (720px), and Low-quality (480px) variants
- Format Support: JPEG, PNG, and WebP images
- Event-Driven Architecture: Uses Motia's event system for scalable processing
- Comprehensive Logging: Detailed tracing and error handling
- File Validation: Format and size validation with clear error messages
- Extensible Storage: Adapter pattern allows easy integration with other storage providers
The application uses an event-driven architecture with the following components:
Upload Image → Save to S3 → Image Saved Event → Parallel Resize Operations → Completion Tracking
↓ ↓ ↓ ↓ ↓
Validation S3 Storage Event Emission Desktop Resize Status Updates
Base64 Decode (with S3 URLs) Mobile Resize (S3 URLs)
Low-Quality Resize
- Node.js 18+
- npm or yarn
- Motia CLI
- AWS S3 bucket (or compatible storage service)
- AWS credentials configured
# Clone the repository
git clone <repository-url>
cd motia-image-resizer
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env
# Edit .env with your AWS S3 credentials
# Set up the project
npm run postinstall
# Start development server
npm run dev-
Start the server:
npm run dev
-
Upload an image:
curl -X POST http://localhost:3000/upload-image \ -H "Content-Type: application/json" \ -d '{ "filename": "test-image.jpg", "data": "base64-encoded-image-data" }'
-
Check your S3 bucket:
- Images are stored in S3 with organized prefixes:
originals/- Original uploaded imagesdesktop/- Desktop-sized images (1920px width)mobile/- Mobile-sized images (720px width)lowquality/- Compressed images (480px width, 60% quality)
Endpoint: POST /upload-image
Request Body:
{
"filename": "image.jpg",
"data": "base64-encoded-image-data"
}Response:
{
"message": "Image uploaded successfully and processing started",
"traceId": "uuid-trace-id",
"imageMetadata": {
"originalFilename": "image.jpg",
"uniqueFilename": "image_1234567890.jpg",
"format": "jpeg",
"originalStorageKey": "originals/image_1234567890.jpg",
"originalUrl": "https://your-bucket.s3.us-east-1.amazonaws.com/originals/image_1234567890.jpg",
"traceId": "uuid-trace-id",
"uploadedAt": "2025-01-26T12:00:00.000Z"
}
}Error Responses:
400: Invalid file format, missing data, or validation errors500: Server error during processing
- JPEG (.jpg, .jpeg)
- PNG (.png)
- WebP (.webp)
- Maximum: 50MB per image
- JSON Body Limit: ~100KB (due to Express body parser limits)
Use the provided utility script:
# Convert an image to base64 for API testing
node scripts/convert-to-base64.js path/to/your/image.jpg
# This generates a JSON file ready for API testing# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with UI
npm run test:ui# Test the complete workflow
npm test -- tests/integration/workflow.test.tsmotia-image-resizer/
├── steps/ # Motia step definitions
│ ├── upload-image.step.ts # Image upload and validation
│ ├── desktop-resize.step.ts # Desktop resize (1920px)
│ ├── mobile-resize.step.ts # Mobile resize (720px)
│ ├── lowquality-resize.step.ts # Low-quality resize (480px)
│ └── completion-tracker.step.ts # Processing completion tracking
├── shared/ # Shared utilities and interfaces
│ ├── interfaces.ts # TypeScript interfaces
│ └── utils.ts # Utility functions
├── tests/ # Test files
│ ├── integration/ # Integration tests
│ └── utils/ # Test utilities
├── scripts/ # Utility scripts
│ └── convert-to-base64.js # Base64 conversion utility
├── output/ # Generated images (gitignored)
│ ├── original/ # Original uploaded images
│ ├── desktop/ # Desktop-sized images
│ ├── mobile/ # Mobile-sized images
│ └── lowquality/ # Low-quality compressed images
└── docs/ # Documentation
├── UPLOAD_TESTING.md # API testing guide
└── github-issue-draft.md # Feature request template
Copy .env.example to .env and configure:
# Storage Configuration
STORAGE_TYPE=s3
# AWS S3 Configuration
AWS_ACCESS_KEY_ID=your_access_key_here
AWS_SECRET_ACCESS_KEY=your_secret_key_here
AWS_REGION=us-east-1
AWS_S3_BUCKET_NAME=your-bucket-nameThe resize configurations are defined in shared/storage-utils.ts:
const RESIZE_CONFIGS = {
desktop: { width: 1920, quality: 90 },
mobile: { width: 720, quality: 85 },
lowquality: { width: 480, quality: 60 }
}Images are organized in S3 with the following prefixes:
originals/- Original uploaded imagesdesktop/- Desktop-optimized images (1920px)mobile/- Mobile-optimized images (720px)lowquality/- Compressed low-quality images (480px)
The application uses the adapter pattern for storage. Current adapters:
- S3StorageAdapter (default) - AWS S3 compatible storage
- TODO: SupabaseStorageAdapter, CloudflareR2Adapter, LocalFileSystemAdapter
To implement a new storage adapter, implement the StorageAdapter interface in shared/storage.ts.
npm run dev # Start development server
npm run build # Build the project
npm run test # Run tests
npm run test:watch # Run tests in watch mode
npm run test:ui # Run tests with UI
npm run clean # Clean build artifacts- Create a new step file in
steps/ - Define the resize configuration
- Subscribe to the
image-savedevent - Emit completion events for tracking
- Update the completion tracker
Enable verbose logging:
npm run dev:debugCurrently, only base64 JSON uploads are supported due to Motia framework limitations:
- Multipart/form-data: Not supported (framework limitation)
- File size: Limited by JSON body parser (~100KB)
- Workaround: Use base64 encoding for smaller images
- Parallel Processing: All resize operations run simultaneously
- Memory Usage: Large images may consume significant memory during processing
- Disk Space: Each image generates 4 files (original + 3 variants)
-
"PayloadTooLargeError": Image too large for JSON body parser
- Solution: Use smaller images or compress before upload
-
"Invalid image format": Unsupported file type
- Solution: Use JPEG, PNG, or WebP formats only
-
"Sharp processing failed": Image corruption or invalid data
- Solution: Verify image file integrity
Check server logs for detailed processing information:
- Upload validation steps
- Resize operation progress
- Error details with context
- Processing time metrics
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Run the test suite
- Submit a pull request
This project is licensed under the MIT License.
For issues and questions:
- Check the troubleshooting section
- Review the test files for usage examples
- Submit issues on GitHub
- Refer to Motia framework documentation