A very performant and light (2mb in memory) link shortener and tracker. Written in Rust and React and uses Postgres/SQLite.
1# Link Shortener API Documentation
2
3## Base URL
4`http://localhost:8080`
5
6## Authentication
7The API uses JWT tokens for authentication. Include the token in the Authorization header:
8```
9Authorization: Bearer <your_token>
10```
11
12### Register
13Create a new user account.
14
15```bash
16POST /api/auth/register
17```
18
19Request Body:
20```json
21{
22 "email": string, // Required: Valid email address
23 "password": string // Required: Password
24}
25```
26
27Example:
28```bash
29curl -X POST http://localhost:8080/api/auth/register \
30 -H "Content-Type: application/json" \
31 -d '{
32 "email": "user@example.com",
33 "password": "your_password"
34 }'
35```
36
37Response (200 OK):
38```json
39{
40 "token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
41 "user": {
42 "id": 1,
43 "email": "user@example.com"
44 }
45}
46```
47
48### Login
49Authenticate and receive a JWT token.
50
51```bash
52POST /api/auth/login
53```
54
55Request Body:
56```json
57{
58 "email": string, // Required: Registered email address
59 "password": string // Required: Password
60}
61```
62
63Example:
64```bash
65curl -X POST http://localhost:8080/api/auth/login \
66 -H "Content-Type: application/json" \
67 -d '{
68 "email": "user@example.com",
69 "password": "your_password"
70 }'
71```
72
73Response (200 OK):
74```json
75{
76 "token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
77 "user": {
78 "id": 1,
79 "email": "user@example.com"
80 }
81}
82```
83
84## Protected Endpoints
85
86### Health Check
87Check if the service and database are running.
88
89```bash
90GET /health
91```
92
93Example:
94```bash
95curl http://localhost:8080/health
96```
97
98Response (200 OK):
99```json
100"Healthy"
101```
102
103Response (503 Service Unavailable):
104```json
105"Database unavailable"
106```
107
108### Create Short URL
109Create a new shortened URL with optional custom code. Requires authentication.
110
111```bash
112POST /api/shorten
113```
114
115Request Body:
116```json
117{
118 "url": string, // Required: The URL to shorten
119 "custom_code": string, // Optional: Custom short code
120 "source": string // Optional: Source of the request
121}
122```
123
124Examples:
125
1261. Create with auto-generated code:
127```bash
128curl -X POST http://localhost:8080/api/shorten \
129 -H "Content-Type: application/json" \
130 -H "Authorization: Bearer YOUR_TOKEN" \
131 -d '{
132 "url": "https://example.com",
133 "source": "curl-test"
134 }'
135```
136
137Response (201 Created):
138```json
139{
140 "id": 1,
141 "user_id": 1,
142 "original_url": "https://example.com",
143 "short_code": "Xa7Bc9",
144 "created_at": "2024-03-01T12:34:56Z",
145 "clicks": 0
146}
147```
148
1492. Create with custom code:
150```bash
151curl -X POST http://localhost:8080/api/shorten \
152 -H "Content-Type: application/json" \
153 -H "Authorization: Bearer YOUR_TOKEN" \
154 -d '{
155 "url": "https://example.com",
156 "custom_code": "example",
157 "source": "curl-test"
158 }'
159```
160
161Response (201 Created):
162```json
163{
164 "id": 2,
165 "user_id": 1,
166 "original_url": "https://example.com",
167 "short_code": "example",
168 "created_at": "2024-03-01T12:34:56Z",
169 "clicks": 0
170}
171```
172
173Error Responses:
174
175Invalid URL (400 Bad Request):
176```json
177{
178 "error": "URL must start with http:// or https://"
179}
180```
181
182Custom code taken (400 Bad Request):
183```json
184{
185 "error": "Custom code already taken"
186}
187```
188
189Invalid custom code (400 Bad Request):
190```json
191{
192 "error": "Custom code must be 1-32 characters long and contain only letters, numbers, underscores, and hyphens"
193}
194```
195
196Unauthorized (401 Unauthorized):
197```json
198{
199 "error": "Unauthorized"
200}
201```
202
203### Get All Links
204Retrieve all shortened URLs for the authenticated user.
205
206```bash
207GET /api/links
208```
209
210Example:
211```bash
212curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8080/api/links
213```
214
215Response (200 OK):
216```json
217[
218 {
219 "id": 1,
220 "user_id": 1,
221 "original_url": "https://example.com",
222 "short_code": "Xa7Bc9",
223 "created_at": "2024-03-01T12:34:56Z",
224 "clicks": 5
225 },
226 {
227 "id": 2,
228 "user_id": 1,
229 "original_url": "https://example.org",
230 "short_code": "example",
231 "created_at": "2024-03-01T12:35:00Z",
232 "clicks": 3
233 }
234]
235```
236
237### Redirect to Original URL
238Use the shortened URL to redirect to the original URL. Source tracking via query parameter is supported.
239
240```bash
241GET /{short_code}?source={source}
242```
243
244Example:
245```bash
246curl -i http://localhost:8080/example?source=email
247```
248
249Response (307 Temporary Redirect):
250```http
251HTTP/1.1 307 Temporary Redirect
252Location: https://example.com
253```
254
255Error Response (404 Not Found):
256```json
257{
258 "error": "Not found"
259}
260```
261
262## Custom Code Rules
2631. Length: 1-32 characters
2642. Allowed characters: letters, numbers, underscores, and hyphens
2653. Case-sensitive
2664. Cannot use reserved words: ["api", "health", "admin", "static", "assets"]
267
268## Rate Limiting
269Currently, no rate limiting is implemented.
270
271## Notes
2721. All timestamps are in UTC
2732. Click counts are incremented on successful redirects
2743. Source tracking is supported both at link creation and during redirection via query parameter
2754. Custom codes are case-sensitive
2765. URLs must include protocol (http:// or https://)
2776. All create/read operations require authentication
2787. Users can only see and manage their own links
279
280## Error Codes
281- 200: Success
282- 201: Created
283- 307: Temporary Redirect
284- 400: Bad Request (invalid input)
285- 401: Unauthorized (missing or invalid token)
286- 404: Not Found
287- 503: Service Unavailable
288
289## Database Schema
290```sql
291-- Users table for authentication
292CREATE TABLE users (
293 id SERIAL PRIMARY KEY,
294 email VARCHAR(255) NOT NULL UNIQUE,
295 password_hash TEXT NOT NULL
296);
297
298-- Links table with user association
299CREATE TABLE links (
300 id SERIAL PRIMARY KEY,
301 original_url TEXT NOT NULL,
302 short_code VARCHAR(8) NOT NULL UNIQUE,
303 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
304 clicks BIGINT NOT NULL DEFAULT 0,
305 user_id INTEGER REFERENCES users(id)
306);
307
308-- Click tracking with source information
309CREATE TABLE clicks (
310 id SERIAL PRIMARY KEY,
311 link_id INTEGER REFERENCES links(id),
312 source TEXT,
313 query_source TEXT,
314 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
315);
316
317-- Indexes
318CREATE INDEX idx_short_code ON links(short_code);
319CREATE INDEX idx_user_id ON links(user_id);
320CREATE INDEX idx_link_id ON clicks(link_id);
321```