Overview
Build a web-based Markdown Pastebin where users can paste markdown text, get a unique shareable URL, and view rendered markdown with syntax highlighting. No authentication required — pastes are anonymous and permanent.
Requirements
- Express.js backend serving both API and static frontend
- TypeScript with strict mode
- Create paste: POST /api/pastes accepts { content, title? } and returns { id, url, createdAt }
- View paste: GET /api/pastes/:id returns { id, title, content, createdAt, viewCount }
- Each paste gets a unique 8-character alphanumeric ID (nanoid)
- View count increments on each GET request
- Recent pastes: GET /api/pastes?limit=10 returns the 10 most recent pastes (title + id + createdAt only, no content)
- Frontend served from /public as static HTML/CSS/JS
- Home page (/) has a textarea for markdown input, a live preview panel, and a Create Paste button
- After creating a paste, redirect to /:id which shows the rendered markdown
- Paste view page (/:id) shows rendered HTML from markdown with syntax highlighting for code blocks
- Raw view: GET /api/pastes/:id/raw returns plain text content with Content-Type: text/plain
- GET /health returns { status: "ok", pasteCount: N }
- In-memory storage using a Map (no database)
- Maximum paste size: 100KB — reject larger with 413
- Global error handling middleware
File Structure
src/
├── index.ts # Entry point — Express server, static files, routes
├── routes/
│ ├── pastes.ts # CRUD API handlers for pastes
│ └── health.ts # Health check endpoint
├── middleware/
│ ├── errorHandler.ts # Global error handling middleware
│ └── sizeLimit.ts # Request body size validation (100KB max)
├── lib/
│ ├── store.ts # In-memory paste storage (Map-based)
│ └── id.ts # ID generation (nanoid, 8 chars)
├── types.ts # TypeScript interfaces (Paste, CreatePasteRequest)
public/
├── index.html # Home page — create paste with live preview
├── view.html # Paste view page — rendered markdown
├── css/
│ └── style.css # Styling — clean, minimal, responsive
├── js/
│ ├── editor.js # Live preview logic (textarea to rendered markdown)
│ ├── view.js # Fetch and render paste on view page
│ └── lib/
│ └── marked.min.js # Markdown parser (bundled, no CDN)
tests/
├── pastes.test.ts # Integration tests for paste CRUD
└── store.test.ts # Unit tests for in-memory store
package.json
tsconfig.json
README.md
Dependencies
- express@4 — HTTP server and static file serving
- nanoid@5 — Unique ID generation
- typescript@5 — Type safety
- tsx@4 — TypeScript execution
- vitest@1 — Test runner
- supertest@6 — HTTP testing
Acceptance Criteria
Edge Cases
- Empty content field returns 400 with "content is required"
- Content over 100KB returns 413 with "paste too large"
- Non-existent paste ID returns 404 with "paste not found"
- Missing title defaults to "Untitled"
- Special characters in markdown (HTML injection) must be sanitized in rendered output
- Very long title truncated to 200 chars
- Concurrent view count increments are acceptable to lose (in-memory, no locks needed)
Overview
Build a web-based Markdown Pastebin where users can paste markdown text, get a unique shareable URL, and view rendered markdown with syntax highlighting. No authentication required — pastes are anonymous and permanent.
Requirements
File Structure
Dependencies
Acceptance Criteria
Edge Cases