Simple REST API for uploading, resizing, and serving images. The service stores metadata in PostgreSQL (or SQLite for tests) and stores files on the filesystem.
POST /imagesfor image upload with title, width, and heightGET /imageswith pagination and title filteringGET /images/:idfor single image- Files served from
/uploads/* - Swagger OpenAPI v3 docs at
http://localhost:3000/docs
- Node.js LTS
- Docker + docker-compose (recommended)
- Build and run:
docker-compose up --build- Open Swagger UI:
http://localhost:3000/docs
- Install deps:
npm install- Create env file:
copy .env.example .env- Start the API:
npm run start:devSee .env.example for defaults. The most important:
PORT(default3000)UPLOAD_DIR(defaultuploads)UPLOAD_MAX_FILE_SIZE(bytes, default10485760)DB_TYPE(postgresorsqlite)DB_*settings for PostgresDB_SYNC(defaultfalse, use migrations)DB_MIGRATIONS_RUN(defaulttrue, auto-runs migrations on startup)IMAGES_OUTPUT_FORMAT(sourceorjpeg)IMAGES_JPEG_QUALITY/IMAGES_WEBP_QUALITY/IMAGES_AVIF_QUALITY(1-100)IMAGES_PNG_COMPRESSION_LEVEL(0-9)
The project follows a simplified Clean Architecture layout:
src/presentation— controllers, HTTP DTOs, upload configsrc/application— use-cases (ImagesService), application DTOs, portssrc/domain— domain typessrc/infrastructure— persistence, storage, config, migrations
Migrations are enabled by default (DB_MIGRATIONS_RUN=true) and DB_SYNC is disabled to avoid schema sync warnings.
You can toggle them via env if needed.
- Content-Type:
multipart/form-data - Fields:
file(image file)title(string)width(int)height(int)
Response:
{
"id": "uuid",
"url": "/uploads/<filename>",
"title": "My image",
"width": 800,
"height": 600
}Query params:
title(optional, substring match)page(optional, default1)limit(optional, default20, max100)
Response:
{
"data": [
{
"id": "uuid",
"url": "/uploads/<filename>",
"title": "My image",
"width": 800,
"height": 600
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 1,
"totalPages": 1
}
}Returns a single image object.
Run e2e tests (SQLite in-memory):
npm test- Images are resized using
sharpwithfit=cover(crop + scale). Output format and compression are configurable via env. - Files are stored on disk in
UPLOAD_DIRand served via/uploads.