The headless CMS that
doesn't get in your way.

Define your schema. Fill it with content. Get a REST API, GraphQL API, and real-time WebSocket events — in under 10 minutes. Self-hosted. MIT licensed. Yours forever.

Terminal
GET/api/blog/hello-world
200 OK
{
  "id": 1,
  "slug": "hello-world",
  "status": "published",
  "locale": "en",
  "data": {
    "title": "Hello World",
    "body": "<p>My first post.</p>",
    "author": "Karthik"
  },
  "createdAt": "2026-04-20T10:00:00Z"
}
0
E2E Tests
Passing in CI
MIT License
Open Source
Zero Docker
in Development
< 10 min
to first API

From zero to API in three steps

No config files. No plugins to install. No paid tier to unlock basic features.

01

Define your schema

Go to Content Types → New. Give it a name like blog and add fields: title (text), body (richtext), author (text). NodePress normalizes names to snake_case automatically.

02

Add your content

Go to Entries → blog → New Entry. Fill in the fields. A URL slug is auto-generated from the title. Set status to Published when ready.

03

Consume the API

Your content is immediately available at your API endpoints or via GraphQL:

# REST
GET /api/blog
GET /api/blog/hello-world
# GraphQL
query {
entries(contentTypeId: 1) {
data { slug data }
}
}

Everything you need. Nothing you don't.

Built by a developer who got frustrated wiring together five different services.

10 mutations

Dual API out of the box

Every content type gets instant REST endpoints AND a full GraphQL API with queries and mutations. No extra configuration. Swagger UI at /api/docs. Apollo Sandbox at /graphql.

6 events

Live content events

Socket.io gateway broadcasts entry:created, entry:updated, media:uploaded and more. Subscribe to specific content type rooms. Redis adapter for multi-instance sync. Authenticated — JWT or API key required.

No plugin needed

Built-in form builder

Build forms in the UI, embed them anywhere. Submissions trigger email or webhook actions. Three-layer spam protection: rate limiting, honeypot field, and Captcha (Turnstile / hCaptcha / reCAPTCHA).

3-level deep populate

i18n and content relations

Every entry has a locale field. ?populate=author,author.company resolves relation chains up to 3 levels deep in one batched DB query. ?fields=title,body for projection. ?search=keyword with PostgreSQL GIN full-text index.

Up to 50 versions

Never lose content

Every entry save creates a version snapshot. Restore any previous state in one click. Schedule publishing with publishAt — a cron job auto-publishes on the minute. PostgreSQL advisory locks prevent duplicate runs at scale.

9 services

Deploy in one command

docker-compose.prod.yml starts Postgres + PgBouncer + Redis + NestJS + Next.js + Nginx + Prometheus + Grafana + automated daily backups. Memory limits, health checks, and graceful shutdown all configured.

Two APIs. One codebase. Your choice.

Use REST for simplicity. Use GraphQL when you need to fetch multiple resources in one round-trip.

REST API
# List published blog posts
GET /api/blog?locale=en&page=1&limit=10
# Single post
GET /api/blog/hello-world
# With relations populated
GET /api/blog/hello-world?populate=author,tags
# Create (with API key)
POST /api/blog
X-API-Key: np_your_key_here
Content-Type: application/json
{
"slug": "new-post",
"data": { "title": "New Post" }
}
GraphQL
# List posts — query exactly what you need
query BlogPosts {
entries(contentTypeId: 1, status: "published") {
total
data {
id
slug
data
createdAt
}
}
}
# Create (requires JWT or API key)
mutation CreatePost {
createEntry(
contentTypeId: 1
slug: "new-post"
locale: "en"
status: "draft"
data: "{\"title\":\"New Post\"}"
) {
id slug status
}
}

Apollo Sandbox available at /graphql in all environments. Get your Bearer token from POST /api/auth/login.

Your frontend updates itself.

No polling. No refresh. Content changes in NodePress — your UI updates immediately.

socket-client.js
import { io } from 'socket.io-client';
const socket = io('https://your-api.com', {
path: '/api/realtime',
auth: { token: 'Bearer eyJhbGci...' },
});
// Subscribe to a content type room
socket.emit('subscribe', { contentType: 'blog' });
// React to live changes
socket.on('entry:created', ({ slug, contentType }) => {
console.log(`New ${contentType}: ${slug}`);
refreshFeed();
});
socket.on('entry:updated', ({ slug, status }) => {
if (status === 'published') updateCard(slug);
});
Live events
entry:createdblog / hello-worldjust now
entry:updatedblog / my-second-post2s ago
media:uploaded/uploads/hero.webp5s ago
6 events
created, updated, deleted, restored + media
Room-based
subscribe to ct:blog for just that type
Redis-ready
multi-instance sync via redis-adapter

Why NodePress instead of the others?

Honest comparison. No marketing spin.

FeatureNodePressYouStrapi v5ContentfulSanity
Self-hosted
Open source (MIT)
Setup time~10 min~30 min5 min20 min
Docker required in devNoYesN/AN/A
REST API
Full GraphQL mutations
Real-time WebSocketFreePaid
Multi-locale (i18n)FreePaid
Form builderBuilt-in
Scheduled publishingFreePaid
Webhooks with retryBasic
Audit logFreePaid
Preview tokens
E2E test coverage173 testsPartialN/AN/A
Production Docker stackCompletePartialN/AN/A

Data current as of April 2026. We aim to be fair — if something's wrong, open an issue.

Built on technology you already trust.

Backend
NestJSTypeScriptPostgreSQLPrismaRedisSocket.ioApollo GraphQLPassport JWTSharpPrometheus
Frontend
Next.js 14Tailwind CSSshadcn/uiTipTapFramer MotionAxios
Infrastructure
DockerNginxPgBouncerGrafanapino

TypeScript end-to-end. Backend and frontend share zero runtime type coupling — both compile clean independently.

Simple architecture. Clean separation.

Every layer has a single responsibility. Easy to understand, easy to extend.

Browser
Frontend App
Mobile / CLI
RESTGraphQLWebSocket
Next.js Proxy (/api/*, /graphql)
NestJS Backend
PostgreSQL
via PgBouncer
Redis
cache + Socket.io adapter
/uploads
local or S3/R2

In production, Nginx sits in front of everything. /api/realtime gets dedicated WebSocket headers. /api/metrics is blocked from the internet and scraped by Prometheus internally.

Simple pricing. As in, free.

Self-hostedAvailable now
$0
forever
  • Everything — no feature gates
  • Unlimited content types
  • Unlimited entries
  • Unlimited users
  • REST + GraphQL + WebSocket
  • Forms, webhooks, audit log
  • Full production Docker stack
  • MIT license — fork it, modify it, ship it
Get started

Requires a server. ~$5/month on DigitalOcean or Hetzner.

CloudComing soon
$?
/month
  • Everything in self-hosted
  • One-click deploy
  • Managed Postgres + Redis + backups
  • Custom domain
  • Email support

We'll email you when it's ready.

Ready to own your content?

Set up in 10 minutes. No credit card. No vendor lock-in.

MIT Licensed173 E2E TestsTypeScriptSelf-hosted