A scalable Node.js Express API for dynamically resizing and serving images with intelligent caching. This project demonstrates enterprise-level architecture patterns including TypeScript, comprehensive testing, linting, and image processing capabilities.
This API provides two primary use cases:
- Rapid Prototyping: Place resized images in your frontend with dimensions specified via URL parameters
- Production Image Serving: Automatically resize and cache images to reduce page load sizes and optimize bandwidth
The API intelligently caches resized images on first access, serving pre-generated versions on subsequent requests for optimal performance.
image-processing-api/
├── src/ # TypeScript source code
│ ├── index.ts # Server entry point
│ ├── controllers/
│ │ └── imagesController.ts # Request handlers for image API
│ ├── routes/
│ │ └── images.ts # API routes definition
│ └── services/
│ └── imageService.ts # Image processing utility functions
├── tests/ # Test files (Jasmine + SuperTest)
│ ├── api.spec.ts # API endpoint tests
│ └── imageService.spec.ts # Image service unit tests
├── build/ # Compiled JavaScript output
├── assets/
│ ├── full/ # Original full-size images
│ └── thumb/ # Cached resized thumbnails
├── package.json # Project dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── eslint.config.mjs # ESLint rules configuration
└── .prettierrc # Prettier formatting rules
- Node.js (v18 or higher)
- npm
npm installAdd your original JPG images to the assets/full/ directory:
cp your-image.jpg assets/full/npm startThe server will start on http://localhost:3000
# Run all tests (Jasmine + SuperTest)
npm test
# Watch mode with Nodemon (auto-reload on changes)
npm run dev
# Check code formatting and quality
npm run lint
# Auto-format code to match standards
npm run format# Compile TypeScript to JavaScript
npm run build
# Start the production server
npm startGET /api/images?filename=<imageName>&width=<widthPx>&height=<heightPx>
- filename (required, string): Name of image without extension (e.g.,
encenadaport) - width (required, number): Target width in pixels (must be positive integer)
- height (required, number): Target height in pixels (must be positive integer)
- Status: 200 OK
- Content: JPEG image file
- Behavior:
- First request: Processes and caches the resized image
- Subsequent requests: Serves cached image (much faster)
| Status | Scenario | Example |
|---|---|---|
| 400 | Missing parameters | ?filename=image&width=200 (missing height) |
| 400 | Invalid width/height | ?filename=image&width=abc&height=200 |
| 400 | Non-positive dimensions | ?filename=image&width=0&height=200 |
| 404 | Image not found | ?filename=nonexistent&width=200&height=200 |
curl "http://localhost:3000/api/images?filename=encenadaport&width=200&height=200"curl "http://localhost:3000/api/images?filename=encenadaport&width=400&height=300"# Returns 400 error
curl "http://localhost:3000/api/images?filename=encenadaport&width=200"The project includes comprehensive tests using Jasmine and SuperTest:
npm test-
API Endpoint Tests (
tests/api.spec.ts):- Missing parameter validation
- Invalid dimension validation
- Non-existent image handling
- Valid request with caching verification
-
Image Service Tests (
tests/imageService.spec.ts):- Direct function testing with valid inputs
- Error handling for missing files
- Caching behavior verification
6 specs, 0 failures
- Image resizing functionality: ✓
- Error handling: ✓
- Caching mechanism: ✓
- Parameter validation: ✓
- ✓ All source code (
src/**/*.ts) uses TypeScript - ✓ Type annotations on all functions and parameters
- ✓ No use of
anytype - ✓ Proper module imports/exports
# Check code quality
npm run lint
# Auto-format code
npm run formatESLint Configuration: Node.js optimized, TypeScript support Prettier: Enforces consistent code formatting
# Compiles TypeScript to JavaScript
npm run build
# Output: `build/` directory with compiled `.js` filesThe API implements intelligent file-based caching:
- Cache Location:
assets/thumb/directory - Cache Naming:
{filename}_{width}_{height}.jpg - First Request: Image is processed and saved to cache
- Subsequent Requests: Pre-cached version is served immediately
- Performance: ~50-100ms for cached images vs ~500-1000ms for processing
assets/thumb/
├── encenadaport_200_200.jpg # 200x200 cache
├── encenadaport_400_300.jpg # 400x300 cache
└── seagull_150_150.jpg # Different image cache
The API provides clear error messages for all failure scenarios:
// Missing parameters
GET /api/images
→ 400: "Missing filename, width, or height"
// Invalid dimensions
GET /api/images?filename=img&width=abc&height=200
→ 400: "Width and height must be positive numbers"
// Image not found
GET /api/images?filename=nonexistent&width=200&height=200
→ 404: "Image not found"- Separation of Concerns: Routes, Controllers, Services
- Async/Await: Consistent asynchronous handling
- Error Middleware: Centralized error handling
- Module Pattern: Reusable, testable functions
- File-based caching for instant retrieval
- Sharp library for efficient image processing
- ESM modules for better tree-shaking
- Minimal dependencies (~4 production packages)
- Stateless server design (horizontal scaling ready)
- Service abstraction layer for database/cache integration
- Middleware architecture for feature additions
- Environment-agnostic configuration
- Multiple image format support (PNG, WebP, AVIF)
- Redis caching for distributed systems
- Image compression optimization options
- CDN integration
- Admin dashboard for cache management
- S3/Cloud storage backend
- express (^5.2.1): Web server framework
- sharp (^0.34.5): High-performance image processing
- typescript (^5.9.3): Type safety
- ts-node (^10.9.2): TypeScript execution
- jasmine (^6.1.0): Testing framework
- supertest (^7.2.2): HTTP assertion library
- eslint (^9.39.4): Code linting
- prettier (^3.8.1): Code formatting
- nodemon (^3.1.14): Development auto-reload
Currently, the API uses hardcoded defaults. For production, consider adding:
NODE_ENV=production # Server environment
PORT=3000 # Server port
LOG_LEVEL=info # Logging level
CACHE_DIR=./assets/thumb # Cache directory
UPLOAD_DIR=./assets/full # Upload directorySolution: Run npm run build first to compile TypeScript
Solution: Ensure image files are in assets/full/ and are valid JPGs
Solution: Run npm run format to auto-fix formatting issues
Solution:
- Delete
assets/thumb/cache:rm -rf assets/thumb/* - Re-run:
npm test
# 1. Install
npm install
# 2. Add test images
cp your-image.jpg assets/full/encenadaport.jpg
# 3. Check code quality
npm run lint
npm run format
# 4. Run tests
npm test
# 5. Build for production
npm run build
# 6. Start server
npm start
# 7. Test endpoint
curl "http://localhost:3000/api/images?filename=encenadaport&width=200&height=200"