Serverless Vercel KV Node.js REST API

smawl

A self-hosted, serverless URL shortener built on Node.js and Vercel KV. SHA-256 hashing, idempotent short IDs, multi-tier rate limiting, and a clean REST API — at zero infrastructure cost.

Why Another URL Shortener?

Most URL shorteners are a binary choice: a closed SaaS with opaque data policies and vendor lock-in, or an open-source tool that demands a dedicated database server and full-stack infrastructure. smawl fills the gap between the two.

Zero hosting cost

Vercel's free tier handles serverless functions and KV storage for real-world traffic.

One command deploy

vercel deploy — no servers to provision, no containers, no databases to manage.

Fully auditable

Collision handling, URL canonicalization, and rate limiting are all tested and open source.

Deterministic Short IDs

Short IDs are generated by running the normalized URL through SHA-256, taking the first 5 bytes of the digest, and encoding them in Base62 (a–z A–Z 0–9). The result is left-padded to 6 characters and capped at 8.

The same URL always produces the same ID — enabling idempotency without a lookup table query.

Idempotency & Collision Safety

Submitting the same URL twice returns the same short link without creating a duplicate KV entry. When a generated ID collides with an existing different URL, the algorithm appends an incrementing nonce and re-hashes recursively until a free slot is found.

Multi-Tier Rate Limiting

Counters live in Vercel KV — globally consistent across all serverless instances. Falls back gracefully to in-memory when KV is unavailable.

Client type Limit Window
Default / anonymous 30 req 1 min
Whitelisted API key / IP 100 req 1 min
Trusted app referrer 300 req 1 min

URL Canonicalization

Before hashing, every URL is normalized so variations of the same address don't create duplicate entries:

  • Protocol and hostname lowercased
  • Trailing slashes stripped from paths
  • Query strings and hash fragments preserved
  • Only http and https accepted — javascript: and data: schemes rejected
  • URLs longer than 2,048 characters rejected

API Reference

Two endpoints. That's it.

POST /api/shorten

Shortens a URL. Returns the same short URL for the same input (idempotent). Optionally accepts a custom alias.

Request body

{
  "url":      "https://example.com/very/long/path",  // required
  "customId": "myAlias"                           // optional, 3–10 alphanumeric
}

Success — 200 OK

{ "shortUrl": "https://smawl.vercel.app/myAlias" }

Error — 4xx

{ "error": "Custom ID must be 3-10 alphanumeric characters" }
GET /:shortId

Resolves a short ID and issues an HTTP 302 redirect to the original URL. Unknown IDs return an HTML error page — not a bare 404.

HTTP/1.1 302 Found
Location: https://example.com/very/long/path

Code Examples

The API is designed to be called programmatically from any language.

cURL

curl -X POST https://smawl.vercel.app/api/shorten \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/very/long/path"}'

JavaScript

const res = await fetch('https://smawl.vercel.app/api/shorten', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://example.com/very/long/path' }),
});
const { shortUrl } = await res.json();
console.log(shortUrl); // https://smawl.vercel.app/aBcDe1

Python

import requests

res = requests.post(
    'https://smawl.vercel.app/api/shorten',
    json={'url': 'https://example.com/very/long/path'}
)
print(res.json()['shortUrl'])

Architecture

Lean by design — two serverless functions, one KV store, and a static frontend.

Browser / Client
public/index.html + public/main.js
↓ HTTP
Vercel Serverless Functions
api/shorten.js  ·  api/[shortId].js
src/shorten.js  &  src/rateLimit.js
Validation · hashing · collision handling · KV I/O
Vercel KV (Redis)
URL map · rate-limit counters
File Role
api/shorten.js Serverless entry point for POST /api/shorten
api/[shortId].js Serverless entry point for GET /:shortId
src/shorten.js Core logic: validation, SHA-256 hashing, deduplication, KV storage
src/rateLimit.js Multi-tier rate limiting with KV backend and in-memory fallback
public/ Static frontend (HTML + vanilla JS)
server.js Local Express dev server mirroring Vercel routing at localhost:3000
tests/ Jest suite — validation, hashing, Base62, collision, idempotency, rate limiting

Who It's For

  • Developers embedding short-link generation into their own apps via the REST API.
  • Teams who want a self-hosted shortener with configurable rate limits and trusted-app whitelisting.
  • Learners exploring a real-world serverless Node.js project with Redis integration and collision-safe hashing.
  • Privacy-conscious users who want full control over where URL data lives and for how long.

Self-Host in Minutes

Requires Node.js v18+ and a Vercel account with KV enabled. Without KV credentials, URL mappings fall back to process memory — fine for local testing.

git clone <repo-url>
cd smawl && npm install
cp .env.local.example .env.local
# fill in KV_REST_API_URL + KV_REST_API_TOKEN
npm start
# Deploy to Vercel
vercel deploy

Run the test suite with npm test or npm test -- --coverage.

Security Design

  • Only http and https protocols are accepted — javascript: and data: URI schemes are rejected at validation time, preventing open-redirect abuse.
  • Generated short IDs derive solely from the SHA-256 hash of the normalized URL — they are never user-controlled unless a customId is explicitly provided and validated.
  • Rate limiting is enforced server-side in Vercel KV and cannot be bypassed by client-side tricks or distributing requests across browser tabs.
  • No user accounts or credentials are stored. The only data persisted in KV is the URL mapping itself.

Try It or Fork It

The live instance is at smawl.vercel.app. The source is on GitHub — fork it, deploy your own instance, or embed the API in your next project.