duan is a lightweight, high-performance URL shortener built on Cloudflare's edge infrastructure. It leverages Cloudflare Workers for serverless compute and D1 Database for reliable storage, offering global low-latency performance.
Completely free to run within Cloudflare's generous free tier limits:
- Runs entirely on Cloudflare Workers and D1 Database free tier
- No server costs or database hosting fees
- 100,000 Worker requests per day for free
- 5 million D1 database reads and 100,000 writes per day for free
- Global CDN distribution at no additional cost
- Fast Redirects: Serve redirects from the edge with minimal latency
- API-First Design: Complete RESTful API for managing short links
- Secure Authentication: API token-based authentication for administrative endpoints
- Batch Operations: Create multiple short links in a single request
- Custom Short Codes: Define your own memorable short codes or let the system generate random ones
- Analytics: Track visit counts and last access time for each link
- Lightweight: Minimal dependencies, optimized for edge deployment
- Cloudflare account with Workers and D1 access
- A domain name for your short links.
- Optional for pure API usage, Cloudflare provides a *.workers.dev subdomain
- Necessary for Duan Raycast extension
-
Execute SQL statements in Console
DROP TABLE IF EXISTS links; CREATE TABLE links ( short_code TEXT PRIMARY KEY, original_url TEXT NOT NULL, description TEXT, is_enabled INTEGER DEFAULT 1, created_at TEXT DEFAULT CURRENT_TIMESTAMP, last_visited_at TEXT, visit_count INTEGER DEFAULT 0 );
-
Fork the repository to your GitHub account.
-
Edit the
wrangler.jsoncfile and replace thedatabase_namewhitduan-dband thedatabase_idwith your D1 database ID. -
Create a project in Cloudflare Workers, select your forked repository.
-
Set API_TOKEN in Cloudflare Workers.
Step 7: Online random token generator: https://it-tools.tech/token-generator
-
Cloudflare provides a free domain for your worker, e.g.,
your-worker-name.workers.dev.You can test it with cURL or HTTPie.
-
Bind your own domain.
*This is required if you use the Duan Raycast extension, as .workers.dev has TLS issues.
For easy configuration, I recommend hosting your domain on Cloudflare. The images below illustrate how straightforward the setup process is.
-
How to use it?
The Duan extension in Raycast is currently the only UI available.
A web interface isn't available yet. π
-
Clone the repository:
git clone https://github.com/insv23/duan.git cd duan -
Install dependencies:
npm install
-
Create a D1 database:
npx wrangler d1 create prod-cf-d1-short-link
-
Update the
wrangler.jsoncfile with your database ID. -
Initialize the database schema:
npx wrangler d1 execute prod-cf-d1-short-link --remote --file=./schema.sql
-
Set up your API token:
npx wrangler secret put API_TOKEN
-
Start the deployment server:
npx wrangler deploy
This project includes a suite of Hurl tests for API and redirect behavior.
Whatβs covered
- Authentication: missing, malformed, invalid, and valid tokens (hurl/00_auth.hurl)
- Create/Get/List/Duplicate: single link lifecycle and 409 conflict (hurl/10_create_get_list.hurl)
- Update & Redirect Toggle: update url, description, is_enabled; 404 when disabled; 302 with correct Location when enabled (hurl/20_update_redirect_toggle.hurl)
- Delete: delete then verify 404 (hurl/30_delete.hurl)
- Batch Create: mixed success/errors (201) and all-invalid (400) with counts checks (hurl/40_batch.hurl)
Prerequisites
- Install Hurl (macOS): brew install hurl
- Configure variables file hurl/.env: base_url=https://your-domain api_token=your-api-token
Run tests
- Single file: hurl --test --variables-file hurl/.env hurl/00_auth.hurl
- Common flow: hurl --test --variables-file hurl/.env hurl/00_auth.hurl hurl --test --variables-file hurl/.env hurl/10_create_get_list.hurl hurl --test --variables-file hurl/.env hurl/20_update_redirect_toggle.hurl hurl --test --variables-file hurl/.env hurl/30_delete.hurl hurl --test --variables-file hurl/.env hurl/40_batch.hurl
Notes
- Tests use fixed shortcodes (e.g., hurl10-cases-001). Re-running against the same database may cause expected-201 creations to fail due to existing rows. Run against a fresh or staging DB, or clean data between runs.
- The redirect tests assert status 302 and the Location header; when disabled, redirect returns 404 with JSON error.
- Avoid running against production unless you understand the data changes these tests perform.
- GET /:shortcode
- Redirects to the original URL associated with the shortcode
-
POST /api/links
- Create a new short link
- Body:
{ "short_code": "custom", "url": "https://example.com", "description": "Optional description" }
-
POST /api/links/batch
- Create multiple short links in a single request
- Body: Array of link objects
[{ "url": "https://example.com", "short_code": "optional", "description": "optional" }, ...]
-
GET /api/links
- List all links
-
GET /api/links/:shortcode
- Get details for a specific link
-
GET /api/shortcodes
- Get a list of all shortcodes
-
PATCH /api/links/:shortcode
- Update an existing link
- Body:
{ "url": "https://new-url.com", "is_enabled": 1, "description": "Updated description" }
-
DELETE /api/links/:shortcode
- Delete a specific link
All API endpoints require an API token passed in the Authorization header:
Authorization: Bearer your-api-token
src/
βββ handlers/ // Request handler directory for specific endpoint logic
β βββ createBatchLinks.ts // Logic for batch link creation
β βββ createLink.ts // Logic for creating a single link
β βββ deleteLink.ts // Logic for deleting a specific link
β βββ getLink.ts // Logic for retrieving a specific link
β βββ listLinks.ts // Logic for listing all links
β βββ listShortcodes.ts // Logic for listing all shortcodes
β βββ redirect.ts // Logic for shortcode redirection
β βββ updateLink.ts // Logic for updating a link
βββ middleware/ // Middleware directory for request processing functions
β βββ auth.ts // Authentication middleware
βββ types/ // Type definitions directory for TypeScript types
β βββ env.ts // Environment variable type definitions
βββ utils/ // Utility functions directory for reusable helper functions
β βββ response.ts // Common response formatting functions
βββ index.ts // Project entry point responsible for routing
http POST https://your-worker-url.workers.dev/api/links/batch \
Authorization:"Bearer your-api-token" \
@batch-links.jsonExample batch-links.json:
[
{
"url": "https://example.com/page1",
"description": "Example page 1"
},
{
"short_code": "custom1",
"url": "https://example.com/page2",
"description": "Example page with custom shortcode"
}
]CREATE TABLE links (
short_code TEXT PRIMARY KEY,
original_url TEXT NOT NULL,
description TEXT,
is_enabled INTEGER DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
last_visited_at TEXT,
visit_count INTEGER DEFAULT 0
);Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.