v1.1.0 — Zero Dependencies

The Revolutionary
Node.js Framework

Auto-discovery modules, pipeline middleware, DI container, event bus, built-in SSR template engine. 2× faster than Express, on par with Fastify. Just 3 lines to launch.

import { createApp } from 'spacenode'

const app = await createApp()
app.listen(3000)
// That's it. Auto-discovers modules, routes, services.
0
Dependencies
≈ Fastify
Performance Level
Faster than Express
163 KB
Package Size
3
Lines to Launch

One Framework. Three Killer Modes.

REST API, full-stack SSR site, or static files — all in one zero-dependency framework.

REST API

Production API in Minutes

Create a folder inside modules/ with a module.js file — SpaceNode automatically discovers your routes, controllers, services, and DTOs. No manual wiring. Define your endpoints as arrays: method, path, handler, and optional guards. Controllers receive the request context as the first argument and auto-injected services as the second. Guards like auth, admin, and dto:product run before the handler — no middleware chains, no next() calls.

Project Structure

my-api/
├── index.js                        // entry point — createApp + global config
├── package.json
└── modules/                        // each folder = one module (auto-discovered)
    ├── auth/
    │   ├── module.js               // prefix: '/auth', routes: [...]
    │   ├── auth.controller.js      // register, login, logout handlers
    │   └── auth.service.js         // hashing, tokens, sessions
    ├── product/
    │   ├── module.js               // prefix: '/products', routes: [...]
    │   ├── product.controller.js   // list, getById, create, update, remove
    │   ├── product.service.js      // DB queries, business logic
    │   └── product.dto.js          // validation schemas for create/update
    ├── category/
    │   ├── module.js
    │   ├── category.controller.js
    │   └── category.service.js
    └── cart/
        ├── module.js
        ├── cart.controller.js
        └── cart.service.js

index.js — Entry Point

import { createApp, defineAuth, defineGuard } from 'spacenode'

// defineAuth — tell SpaceNode how to resolve a user from a token
defineAuth(async (token) => {
  const session = await Token.findOne({ token })
  if (!session) return null
  return User.findById(session.userId) // → request.user
})

// defineGuard — custom guards reusable across all modules
defineGuard('admin', () => (request) => {
  if (!request.user) request.error(401, 'Authorization required')
  if (request.user.role !== 'admin') request.error(403, 'Admin only')
})

// createApp — discovers all modules/ automatically
const app = await createApp({
  openapi: true,    // auto-generate OpenAPI 3.0 docs at /api-docs
  watch:   true,    // auto-restart on file changes (dev mode)
  debug:   true,    // verbose logging
})

app.listen(3000)

module.js — Route Declaration

// modules/product/module.js
// Each module exports: prefix, optional pipe, and routes array
// Routes: [METHOD, PATH, HANDLER_NAME, [GUARDS]]
export default {
  prefix: '/products',
  pipe: ['cors', 'logger'],          // run on every route in this module
  routes: [
    ['GET',    '',      'list'],                                 // GET /products
    ['GET',    '/:id',  'getById'],                              // GET /products/:id
    ['POST',   '',      'create',  ['auth', 'dto:product']], // auth + validation
    ['PUT',    '/:id',  'update',  ['auth', 'admin']],      // admin only
    ['DELETE', '/:id',  'remove',  ['auth', 'admin']],      // admin only
  ],
}
// 'list' → calls list() from product.controller.js
// 'auth' → built-in guard (uses defineAuth callback)
// 'admin' → custom guard (defined in index.js)
// 'dto:product' → validates body against product.dto.js

Controller — Request Handlers

// modules/product/product.controller.js
// 1st arg — request context (destructure what you need)
// 2nd arg — DI services (auto-injected from *.service.js)

export async function list({ query, send }, { productService }) {
  const { page = 1, limit = 20, search } = query
  const result = await productService.list({ page, limit, search })
  send(result) // → { items: [...], total: 42, pages: 3 }
}

export async function create({ body, send }, { productService }) {
  const product = await productService.create(body)
  send(201, product) // status 201 + JSON body
}

export async function remove({ params, send }, { productService }) {
  await productService.remove(params.id)
  send({ deleted: true })
}
// productService is auto-injected from product.service.js
// Services from OTHER modules also available (e.g. categoryService)
  • Auto-discovery — drop a folder into modules/, everything is wired automatically
  • DI container — services from all modules available as the 2nd handler argument
  • Guards — auth, roles, rate-limit, CORS, logging — just reference by name
  • DTO validation — array-based or plug in Zod/Joi/Yup
  • OpenAPI 3.0 — auto-generated from your routes and DTOs
  • Event bus — inter-module communication without imports
SSR Site

Full-Stack Server-Rendered Sites

Build complete web applications with server-side rendering — no React, no hydration, no client-side framework needed. SpaceNode includes an AOT-compiled template engine that supports layouts, partials, blocks, loops, conditionals, and pipe filters. Combined with cookie-based auth, CSRF protection, flash messages, and DI-powered controllers, you get a full-stack platform that replaces Next.js for server-rendered apps — without the complexity.

// index.js — enable SSR with one option
const app = await createApp({
  views:  './views',   // enables built-in template engine
  static: './public',  // serves CSS, images, JS
})

// simple page — one line
app.render('GET', '/', 'pages/home', { title: 'Welcome' })

// controller with auth, DI, and template rendering
export async function profile(req, { userService }) {
  const user = await userService.getById(req.user.id)
  await req.render('pages/profile', { user })
}

// views/pages/home.html — template syntax
// [# layout 'layouts/main' ]
// <h1>[= title ]</h1>
// [# each posts as post ]
//   <article>[= post.title ]</article>
// [/each]
// [# include 'partials/footer' ]
  • AOT-compiled templates — compiled once at startup, zero runtime parsing overhead
  • Layouts, partials, blocks, pipe filters — full template inheritance
  • Cookie auth, CSRF protection, flash messages — all wired via guards
  • Combine SSR pages with REST API endpoints in the same app
  • Zero frontend dependencies — pure server-rendered HTML
  • Auto-escaping prevents XSS — secure by default
Static Site / SPA

Serve Any Frontend — React, Vue, Vanilla

Use SpaceNode as a production-ready static file server for any frontend application. With SPA fallback enabled by default, client-side routing just works — no nginx configuration needed. Supports 30+ MIME types with proper headers, gzip/brotli compression, and built-in directory traversal protection. You can also add API routes alongside your static files, turning SpaceNode into a full-stack server with one process.

// index.js — serve your frontend app
import { createApp } from 'spacenode'

const app = await createApp({
  static: './public',  // serve files from ./public
  // spa: true         ← default — all routes fallback to index.html
  // spa: false         ← for multi-page static sites
})

// add API routes alongside your static files
app.setRoute('GET', '/api/status', ({ send }) => {
  send({ status: 'ok', uptime: process.uptime() })
})

app.listen(3000)
// public/index.html  → served at /
// public/style.css   → served at /style.css
// public/app.js      → served at /app.js
// /about, /dashboard  → SPA fallback to index.html
  • SPA fallback — client-side routing works out of the box (React Router, Vue Router, etc.)
  • Multi-page mode — set spa: false for traditional static sites
  • Mix static files with API routes in one server
  • Gzip/Brotli compression via built-in pipes
  • Directory traversal protection — secure by default
  • Watch mode for development — auto-restart on changes

Why SpaceNode?

Auto-Discovery

Drop a folder into modules/ — routes, controllers, services, DTOs are discovered automatically. Zero wiring.

Pipeline Middleware

No next(), no mutation soup. Each pipe is a pure function that returns or throws. Clean, composable, testable.

Dependency Injection

Services auto-registered from all modules into a DI container. Injected as second arg to every handler. No decorators needed.

Event Bus

Inter-module communication via events. Auth emits auth:login, Orders reacts. No module imports another module.

Built-in Guards

Auth, roles, rate limiting, CORS, logging — all built-in. Just reference by name: ['auth', 'role:admin'].

DTO Validation

Built-in array-based validation or plug in Zod/Joi/Yup. ['string', 'required', 'email'] — that's a full email validator.

Trie Router

Radix-trie based routing. O(path_length) lookup, not O(n_routes). Faster than regex-based Express routing.

Static File Serving

Built-in static serving with static: './public'. SPA fallback, clean URLs, 30+ MIME types, directory traversal protection.

Production Ready

Server timeouts, graceful shutdown, fatal error catching, auto-204, connection draining — all built-in.

WebSocket

Built-in WebSocket server (RFC 6455). Zero dependencies. app.ws('/chat', handler) — that's it.

Form & File Uploads

JSON, URL-encoded, and multipart/form-data parsed automatically. File uploads via request.files.

Inject (Testing)

Test routes without starting a server. app.inject({ method, url }) returns status, headers, body.

Compression & Security

Gzip/Brotli compression and security headers (HSTS, CSP, X-Frame-Options) as simple pipes.

OpenAPI Auto-Gen

Auto-generates OpenAPI 3.0 spec from your modules, routes, and DTOs. Served at /openapi.json.

Watch Mode

Built-in watch: true — auto-restarts on file changes. No nodemon needed. Zero overhead in production.

SSR Templates

Built-in AOT-compiled template engine. Layouts, includes, blocks, pipe filters, auto-escaping — zero dependencies. Full SSR out of the box.

Benchmark Results

3 rounds × 30s per framework • concurrency 50 • steady-state averages

baseline
Raw HTTP
~94%
SpaceNode
~94%
Fastify 5
~50%
Express 5

Relative to raw Node.js — proportions are hardware-independent

Battle-Tested Resilience

19 stress tests passed. Zero failures. Zero crashes.

1,000
Simultaneous requests handled
All returned 200 OK
0
Crashes from malformed data
Garbage TCP, broken JSON, 64KB headers
318,542
Requests in 30-second sustained load
10,618 req/s — 0 errors (0.00%)
200
Half-open connections survived
Rapid connect/disconnect cycling
Concurrent flood (200 / 500 / 1,000 simultaneous)
Malformed requests (garbage TCP, invalid methods, 8KB URLs)
Body limits enforced (413 on oversized, valid on normal)
Half-open & rapid connection cycling
30s sustained load — zero errors, stable memory
Error recovery — server alive after all attacks
Secret Weapon

Next.js Without The Pain

The market is tired of App Router complexity, Server Components config hell, and 10 configuration files just to render HTML with database data.

SpaceNode is a single-process full-stack framework. Backend and frontend are not separated by a wall of configs. Write a function, return HTML with data from your DB — done.

Next.js

  • next.config.js, tsconfig, postcss, tailwind…
  • App Router vs Pages Router confusion
  • Server Components + Client Components + "use server"
  • Hydration mismatch debugging
  • Separate API routes from pages

SpaceNode

  • One file: createApp({ views: './views' })
  • One concept: render(template, data)
  • One process: API + SSR + Static
  • Zero hydration — pure server HTML
  • Guards, DI, events — all built-in
Full auth page — that's all you write
// controller
export async function login(request, { authService }) {
  const { flash, cookie, redirect } = request
  const { email, password } = request.body
  const result = await authService.login(email, password)
  if (!result) {
    flash('error', 'Invalid credentials')
    return redirect('/auth/login')
  }
  cookie('token', result.token, { httpOnly: true })
  redirect('/profile')
}

Works With Everything

SpaceNode is pure Node.js. No magic, no lock-in. Use any npm package you already know.

Prisma / Mongoose

Import your ORM, register as a service — auto-injected into every handler.

Redis / ioredis

Session store, cache, pub/sub — just npm install and inject.

AWS S3 / Cloudflare R2

File uploads via request.files, then push to any storage via service.

JWT / bcrypt

Built-in guard system. Plug in any auth strategy: Bearer, Cookie, OAuth.

WebSocket

Built-in RFC 6455 server. app.ws('/chat', handler) — zero deps.

Zod / Joi / Yup

Plug any validation library into DTO layer, or use built-in array syntax.

Example: Prisma + Redis in one service
// modules/users/users.service.js — auto-discovered & auto-injected
import { PrismaClient } from '@prisma/client'
import Redis from 'ioredis'

const prisma = new PrismaClient()
const redis  = new Redis()

export const usersService = {
  async getById(id) {
    const cached = await redis.get(`user:${id}`)
    if (cached) return JSON.parse(cached)

    const user = await prisma.user.findUnique({ where: { id } })
    await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 300)
    return user
  },
}
// Now any controller gets it: (request, { usersService }) => ...

Ready to Launch?

Start building in minutes. Zero config, zero dependencies.

Frequently Asked Questions

What is SpaceNode?

SpaceNode is a zero-dependency Node.js framework for building REST APIs, server-rendered websites, and static sites. It features auto-discovery modules, a pipeline middleware system, a DI container, an event bus, and a built-in AOT-compiled SSR template engine — all without any npm dependencies.

How do I create a REST API in Node.js without Express?

With SpaceNode you can create a full REST API in 3 lines: import { createApp } from 'spacenode', const app = await createApp(), app.listen(3000). Then add modules in the modules/ folder — the framework auto-discovers routes, controllers, services, and DTOs without any manual wiring.

Does SpaceNode have any dependencies?

No. SpaceNode has zero production dependencies. The entire framework — HTTP server, router, body parser, template engine, WebSocket, DI container, event bus — is built from scratch using only the Node.js standard library. The package size is just 163 KB.

How fast is SpaceNode compared to Express and Fastify?

SpaceNode is approximately 2× faster than Express 5 and performs on par with Fastify 5. Benchmarks are run over 3 rounds × 30 seconds at concurrency 50, producing stable steady-state results. The proportions are hardware-independent.

Does SpaceNode support server-side rendering (SSR)?

Yes. SpaceNode includes a built-in AOT-compiled template engine. You write .html templates with [= variable] syntax, layouts, partials, block inheritance, and pipe filters. Templates are compiled into JavaScript functions at startup for near-zero render overhead. No React, no build step required.

How does auto-discovery work in SpaceNode?

When you call createApp(), SpaceNode scans the modules/ directory recursively. Each subfolder with a module.js file is registered as a module. The framework automatically loads routes, controllers (*.controller.js), services (*.service.js), and DTOs (*.dto.js) — no manual imports or registration needed.

Can I use SpaceNode with MongoDB, PostgreSQL, or other databases?

Yes. SpaceNode is database-agnostic. You can use Mongoose, Prisma, Sequelize, Drizzle, or any other Node.js database library. Just import your database client in a service file and it will be auto-injected into controllers via the built-in DI container.

Does SpaceNode support WebSocket?

Yes. SpaceNode has a built-in WebSocket implementation with fragment assembly, ping/pong heartbeat, backpressure control, and full DI integration. No additional packages like ws or socket.io are needed.

How do I add authentication to a SpaceNode app?

Use defineAuth() to register a global auth resolver that extracts and verifies tokens (JWT, session, etc.). Then add 'auth' to any route's guard list. The resolved user object is available as request.user in controllers. You can also create custom guards with defineGuard() for role-based access control.

Can SpaceNode serve static files and single-page applications?

Yes. Set static: './public' in createApp() to serve static files with automatic MIME type detection, ETag caching, 304 responses, and streaming for large files. Enable spa: true (default) for SPA fallback — all unknown routes serve index.html for client-side routing.

Is SpaceNode production-ready?

Yes. SpaceNode passes 19 stress tests with zero failures and zero crashes. It includes built-in security headers, CSRF protection, rate limiting, graceful shutdown (SIGTERM/SIGINT), gzip compression, and structured logging — all without external dependencies.

How does SpaceNode compare to NestJS?

NestJS uses decorators, TypeScript classes, and has 80+ dependencies. SpaceNode achieves similar architecture (modules, DI, guards, pipes) with zero dependencies, plain JavaScript functions, and a simpler folder-based convention. SpaceNode is significantly smaller and faster to start.

Does SpaceNode have built-in request validation?

Yes. Create a DTO file (e.g., product.dto.js) that declares expected fields and validation rules. Add 'dto:product' to a route's guard list and SpaceNode validates the request body automatically before your controller runs. Invalid requests receive a structured 400 error response.

Can I deploy SpaceNode on Vercel, Railway, or Docker?

Yes. SpaceNode runs anywhere Node.js runs. It supports Vercel (with a vercel.json config), Railway, Render, Fly.io, AWS, and Docker. The zero-dependency design means smaller Docker images and faster cold starts.

Does SpaceNode generate OpenAPI / Swagger documentation?

Yes. Pass openapi: { title, version } to createApp() and SpaceNode auto-generates a Swagger/OpenAPI specification from your module routes, DTOs, and guards. The spec is served at /api-docs by default.