https://github.com/johnwmail/notejs
Lightweight serverless note-taking app — Cloudflare Workers & Vercel Edge
https://github.com/johnwmail/notejs
cloudflare-workers edge-computing note-taking redis serverless typescript upstash vanilla-js vercel-edge
Last synced: 5 days ago
JSON representation
Lightweight serverless note-taking app — Cloudflare Workers & Vercel Edge
- Host: GitHub
- URL: https://github.com/johnwmail/notejs
- Owner: johnwmail
- License: mit
- Created: 2026-05-12T09:12:39.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-05-13T09:08:15.000Z (3 months ago)
- Last Synced: 2026-05-13T11:12:17.381Z (2 months ago)
- Topics: cloudflare-workers, edge-computing, note-taking, redis, serverless, typescript, upstash, vanilla-js, vercel-edge
- Language: TypeScript
- Homepage: https://notejs-coral.vercel.app
- Size: 57.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# notejs
A lightweight, serverless note-taking web app written in TypeScript. Create, edit, and share notes with auto-save. Deploys on Cloudflare Workers (native KV) or Vercel Edge (Upstash Redis). No JS frameworks, no build step, zero dependencies — pure vanilla HTML/CSS/JS.
## Features
- **Simple Note Editor**: Lightweight web interface for creating and editing notes
- **Auto-Save**: Automatically saves note content every second
- **Shareable URLs**: Notes accessible via direct links with human-friendly WORDnn IDs
- **Multi-Deployment**: Cloudflare Workers or Vercel Edge
- **Zero Dependencies**: No npm runtime deps — KV via native bindings (Cloudflare) or REST fetch (Vercel)
- **XSS Protection**: User content is HTML-escaped
- **Responsive**: Works on desktop and mobile
- **Print Support**: Print-friendly interface
## Quick Start
### Prerequisites
- Node.js 24+
### Local Development
The app uses Wrangler (Cloudflare Workers) for local dev. KV is simulated by Miniflare — no account needed:
```bash
npm install
npm run dev
```
Open http://localhost:8787.
---
## Deploy to Cloudflare Workers (recommended)
**Storage**: Cloudflare KV — persistent, native, zero-config.
### Step 1: Create a KV namespace
```bash
npx wrangler kv:namespace create "KV"
```
### Step 2: Add the namespace ID to `wrangler.toml`
Copy the ID from the output and paste it:
```toml
[[kv_namespaces]]
binding = "KV"
id = "your-namespace-id"
```
### Step 3: Deploy
```bash
npm run deploy
```
Your app is live at `https://your-app.your-account.workers.dev` with persistent KV storage.
---
## Deploy to Vercel Edge
**Storage**: Upstash Redis — persistent with 7-day TTL.
### Step 1: Create Upstash Redis (or Vercel KV)
Create a Redis database at [upstash.com](https://upstash.com) (free tier). Copy the `KV_REST_API_URL` and `KV_REST_API_TOKEN` from the REST API section.
Alternatively, create via Vercel Dashboard: Storage → Create → Redis. This auto-injects the env vars.
### Step 2: Set environment variables
```bash
npx vercel env add KV_REST_API_URL production --value
npx vercel env add KV_REST_API_TOKEN production --value
```
### Step 3: Deploy
```bash
npx vercel --prod
```
`vercel.json` builds `src/vercel.ts` as an Edge Function and routes all requests to it. The app uses `VercelKVStorage` which connects to Upstash Redis via its REST API.
Your app is live at `https://your-app.vercel.app` with persistent KV storage.
---
## CI/CD (GitHub Actions)
The repository includes two workflows:
### Test (`test.yml`)
Runs on every push/PR to `feature/typescript`:
- `npm ci` → `tsc --noEmit` → `npm test`
### Deploy Cloudflare (`deploy-cloudflare.yml`)
Manual trigger (`workflow_dispatch`) — typecheck → deploy to Cloudflare Workers.
**Required secret:** `CF_API_TOKEN` — Cloudflare API token with Workers permissions (create in Cloudflare Dashboard → My Profile → API Tokens, template: "Edit Cloudflare Workers").
### Deploy Vercel (`deploy-vercel.yml`)
Manual trigger (`workflow_dispatch`) — typecheck → deploy to Vercel Edge.
**Required secrets:**
| Secret | Description |
|---|---|
| `VERCEL_TOKEN` | Vercel access token (create in Vercel Dashboard → Settings → Tokens) |
| `VERCEL_ORG_ID` | Your Vercel team/org ID (from `.vercel/project.json`) |
| `VERCEL_PROJECT_ID` | Your Vercel project ID (from `.vercel/project.json`) |
**To set up secrets:** Go to your GitHub repo → Settings → Secrets and variables → Actions, then add each value. Run workflows via Actions tab → select workflow → Run workflow.
## API
### GET /noteid/{noteId} (or `/?note={noteId}`)
Retrieve and display a note.
**Parameters:**
- `noteId` (path) or `note` (query): Note ID (alphanumeric, 3–32 chars, no I/O/0/1)
**Notes:**
- The preferred URL format is `/noteid/{noteId}` for shell-friendly links (e.g., `http://example.com/noteid/BLAST47`).
- Backwards compatibility: `/?note={noteId}` still works.
- Links use the request host or `x-forwarded-*` headers for reverse proxy support.
- Requests with `User-Agent` containing "curl" return plain text instead of HTML.
### POST /
Save or delete a note.
**Request body (JSON):**
```json
{
"noteId": "BLAST47",
"content": "Note content here"
}
```
**Response (JSON):**
```json
{
"success": true,
"noteId": "BLAST47"
}
```
**Behavior:**
- If `noteId` is empty, a random WORDnn ID is generated (e.g., "BLAST47")
- If `content` is empty or whitespace-only, the note is deleted
- Otherwise, the note is saved
**Examples:**
```bash
# Create new note
curl -X POST https://your-app.com/ \
-H "Content-Type: application/json" \
-d '{"content":"Hello World"}'
# Returns URL for curl requests
curl -X POST https://your-app.com/ \
-H "Content-Type: application/json" \
-H "User-Agent: curl/8.0" \
-d '{"content":"Hello World"}'
# → https://your-app.com/noteid/BLAST47
```
## Project Structure
```
.
├── src/
│ ├── workers.ts # Cloudflare Workers entry point
│ ├── vercel.ts # Vercel Edge entry point
│ ├── handler.ts # HTTP handlers (GET, POST, OPTIONS)
│ ├── handler.test.ts # Handler unit tests
│ └── lib/
│ ├── template.ts # Full HTML/CSS/JS UI template
│ ├── storage.ts # Storage interface + KV / Vercel KV backends
│ ├── storage.test.ts # Storage unit tests
│ ├── utils.ts # Note ID generation/validation, HTML escaping, ClientIP
│ └── types.ts # TypeScript type definitions
├── wrangler.toml # Cloudflare Workers configuration
├── vercel.json # Vercel deployment configuration
├── .vercelignore # Files excluded from Vercel deployment
├── .github/workflows/ # GitHub Actions CI/CD workflows
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── README.md # This file
```
## Scripts
| Command | Description |
|---|---|
| `npm run dev` | Start local dev server with Wrangler |
| `npm run deploy` | Deploy to Cloudflare Workers |
| `npm test` | Run Vitest unit tests |
| `npm run typecheck` | Run TypeScript type check (`tsc --noEmit`) |
## Configuration
### Note ID Format
Note IDs use a WORDnn format — a random dictionary word (3–5 uppercase letters, excluding ambiguous characters I/O/0/1) followed by 2 digits. Examples: `BLAST47`, `ACE23`, `ZEBRA99`.
### Storage
Both platforms use persistent KV storage with 7-day TTL. Cloudflare Workers uses native **Workers KV** via `env.KV`. Vercel Edge uses **Upstash Redis** via REST API (`KV_REST_API_URL` / `KV_REST_API_TOKEN`). Both implement the same `Storage` interface.
## Testing
```bash
npm test # Run Vitest
npm run typecheck # TypeScript type checking
```
## Security
- Input validation: Note IDs restricted to `[A-HJ-NP-Z2-9]{3,32}`
- XSS protection: User content server-side HTML-escaped before rendering
- No third-party frontend dependencies
## License
MIT License — feel free to use and modify for your needs.