Generated Computers
Locals: an Apple Silicon engine that runs local LLMs up to 3x faster. A product of Generated Computers.
翻译准备中
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Self-hosted headless CMS for frontend developers. Built with NestJS, Next.js, and PostgreSQL.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗WordPress — You're maintaining PHP, plugins, and a theme engine for a simple blog.
Contentful — $300/mo for features your project will use maybe 20% of.
Strapi — Spent 4 hours fighting Docker, Node version conflicts, and a half-broken admin UI.
Sanity — Vendor lock-in, CDN pricing surprises, and a learning curve nobody warned you about.
NodePress is different. It's a headless CMS built specifically for frontend developers who need a backend for their clients — not a platform to build a career on.
npx create-nodepress-app my-project
That's it. The CLI scaffolds the project, generates cryptographically secure secrets, installs all dependencies, and tells you exactly what to do next.
Open http://localhost:5173 → fill in a form → you have a working CMS with a REST API, GraphQL API, admin panel, and real-time WebSocket events.
On a server (Linux + Docker), one line — with automatic HTTPS when you pass a domain:
curl -fsSL https://raw.githubusercontent.com/buildwithkode/nodepress/main/scripts/install.sh | DOMAIN=cms.example.com bash
Putting it live for a client? The deploy guides walk through it step by step: your own server (DigitalOcean, Hetzner, Lightsail…) with Docker or without Docker, Railway or DigitalOcean App Platform, and a go-live checklist. Shared hosting (cPanel) can't run NodePress.
From your frontend: npm install @nodepress-cms/client — see the client README. From AI assistants: npx create-nodepress-app mcp (MCP server).
| Feature | What it does |
|---|---|
| Visual content type builder | Define your schema in the UI — no code, no config files |
| Auto-generated REST API | Every content type instantly gets GET, POST, PUT, DELETE endpoints |
| Advanced REST Filter Engine | Nested filtering with operators: where[price][gte]=100, where[tag][in]=a,b, OR & AND logic |
| GraphQL API | Apollo Server with full queries + mutations — entries, content types, webhooks, media |
| Multi-locale (i18n) | Per-entry locale field with ?locale= filtering on all endpoints |
| Side-by-Side Translation Studio | Dual-pane comparative translation workspace with 1-click "Copy Empty Fields from Reference" |
| Content relations | Link entries across types — ?populate=author,author.company ($O(\text{depth})$ batched lookups) |
| Next.js Live Draft Mode | Built-in Next.js App Router Draft Mode integration, signed HMAC tokens & preview banner |
| Field-Level Security (RBAC/PBAC) | Granular readRoles (strip fields from public responses) & writeRoles (protect fields from edits) |
| Bulk operations | Publish, archive, or delete hundreds of entries in one click |
| Export / Import | Download all entries as JSON, re-import to seed or migrate |
| Version history | Every entry save is versioned — restore any previous state in one click |
| Schema versioning | Content type changes are tracked — see exactly what changed and when |
| Field data migrations | Rename or retype a field and existing entries follow — previewed before you save |
| Conditional fields | "Show only if…" rules between fields; hidden fields are never required |
| Reusable components |
| Feature | What it does |
|---|---|
| TypeScript Codegen | Run npx nodepress generate:types to generate 100% type-safe TypeScript interfaces |
@nodepress-cms/client | Typed, dependency-free JS/TS client — lists, entries, GraphQL, forms, image URLs, live preview |
| Starter sites | Next.js, Astro and Nuxt starters in the NodePress repo |
| OpenAPI 3.1 | /api/openapi.json describes your own content types — generate clients with any OpenAPI tool |
| Schema as code | schema:pull / schema:diff / schema:push keep the content model in git and check it in CI |
| MCP server | Let AI assistants read your content and create drafts (create-nodepress-app mcp) |
| Real-time WebSocket | Socket.io gateway — subscribe to entry:created, entry:updated, media:uploaded, and more |
| Collaborative Presence & Locking | Live user presence bubbles & 60s renewable soft-locks preventing concurrent edit collisions |
| Plugin Hook & Filter Bus | PluginHookBus with priority lifecycle actions (beforeCreate, afterUpdate) and data filters |
| Plugins from the admin | Upload signed plugin packages, or browse and install from the plugin directory |
| API keys | Scoped keys with per-key rate limiting (read / write / all · per content type) |
| Webhooks | HTTP callbacks on any event — HMAC signed, with retry + exponential backoff |
| Swagger UI | Interactive API docs at /api/docs — test every endpoint in the browser |
| GraphQL Playground | Apollo Sandbox at /graphql — explore your full schema and run mutations interactively |
| Feature | What it does |
|---|---|
| Dynamic Image Resizing | On-the-fly Sharp transformations: GET /api/media/:file/transform?w=800&h=600&q=80&format=webp |
| Focal points & smart formats | Set a focal point per image for fit=cover crops; format=auto serves AVIF/WebP by browser |
| Persistent Variant Cache | Automatically saves resized WebP/Avif variants to disk for instant subsequent serving |
| File uploads | Images, PDFs, videos — up to 10MB per file with magic bytes validation |
| Folder organisation | Group media into folders in the admin panel |
| Storage options | Switch from local disk to any S3-compatible bucket (AWS, DigitalOcean Spaces, MinIO), Cloudflare R2 or ImageKit, from the admin or with env vars |
| Feature | What it does |
|---|---|
| Two-Factor Auth (2FA / TOTP) | Built-in RFC 6238 TOTP (Google Authenticator, Authy, 1Password) + 8 backup recovery codes |
| Emergency Admin Reset CLI | npm run reset-admin <email> <pass> instant password & 2FA recovery from terminal |
| Forgot & Reset Password | Secure 15-minute token flow with SMTP support & instant dev-mode link preview |
| Field-Level Access (RBAC) | Restrict read and write access per field to specific roles |
| Roles | Admin · Editor · Contributor · Viewer, plus your own custom roles with per-content-type permissions |
| Single sign-on | Any OpenID Connect provider (Google Workspace, Entra ID, Okta, Auth0, Keycloak) — free |
| Per-content-type overrides | Give Editor full access to blog but read-only on legal |
| JWT + refresh tokens | 7-day access tokens, 30-day silent refresh (HttpOnly cookie with rotation) |
| Helmet + CORS | Security headers on every response, strict multi-origin enforcement |
| Rate limiting | Per-IP throttling on all endpoints, stricter on auth routes |
| Feature | What it does |
|---|---|
| Form builder | Create forms in the UI, get a hosted submission endpoint |
| Email actions | Send submission data to any email address |
| Webhook actions | Forward submissions to Zapier, Make, or your own endpoint |
| 3-layer spam protection | Rate limiting + honeypot field + captcha (Turnstile / hCaptcha / reCAPTCHA) |
| Feature | What it does |
|---|---|
| PostgreSQL Full-Text Search | Indexed GIN full-text search with database-pushed LIMIT / OFFSET pagination |
| Docker Compose stack | One command deploys the full stack — backend, frontend, Nginx, Redis, PgBouncer |
| PgBouncer | Connection pooling — handles traffic spikes without exhausting PostgreSQL |
| Redis | Query-result cache + Socket.io multi-instance sync |
| Prometheus + Grafana | Pre-built dashboard, alerting rules for high error rate, high latency, backend-down |
| Automated daily backups | pg_dump every night at 2am UTC, 30-day retention, optional S3 upload |
| Backup & restore commands | npm run backup / npm run restore — database + uploads in one archive, guarded restore (confirmation, overwrite and version checks, single transaction) |
| Graceful shutdown | SIGTERM / SIGINT handled — no dropped requests on deploy |
| Feature | NodePress | Strapi v4/v5 | WordPress Headless | Payload CMS | Directus |
|---|---|---|---|---|---|
| Price | Free forever | Free / $499+/mo | Free / Plugin fees | Free / Enterprise | Free / $99+/mo |
| Self-hosted | ✅ | ✅ | ✅ | ✅ | ✅ |
| Setup time | ~10 min | 2–4 hrs | 1–2 hrs | 30 min | 30 min |
| Advanced Nested Filters | ✅ where[x][op] | ✅ | 🟡 Plugin req. | ✅ | ✅ |
| TypeScript Codegen | ✅ Built-in CLI | 🟡 Manual | ❌ | ✅ | 🟡 Export |
| Next.js Live Draft Mode | ✅ Integrated | 🟡 Plugin | 🟡 Plugin | ✅ | 🟡 Preview URL |
| Field-Level Security | ✅ Built-in | ✅ Enterprise | 🟡 Plugin | ✅ Hooks | ✅ Permissions |
| Dynamic Image Resizing | ✅ On-the-fly WebP | 🟡 Plugin/CDN | 🔴 Static only | 🟡 Config | ✅ Asset API |
| Plugin / Hook Bus | ✅ Actions & Filters | ✅ Lifecycles |
Where others are ahead: NodePress is new — the plugin ecosystem, community and hosted cloud of Strapi, WordPress, Payload and Directus are far larger, and NodePress runs on PostgreSQL only.
You need three things. Already have them? Jump to Quick Start.
1. Node.js 18+
node -v # must show v18 or higher
Download from nodejs.org — install the LTS version. npm 12 (bundled with newer Node releases) needs Node.js 22.22+, 24.15+ or 26+; NodePress installs cleanly with it.
2. Git
git --version # any version is fine
Download from git-scm.com.
3. PostgreSQL 14+ (or use Docker — see Option A below)
psql --version # must show 14 or higher
Download from postgresql.org.
Pass --docker to include the Docker setup — it manages PostgreSQL and Redis for you.
npx create-nodepress-app my-project --docker
cd my-project
docker-compose up -d # starts PostgreSQL + Redis
npm run migrate # creates all DB tables
npm run dev # backend :3000 + admin panel :5173 (one command)
npx create-nodepress-app my-project
cd my-project
Open backend/.env and set your PostgreSQL credentials:
DATABASE_URL="postgresql://postgres:YOUR_PASSWORD@localhost:5432/nodepress"
Then, from the project root:
npm run migrate # creates all DB tables
npm run dev # backend :3000 + admin panel :5173 (one command)
Works with no code changes: prisma/schema.prisma already declares both a pooled url and a directUrl, which is exactly the split Supabase needs.
In your Supabase project open Connect. You'll see several connection strings — which one goes where matters:
| Supabase mode | Port | Goes in | Used for |
|---|---|---|---|
| Transaction pooler | 6543 | DATABASE_URL | Runtime queries (PgBouncer-pooled) |
| Session pooler | 5432 | DIRECT_URL | Migrations (DDL can't run through a transaction pooler) |
DATABASE_URL="postgresql://postgres.<ref>:<PASSWORD>@aws-0-<region>.pooler.supabase.com:6543/postgres?pgbouncer=true&connection_limit=1"
DIRECT_URL="postgresql://postgres.<ref>:<PASSWORD>@aws-0-<region>.pooler.supabase.com:5432/postgres"
Then create the tables — note this is not npm run migrate:
cd backend && npx prisma migrate deploy
Three things that will bite you otherwise:
db.<ref>.supabase.co). It is IPv6-only unless you've bought the IPv4 add-on, so on most home and office networks it fails with P1001: Can't reach database server — which reads like Supabase is down when it's actually unroutable from your machine. Use the Session pooler for DIRECT_URL instead; it's the same database over IPv4. To check: dig +short A db.<ref>.supabase.co returning nothing means IPv6-only.?pgbouncer=true&connection_limit=1 on DATABASE_URL. Without it Prisma eventually throws prepared statement "s0" already exists, and only under concurrency — so it looks intermittent and unrelated to configuration.npx prisma migrate deploy, not npm run migrate. That script runs prisma migrate dev, which wants a shadow database and CREATE DATABASE rights the pooler user doesn't have. migrate dev is for authoring new migrations locally; migrate deploy applies existing ones.If your password contains characters like @, : or /, percent-encode them (@ becomes %40) or the URL won't parse.
Everything else — JWT_SECRET, CORS_ORIGIN, PORT — is unchanged. Since there's no local database daemon, nothing needs starting after a reboot.
| Flag | Effect |
|---|---|
--docker | Include the Docker setup (docker-compose*.yml, nginx/, caddy/, monitoring/, docker:* scripts). Manages PostgreSQL + Redis for you. |
--sentry | Include Sentry error tracking (@sentry/* packages, ~100 MB). Off by default — without it the scaffold is lighter and ships no error-tracking code. Enable later by re-scaffolding with --sentry, or set SENTRY_DSN / NEXT_PUBLIC_SENTRY_DSN once the packages are installed. |
npx create-nodepress-app my-project --docker --sentry # both
git clone https://github.com/buildwithkode/nodepress.git
cd nodepress
# Configure environment
cp backend/.env.example backend/.env
# Edit backend/.env — set DATABASE_URL, JWT_SECRET, CORS_ORIGIN
cp frontend/.env.local.example frontend/.env.local
# Install everything (npm workspaces — one install for backend + frontend)
npm install
# Create DB tables, then start backend (:3000) + admin panel (:5173) together
npm run migrate
npm run dev
Open http://localhost:5173 — you'll see the NodePress setup screen.
Enter your site name, email, and password. Save these credentials — password reset requires SMTP to be configured. If you lose access before setting up SMTP, run npx prisma studio in the backend/ folder to reset directly.
Any Ubuntu 22.04 VPS works. Recommended providers:
| Provider | Spec | Price |
|---|---|---|
| Hetzner | 2 vCPU · 4GB RAM | ~€6/mo |
| DigitalOcean | 2 vCPU · 2GB RAM | ~$12/mo |
| Vultr | 1 vCPU · 2GB RAM | ~$6/mo |
Minimum: 1 vCPU · 1GB RAM. Recommended: 2 vCPU · 2GB RAM for Grafana + Redis.
curl -fsSL https://get.docker.com | sh
git clone https://github.com/buildwithkode/nodepress.git
cd nodepress
Create your production environment file:
# backend/.env
NODE_ENV=production
DATABASE_URL=postgresql://postgres:your_password@localhost:5432/YOUR_NODEPRESS_DATABASE
DIRECT_URL=postgresql://postgres:STRONG_PASSWORD@db:5432/YOUR_NODEPRESS_DATABASE
DB_PASSWORD=STRONG_PASSWORD
JWT_SECRET=<run: openssl rand -hex 32>
CORS_ORIGIN=https://yourdomain.com
APP_URL=https://yourdomain.com
SITE_URL=https://yourdomain.com
# SMTP — required for password reset (see providers below)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASS=xxxx xxxx xxxx xxxx
[email protected]
# Protect the metrics endpoint
METRICS_TOKEN=<run: openssl rand -hex 16>
Open nginx/default.conf and replace YOUR_DOMAIN with your actual domain:
server_name yourdomain.com www.yourdomain.com;
NODE_ENV=production docker-compose -f docker-compose.prod.yml up -d --build
This starts: PostgreSQL · PgBouncer · Redis · NestJS backend · Next.js frontend · Nginx · Prometheus · Grafana · daily backup job.
Migrations run automatically on first start.
apt install certbot python3-certbot-nginx
certbot --nginx -d yourdomain.com -d www.yourdomain.com
Then uncomment the HTTPS server block in nginx/default.conf and reload:
docker-compose -f docker-compose.prod.yml restart nginx
Visit https://yourdomain.com → complete the setup wizard → you're live.
Development: No SMTP needed — the reset link is shown directly in the browser UI. Production: Configure one of the providers below before going live.
Gmail — free, works with any Google account
backend/.env:SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASS=xxxx xxxx xxxx xxxx
[email protected]
Resend — recommended for developers, 100 free emails/day
backend/.env:SMTP_HOST=smtp.resend.com
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=resend
SMTP_PASS=re_xxxxxxxxxxxx
[email protected]
SendGrid — 100 free emails/day
backend/.env:SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=SG.xxxxxxxxxxxx
[email protected]
Before going live, verify:
JWT_SECRET is at least 32 random characters (openssl rand -hex 32)NODE_ENV=production is setCORS_ORIGIN matches your exact frontend domain — no trailing slashMETRICS_TOKEN is set — otherwise /api/metrics is publicly readableNo! For consumer websites and visitor frontends (Next.js, React, Astro, Vue, Svelte, or mobile apps), content reading and form submissions require ZERO authentication tokens. You can query published content directly from client-side or server-side code without passing headers or secret keys.
| Tier | Endpoints | Auth Header Needed? | Typical Frontend Use Case |
|---|---|---|---|
| Tier 1: Public Read APIs | GET /api/:contentTypeGET /api/:contentType/:slugGET /api/brandPOST /api/submit/:formSlugGET /api/sitemap.xml | None (Zero Token) | Visitor frontends, blog articles, product catalogs, contact forms, SEO sitemaps |
| Tier 2: Headless Frontends & SSG | All endpoints (scoped by key permissions) | X-API-Key: np_live_... | Jamstack static site builds (Astro, Next.js SSG), CI/CD content sync pipelines, external microservices |
| Tier 3: Admin & Content Mutations | POST/PUT/DELETE /api/entriesPOST /api/media/upload/api/users | Authorization: Bearer <JWT> | Admin dashboards, content editing, schema builder, media asset manager |
// Fetch published articles — zero headers, zero token required!
const res = await fetch('http://localhost:3001/api/posts?status=published');
const { data: posts, meta } = await res.json();
console.log(`Fetched ${posts.length} posts (Total: ${meta.total})`);
@/lib/nodepress)import { fetchEntries, fetchEntry } from '@/lib/nodepress';
// List entries with automatic pagination & sorting
const { data: articles } = await fetchEntries({
type: 'blog',
page: 1,
limit: 10,
sort: 'createdAt:desc',
});
// Fetch a single entry by slug (for dynamic [slug] routes)
const post = await fetchEntry({
type: 'blog',
slug: 'hello-world',
});
// SSG build or external microservice
const res = await fetch('http://localhost:3001/api/entries?contentTypeId=articles', {
headers: {
'X-API-Key': process.env.NODEPRESS_API_KEY, // e.g. np_live_...
},
next: { revalidate: 3600 },
});
const entries = await res.json();
The GraphQL endpoint is at /graphql. Apollo Sandbox (interactive playground) is available in all environments — click GraphQL Playground in the Developer sidebar.
All write mutations and protected queries require a JWT Bearer token. Three ways to get one:
Option A — Login API (curl):
curl -X POST http://localhost:3001/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"yourpassword"}'
# Response: { "access_token": "eyJhbGci..." }
Option B — Browser cookie: Log into the admin panel → open DevTools (F12) → Application → Cookies → copy the value of np_token. That is your Bearer token (valid 7 days).
Option C — API key: Create one in Admin → API Keys. Use X-API-Key: np_yourkey header instead of Bearer token (no expiry).
Use the token in any request:
Authorization: Bearer eyJhbGci...
In Apollo Sandbox: click Headers tab at the bottom → add { "Authorization": "Bearer eyJhbGci..." }.
query { entries(contentTypeId: 1, status: "published") { total data { id slug data } } }
query { entry(id: 1) { id slug status data createdAt } }
query { contentTypes { id name schema } }
query { contentType(id: 1) { id name schema } }
query { mediaFiles(page: 1, limit: 20) { total data { id filename url mimetype } } }
query { webhooks { id name url events enabled } } # admin only
query { webhookDeliveries(webhookId: 1) { id status attempts responseStatus } }
mutation { createEntry(contentTypeId: 1, slug: "hello", locale: "en", status: "draft", data: "{\"title\":\"Hello\"}") { id slug } }
mutation { updateEntry(id: 1, status: "published", data: "{\"title\":\"Updated\"}") { id updatedAt } }
mutation { deleteEntry(id: 1) { message } } # soft delete
mutation { restoreEntry(id: 1) { id slug } }
mutation { purgeEntry(id: 1) { message } } # permanent, admin only
mutation { restoreEntryVersion(entryId: 1, versionId: 3) { id updatedAt } }
mutation { bulkPublishEntries(ids: [1,2,3]) { affected } }
mutation { bulkArchiveEntries(ids: [4,5]) { affected } }
mutation { bulkDeleteEntries(ids: [6,7]) { affected } }
mutation { bulkSetPendingReviewEntries(ids: [8,9]) { affected } }
mutation { createContentType(name: "blog", schema: "[{\"name\":\"title\",\"type\":\"text\"}]") { id name } }
mutation { updateContentType(id: 1, schema: "[{\"name\":\"title\",\"type\":\"text\"},{\"name\":\"body\",\"type\":\"richtext\"}]") { id } }
mutation { deleteContentType(id: 1) { id name } }
mutation { createWebhook(name: "Deploy", url: "https://example.com/hook", events: ["entry.created"]) { id } }
mutation { toggleWebhook(id: 1, enabled: false) { id enabled } }
mutation { pingWebhook(id: 1) { message } }
mutation { deleteWebhook(id: 1) { message } }
Add Authorization: Bearer YOUR_JWT_TOKEN header for authenticated mutations. Public queries (entries, contentTypes) work without auth but return published entries only.
A project made with create-nodepress-app starts on a release and records it in nodepress.json. To move it to a newer release, commit your work, then:
npx create-nodepress-app upgrade --dry-run # what would change (default target: the latest release)
npx create-nodepress-app upgrade # or: upgrade 1.9.0
# fix any conflicts it lists, then:
npm install # workspaces — backend + frontend
cd backend && npx prisma migrate deploy && cd ..
# restart, check the admin, commit
The upgrade applies the changes between your release and the new one to your code, keeping your own edits; where you and NodePress changed the same lines it leaves conflict markers. It skips files your project doesn't have (tests, the CLI, files you deleted) and lists them. Your project's root package.json is its own, so when NodePress's changes, the command shows how to compare them. Back up the database before migrate deploy. Prisma applies only the new migrations, so skipping releases is fine, e.g. 1.7.0 straight to 1.9.2.
Projects created before nodepress.json existed say once which release they started from: npx create-nodepress-app upgrade --from 1.7.0 (backend/package.json has the version). If you started from an unreleased main, use the release before it; the extra changes show up as conflicts you can resolve.
See CHANGELOG.md for what changed in each version, including upgrade notes for major versions.
| Layer | Technology |
|---|---|
| Backend | NestJS · TypeScript · Prisma ORM |
| Database | PostgreSQL 16 · PgBouncer |
| Cache | Redis (ioredis) |
| Auth | JWT · Passport · bcryptjs |
| API | REST · Apollo GraphQL · Swagger |
| Real-time | Socket.io (Redis adapter for multi-instance) |
| Media | Multer · Sharp (WebP) · S3 compatible · R2 · ImageKit |
| Frontend | Next.js 14 App Router · TypeScript · Tailwind CSS |
| Proxy | Nginx (gzip · rate limiting · WebSocket · SSL) |
| Monitoring | Prometheus · Grafana · pino structured logging |
| Testing | Jest · Supertest · 173 e2e tests |
"Cannot connect to the server" on login
The backend isn't running. In the backend/ folder run npm run start:dev, then check http://localhost:3001/api/health (or http://localhost:3000/api/health depending on your PORT config).
AggregateError [ECONNREFUSED] ... port 3001 (or port 3000), or the login page just fails
PostgreSQL isn't running, so the backend exited during startup while the frontend kept going. The error names the frontend's proxy port and never mentions the database, which makes it look like a broken login page. npm run dev checks for this before starting and tells you which command to run; set SKIP_DB_CHECK=1 to bypass it.
PostgreSQL doesn't start automatically after a reboot (macOS Ventura or newer)
macOS Background Task Management blocks launch daemons independently of launchctl, so the daemon can be enabled, have a valid plist and RunAtLoad, and still never start at boot. launchctl bootstrap appears to fix it because a manual start isn't gated — but the next reboot leaves the database down again.
Confirm it with:
log show --last 1h --predicate 'eventMessage CONTAINS "postgresql"' | grep disposition
disposition=[enabled, disallowed, ...] means Background Task Management is blocking it. launchctl cannot override this — fix it in System Settings → General → Login Items & Extensions → Allow in the Background, and enable the postgres entry (it points at /Library/LaunchDaemons/postgresql-*.plist). Then reboot and confirm with lsof -nP -iTCP:5432 -sTCP:LISTEN.
Login fails with correct credentials
You may have hit the rate limit (10 attempts/min in production). Restart the backend to clear it. If the problem persists, check http://localhost:3001/api/auth/setup-status — if required: true, setup wasn't completed.
"Your session expired" after signing in Normal behaviour after 30 days of inactivity (refresh token lifetime). Just sign in again — no data is lost.
Forgot password — no email received
In development: the reset link appears directly on the forgot-password page — no SMTP needed.
In production: configure SMTP_HOST, SMTP_USER, SMTP_PASS, and SMTP_FROM in backend/.env using one of the providers above.
npx prisma migrate dev fails with auth error
The password in DATABASE_URL doesn't match your PostgreSQL installation. Update backend/.env:
DATABASE_URL="postgresql://postgres:YOUR_POSTGRES_PASSWORD@localhost:5432/YOUR_NODEPRESS_DATABASE"
Admin panel shows blank page or 401
The frontend can't reach the backend. Confirm npm run start:dev is running in backend/ and that CORS_ORIGIN=http://localhost:5173 is set in backend/.env.
Images not loading after deploy
Set APP_URL in backend/.env to your backend's public URL (e.g. https://yourdomain.com). Without it, uploaded file URLs point to localhost.
Sitemap shows localhost URLs
Set SITE_URL in backend/.env to your frontend's public URL (e.g. https://yourdomain.com).
Port 3000 or 5173 already in use
Change PORT=3001 in backend/.env and update CORS_ORIGIN and BACKEND_URL accordingly.
Real-time updates not working in production
Ensure your nginx config includes the /api/realtime WebSocket location block (included by default in nginx/proxy.conf). Check that port 80/443 is open and nginx is running.
GraphQL Playground shows a blank page
Browser cached an old response with stale security headers. Open an Incognito window (works immediately) or press Ctrl+Shift+Delete → clear Cached images and files → refresh. This only happens once after a server restart where headers changed.
NodePress is open source and contributions are welcome.
/docs in your running admin panel1-click deploy at nodepress.buildwithkode.com — $?/mo per project. No Docker, no server management, automated backups included.
Copyright © 2026-present BuildWithKode.
NodePress is open source software licensed under the MIT License. Free to use, modify, and distribute — forever.
| Define a field set once (SEO, address, CTA) and use it in any content type |
| Two-way relations | Reverse fields list what links to an entry; filter by a related entry's fields |
| Content releases | Publish, unpublish or archive many entries together, now or on a schedule |
| Live preview | Edit an entry and watch your site update beside it; click an element to jump to its field |
| Redirects | 301/302/410 manager with CSV import, plus automatic redirects when a slug changes |
| Audit log | Every admin action is logged with user, IP, resource, and timestamp |
| ✅ Hooks |
| ✅ Hooks |
| ✅ Flows |
| Real-time WebSocket | ✅ Built-in | ❌ Paid | ❌ | ❌ | ✅ |
| Audit log | ✅ Built-in | ❌ Paid | 🟡 Plugin | ✅ | ✅ |
| Automated backups | ✅ Built-in | ❌ | 🟡 Plugin | ❌ | ❌ |
| Version history | ✅ | ✅ Paid | ✅ | ✅ | ✅ |
| Content releases | ✅ Free | ✅ Paid | ❌ | ❌ | ❌ |
| Review workflow stages | ✅ Free | ✅ Paid | 🟡 Plugin | 🟡 Build it | ✅ Flows |
| Single sign-on (OIDC) | ✅ Free | ✅ Paid | 🟡 Plugin | 🟡 Build it | ✅ |
| Custom roles | ✅ | ✅ | ✅ | ✅ Access functions | ✅ |
| TypeScript end-to-end | ✅ | ✅ | N/A | ✅ | ✅ |