Example: REST API

A complete CRUD REST API with authentication, validation, and RBAC.

Project Structure

my-api/
├── app.js
├── package.json
└── modules/
    ├── auth/
    │   ├── module.js
    │   ├── auth.controller.js
    │   ├── auth.service.js
    │   └── auth.dto.js
    └── products/
        ├── module.js
        ├── products.controller.js
        ├── products.service.js
        └── products.dto.js

app.js

import { createApp, defineAuth } from 'SpaceNode'
import jwt from 'jsonwebtoken'

const SECRET = process.env.JWT_SECRET || 'org-secret-key'

defineAuth(async (token) => {
  try {
    return jwt.verify(token, SECRET)
  } catch {
    return null
  }
})

const app = await createApp({
  pipe: ['cors', 'logger'],
  openapi: { title: 'Products API', version: '1.0.0' },
})

app.setRoute('GET', '/health', ({ send }) => send({ status: 'ok' }))
app.listen(3000)

modules/auth/module.js

export default {
  name: 'auth',
  prefix: '/auth',
  routes: [
    ['POST', '/login',    'login',    ['rateLimit:10', 'dto:loginDto']],
    ['POST', '/register', 'register', ['dto:registerDto']],
    ['GET',  '/me',       'me',       ['auth']],
  ],
}

modules/auth/auth.dto.js

import { dto } from 'SpaceNode'

export const loginDto = dto({
  email:    ['string', 'required', 'email'],
  password: ['string', 'required', 'min:6'],
})

export const registerDto = dto({
  name:     ['string', 'required', 'min:2', 'max:50'],
  email:    ['string', 'required', 'email'],
  password: ['string', 'required', 'min:6'],
  role:     ['string', 'enum:customer,seller', 'default:customer'],
})

modules/auth/auth.controller.js

export async function login(request, services) {
  const { body, send, check, cookie } = request
  const { authService } = services

  const result = await authService.login(body.email, body.password)
  check(result.success, result.status, result.error)
  cookie('token', result.data.token, { httpOnly: true, maxAge: 86400 })
  send(result.data)
}

export async function register(request, services) {
  const { body, send, guard, emit } = request
  const { authService } = services

  const exists = await authService.findByEmail(body.email)
  guard(exists, 409, 'Email already registered')
  const user = await authService.register(body)
  await emit('auth:registered', { userId: user.id })
  send(201, user)
}

export function me({ user, send }) {
  send(user)
}

modules/products/module.js

export default {
  name: 'products',
  prefix: '/products',
  routes: [
    ['GET',    '/',    'list'],
    ['GET',    '/:id', 'getById'],
    ['POST',   '/',    'create',  ['auth', 'role:seller,admin', 'dto:createProductDto']],
    ['PUT',    '/:id', 'update',  ['auth']],
    ['DELETE', '/:id', 'remove',  ['auth', 'role:admin']],
  ],
}

modules/products/products.controller.js

export async function list(request, services) {
  const { query, send } = request
  const { productService } = services

  const page = Number(query.page) || 1
  const limit = Number(query.limit) || 20
  const products = await productService.list({ page, limit, q: query.q })
  send(products)
}

export async function getById(request, services) {
  const { params, send, check } = request
  const { productService } = services

  const product = await productService.findById(params.id)
  check(product, 404, 'Product not found')
  send(product)
}

export async function create(request, services) {
  const { body, user, send, emit } = request
  const { productService } = services

  const product = await productService.create({ ...body, sellerId: user.id })
  await emit('product:created', { id: product.id })
  send(201, product)
}

export async function update(request, services) {
  const { params, body, user, send, check } = request
  const { productService } = services

  const product = await productService.findById(params.id)
  check(product, 404, 'Product not found')
  check(product.sellerId === user.id || user.role === 'admin', 403, 'Forbidden')
  const updated = await productService.update(params.id, body)
  send(updated)
}

export async function remove(request, services) {
  const { params, send, check } = request
  const { productService } = services

  const product = await productService.findById(params.id)
  check(product, 404, 'Product not found')
  await productService.delete(params.id)
  send(204)
}
Run itnode app.js and you have a fully working REST API with auth, RBAC, validation, CORS, logging, and auto-generated OpenAPI docs.