A high-performance URL shortener service built with Next.js and Upstash Redis. This service provides a simple API to create, manage, and track shortened URLs with analytics.
- URL Shortening: Generate short, unique slugs (6 characters) for any URL
- Custom Slugs: Rename slugs to custom values
- Analytics: Track views and last access time for each shortened URL
- Expiration: Set expiration dates for temporary links
- CORS Support: Pre-configured CORS for JUKI ecosystem and custom origins
- Redis Storage: Fast, scalable storage using Upstash Redis
- TypeScript: Fully typed for better developer experience
- Framework: Next.js 16 (App Router)
- Language: TypeScript 5.9
- Database: Upstash Redis
- ID Generation: nanoid with custom alphabet
- Libraries: @juki-team/commons
src/
├── app/
│ ├── [slug]/ # Dynamic route for URL redirection
│ │ └── route.ts
│ ├── api/
│ │ ├── create/ # Create shortened URLs
│ │ │ └── route.ts
│ │ ├── list/ # List all shortened URLs
│ │ │ └── route.ts
│ │ ├── rename/ # Rename slugs
│ │ │ └── route.ts
│ │ └── helpers.ts # CORS and request helpers
│ └── route.ts # Root route
├── types/
│ ├── url.ts # URL-related types
│ └── commons.ts # Common type exports
├── helpers/ # Utility functions
└── constants/ # Configuration constants
- Node.js 18+ or Bun
- Yarn package manager
- Upstash Redis account
Create a .env file in the root directory with the following variables:
NODE_ENV=development
# Upstash Redis Configuration (required)
STORAGE_KV_REST_API_URL=your_upstash_redis_url
STORAGE_KV_REST_API_TOKEN=your_upstash_redis_token
# Optional: Additional allowed CORS origins (comma-separated)
ORIGINS=https://example.com,https://another-domain.com
# Optional: JUKI Service Configuration
NEXT_PUBLIC_JUKI_SOCKET_BASE_URL=
NEXT_PUBLIC_JUKI_SERVICE_V2_URL=
NEXT_PUBLIC_JUKI_TOKEN_NAME=See .env.example for a template.
# Install dependencies
yarn install
# Run development server
yarn devThe server will start on http://localhost:3074.
# Build for production
yarn build
# Start production server
yarn start
# Lint code
yarn lintEndpoint: POST /api/create
Request Body:
{
"url": "https://example.com/very/long/url",
"expiresInDays": 30,
"origin": "https://custom-domain.com"
}Fields:
url(required): The original URL to shortenexpiresInDays(optional): Expiration in days (0 or omit for no expiration)origin(optional): Custom domain for the shortened URL in response
Response:
{
"success": true,
"message": "ok",
"content": {
"shortenedUrl": "https://juki.ly/Ab3DeF",
"slug": "Ab3DeF",
"url": "https://example.com/very/long/url"
}
}Endpoint: GET /{slug}
Redirects to the original URL and increments view count.
Responses:
302: Redirect to original URL404: Slug not found410: Link expired
Endpoint: GET /api/list
Response:
{
"success": true,
"message": "ok",
"contents": [
{
"slug": "Ab3DeF",
"url": "https://example.com/very/long/url",
"createdAt": 1234567890000,
"expiresAt": 1234567890000,
"views": 42,
"lastAccessAt": 1234567890000
}
],
"meta": {
"total": 1
}
}Endpoint: POST /api/rename
Request Body:
{
"oldSlug": "Ab3DeF",
"newSlug": "MyCustomSlug"
}Response:
{
"success": true,
"message": "ok",
"content": {
"slug": "MyCustomSlug"
}
}Endpoint: GET /api/version
Returns the current API version.
type UrlBodyType = {
url: string; // Original URL
createdAt: number; // Timestamp (ms)
expiresAt: number; // Expiration timestamp (ms), 0 = never expires
views: number; // Number of times accessed
lastAccessAt: number; // Last access timestamp (ms)
}- 6 characters long
- Custom alphabet:
23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz - Excludes confusing characters (0, O, 1, I, l)
- Collision detection ensures uniqueness
The easiest way to deploy this app is using the Vercel Platform.
- Push your code to a Git repository
- Import the project to Vercel
- Configure environment variables
- Deploy
This is a standard Next.js application and can be deployed to any platform that supports Next.js:
- Netlify
- AWS Amplify
- Railway
- Render
- Self-hosted with Docker
Private project for JUKI ecosystem.