A production-grade, high-performance URL Shortener Microservice built with Express 5, TypeScript, tRPC v11, Zod, MongoDB (Mongoose), Redis, and Docker.
Features atomic Base62 short-code generation, fast Redis caching, tRPC Endpoints with Zod schema validation, HTTP 302 redirection, and Docker containerization with hot-reloading.
- β‘ TypeScript & Express 5: Strict type safety with modern async request handling.
- π§ββοΈ tRPC v11 Integration: End-to-end type-safe API procedures (
url.create,url.getOriginalUrl). - π‘οΈ Zod Validation: Input payload validation with custom error messages.
- π High Performance Redis Cache: Sub-millisecond lookup cache for short URL resolution and atomic counter generation.
- π Persistent MongoDB Storage: Mongoose schemas with indexed
shortUrland click tracking. - π HTTP 302 Redirection: Dedicated high-speed Express redirection route (
GET /:shortUrl) with click analytics. - π³ Docker & Docker Compose: Instant multi-container setup (Express + MongoDB + Redis) with volume mounts for live hot reloading.
- π‘οΈ Security: Pre-configured with
helmetandcorssecurity headers. - π¦ Smart Dependency Helper: Custom
npm run addcommand to install packages on Mac and Docker simultaneously.
flowchart TB
subgraph Clients ["1. Client Layer"]
C1["π Web Browser"]
C2["π± Mobile App"]
C3["π» tRPC / HTTP Client"]
end
subgraph Security ["2. Middleware & Security Layer"]
M1["π‘οΈ Helmet Security Headers"]
M2["π CORS Policy Manager"]
M3["π Morgan & Request Logger"]
M4["π¨ Global Error & 404 Handlers"]
end
subgraph Routing ["3. Routing & Validation Layer"]
R1["β‘ Express 5 Application (app.ts)"]
R2["π§ββοΈ tRPC Adapter & Router (appRouter)"]
R3["π‘οΈ Zod Schema Validator"]
R4["π HTTP 302 Redirection Route (GET /:shortUrl)"]
end
subgraph Controllers ["4. Controller Layer"]
CTL1["UrlController (create & getOriginalUrl)"]
CTL2["redirectController (HTTP Redirection)"]
end
subgraph Services ["5. Business Logic & Service Layer"]
S1["βοΈ SUrlService"]
S2["π€ Base62 Encoder (toBase62)"]
end
subgraph Repositories ["6. Repository Abstraction Layer"]
REP1["π¦ CacheRepository (Redis Bridge)"]
REP2["ποΈ UrlRepository (MongoDB Mongoose Bridge)"]
end
subgraph Databases ["7. Storage & Infrastructure Layer"]
DB1[("β‘ Redis 7 (In-Memory)\n- Atomic Counter (INCR)\n- TTL Cache (url:code)")]
DB2[("π MongoDB 7 (Database)\n- SUrl Collection\n- Indexed shortUrl\n- Click Analytics")]
end
%% Client Interactions
C1 -->|"GET /:shortUrl"| M1
C2 -->|"POST /trpc/url.create"| M1
C3 -->|"GET /trpc/url.getOriginalUrl"| M1
M1 --> M2 --> M3 --> R1
%% Routing
R1 --> R4
R1 --> R2
R2 --> R3
R3 --> CTL1
R4 --> CTL2
%% Controllers to Service
CTL1 --> S1
CTL2 --> S1
%% Service internal logic
S1 --> S2
%% Service to Repositories
S1 --> REP1
S1 --> REP2
%% Repositories to Databases
REP1 -->|"Atomic INCR & GET/SET Cache"| DB1
REP2 -->|"Insert, Find & incrementClick"| DB2
sequenceDiagram
autonumber
actor Creator as Link Creator (User)
actor Visitor as Link Visitor (End User)
participant Server as Express Server (app.ts)
participant Redis as Redis Cache (Memory)
participant Mongo as MongoDB (Database)
Note over Creator, Server: 1. URL Creation Flow
Creator->>Server: POST /trpc/url.create { "originalUrl": "https://example.com" }
Server->>Redis: INCR url_shortner_counter (Atomic ID)
Redis-->>Server: Returns nextId (e.g. 3)
Server->>Server: Base62 Encode ID β shortUrl ("3")
Server->>Mongo: Insert Document { shortUrl: "3", originalUrl: "https://example.com" }
Server->>Redis: SET url:3 "https://example.com" (TTL 30 Days)
Server-->>Creator: Return Short URL ("http://localhost:3000/3")
Note over Visitor, Server: 2. Redirection & Click Analytics Flow
Visitor->>Server: GET /3 (Click Short Link in Browser)
Server->>Redis: GET url:3 (Check Cache)
alt Cache Hit (Sub-millisecond)
Redis-->>Server: Return originalUrl ("https://example.com")
else Cache Miss (Fallback)
Server->>Mongo: findByShortUrl("3")
Mongo-->>Server: Return Record
Server->>Redis: SET url:3 "https://example.com"
end
Server->>Mongo: incrementClick("3") (clicks + 1)
Server-->>Visitor: HTTP 302 Found (Location: https://example.com)
| Layer | Technology | Description |
|---|---|---|
| Runtime & Language | Node.js 20 & TypeScript 5 | Typed server-side JavaScript runtime |
| API Framework | Express.js 5 & tRPC 11 | Type-safe RPC router & Express HTTP server |
| Schema Validation | Zod | Runtime type safety & payload validation |
| Caching Layer | Redis 7 | In-memory cache for fast lookups & atomic ID counter |
| Database | MongoDB 7 (Mongoose 9) | Persistent document store for URL mapping & analytics |
| Orchestration | Docker & Docker Compose | Containerization for app, database, and cache |
βββ src/
β βββ config/ # Environment configuration & DB/Redis setup
β βββ controllers/ # Controller layer & Express redirect handlers
β βββ dtos/ # Data Transfer Objects & TypeScript interfaces
β βββ middlewares/ # Error handling, 404, logger middlewares
β βββ models/ # Mongoose database schemas (SUrlModel)
β βββ repositories/ # Database & Redis caching repository layer
β βββ routes/ # Express API & tRPC router definitions
β β βββ trpc/ # tRPC appRouter, context, and urlRouter
β β βββ v1/ # Express REST API v1 routes
β β βββ v2/ # Express REST API v2 routes
β βββ services/ # Business logic layer (SUrlService)
β βββ utils/ # ApiError, ApiResponse, Base62 encoder, asyncHandler
β βββ app.ts # Express app initialization & middleware stack
β βββ server.ts # Entrypoint, server boot & graceful shutdown
βββ Dockerfile # Multi-stage Docker build config
βββ docker-compose.yml # Multi-container orchestration (App, Mongo, Redis)
βββ .env.example # Template environment file
βββ tsconfig.json # TypeScript configuration
βββ package.json
βββ README.md
- Docker Desktop installed and running.
cp .env.example .envnpm run docker:upThis starts:
- Express API & tRPC Server:
http://localhost:3000 - MongoDB:
localhost:27017 - Redis:
localhost:6379
Endpoint: POST http://localhost:3000/trpc/url.create
curl --location 'http://localhost:3000/trpc/url.create' \
--header 'Content-Type: application/json' \
--data '{
"originalUrl": "https://trpc.io/docs/server/adapters/express"
}'Response:
{
"result": {
"data": {
"id": "6a5d09de6bbccc10a0c0a003",
"shortUrl": "3",
"originalUrl": "https://trpc.io/docs/server/adapters/express",
"clicks": 0,
"fullUrl": "http://localhost:3000/3",
"createdAt": "2026-07-19T17:31:10.604Z",
"updatedAt": "2026-07-19T17:31:10.604Z"
}
}
}Read-only lookup. Does not increment click analytics.
Endpoint: GET http://localhost:3000/trpc/url.getOriginalUrl
curl -G 'http://localhost:3000/trpc/url.getOriginalUrl' \
--data-urlencode 'input={"shortUrl":"3"}'Response:
{
"result": {
"data": {
"originalUrl": "https://trpc.io/docs/server/adapters/express",
"shortUrl": "3"
}
}
}Visits the short code, increments the click counter, and redirects the browser/client.
Endpoint: GET http://localhost:3000/:shortUrl
curl -i http://localhost:3000/3Response:
HTTP/1.1 302 Found
Location: https://trpc.io/docs/server/adapters/express
Found. Redirecting to https://trpc.io/docs/server/adapters/express| Script | Command | Description |
|---|---|---|
npm run dev |
node --import tsx --watch src/server.ts |
Starts development server with native hot reloading |
npm run add package-name |
sh -c 'npm i "$@" && docker exec express_app npm i "$@"' -- |
Installs dependency on Mac and Docker container simultaneously |
npm run build |
tsc |
Compiles TypeScript source to dist/ |
npm start |
node dist/server.js |
Runs production JavaScript build |
npm run docker:up |
docker compose up -d --build |
Launches all containers in background |
npm run docker:down |
docker compose down |
Stops Docker containers |
npm run docker:clean |
docker compose down -v |
Stops containers and removes database volumes |
npm run docker:reset |
docker compose down -v && docker compose up -d --build |
Full fresh container rebuild |
npm run docker:logs |
docker compose logs -f |
Tails live container logs |
npm run lint |
eslint . |
Runs ESLint analysis |
npm run format |
prettier --write "src/**/*.ts" |
Formats code with Prettier |
This project is open-source under the ISC License.