A minimal API to collect email subscriptions with SQLite.
- Node.js 24 LTS
- pnpm
pnpm install
pnpm buildpnpm startPort defaults to 3000. Override with the PORT env var.
Runs behind nginx — client IP is read from X-Forwarded-For.
curl -X POST http://localhost:3000/subscribe \
-H 'Content-Type: application/json' \
-d '{"email": "user@example.com"}'Responses
| Status | Body |
|---|---|
200 |
{ "ok": true } |
400 |
{ "error": "Invalid email address" } |
400 |
{ "error": "Invalid JSON" } |
400 |
{ "error": "Payload too large" } |
404 |
{ "error": "Not found" } |
Rate limited requests are silently dropped — they return 200 identically to a successful subscription.
pnpm import -- path/to/file.csvExpected format — one email per line, email header row:
email
user@example.com
another@example.com
Duplicate and invalid emails are skipped. A summary is printed on completion.
pnpm export -- path/to/output.csvExports all emails in the database in the same format accepted by import.
pnpm summaryPrints a count of total emails and new emails in the last 7 and 30 days:
Total: 1000
Last 7 days: 12
Last 30 days: 47
| Env var | Default | Description |
|---|---|---|
PORT |
3000 |
Port to listen on |
DB_PATH |
data/emails.db |
SQLite database path |
RATE_LIMIT_ENABLED |
true |
Enable IP and email rate limiting |
Rate limit: 10 requests per IP and per email address per hour.
A template nginx config is provided in email-list.nginx.conf. It:
- Redirects HTTP to HTTPS
- Exposes only
POST /subscribeand returns 404 for all other paths - Forwards the real client IP via
X-Forwarded-Forfor rate limiting - Enforces a 2 KB body size limit at the nginx level
Copy and adjust the file (replace example.com and TLS paths), then:
cp email-list.nginx.conf /etc/nginx/sites-available/email-list
ln -s /etc/nginx/sites-available/email-list /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginxCopy and adjust email-list.service, then:
useradd -r -s /sbin/nologin email-list
cp email-list.service /etc/systemd/system/
systemctl enable --now email-listThe service expects the app to be deployed to /opt/email-list.
pnpm test # build + run tests
pnpm check # format + lint + fixThe goal of this project is a long-running, stable service that requires minimal maintenance. Contributions should respect that goal.
No runtime dependencies. The API uses only Node.js built-ins and SQLite (via the built-in node:sqlite module available in Node.js 22+). Do not add npm packages to dependencies. If something can be done with the standard library, it should be.
LTS versions only. Node.js and all tooling should track Active or Maintenance LTS releases. Avoid features or syntax that require a non-LTS version.
Stability over features. Prefer simple, explicit code over clever abstractions. New features should have a clear operational use case and should not increase the maintenance surface.
It just an email list. This is a simple email collector, nothing more. No new production-facing features should be added to extend functionality, the production API surface should do nothing more than collect emails.