@zerva/core
v0.81.1
Published
π± Simple event driven server
Readme
π± @zerva/core
Event-driven server foundation for building modular web services
The core of the Zerva ecosystem - a minimal, powerful foundation for creating event-driven web services with a clean module system and lifecycle management.
β¨ Features
- π― Event-driven architecture - Clean separation of concerns
- π§ Modular design - Composable, reusable modules
- β‘ Lightweight core - Minimal footprint, maximum flexibility
- π Lifecycle management - Structured startup and shutdown
- πͺ Dependency system - Automatic module loading and ordering
- π‘ Global context - Simplified event bus with implicit context
- π οΈ TypeScript first - Full type safety and IDE support
- π¨ Clean APIs - Intuitive, consistent module interfaces
π¦ Installation
npm install @zerva/core
# or
pnpm add @zerva/core
# or
yarn add @zerva/coreπ Quick Start
Basic Server Setup
// main.ts
import { serve } from '@zerva/core'
import { useHttp } from '@zerva/http'
// Setup HTTP server
useHttp({
port: 8080,
showServerInfo: true
})
// Start the server
serve()Package.json Setup
{
"name": "my-zerva-app",
"type": "module",
"version": "1.0.0",
"scripts": {
"dev": "zerva",
"build": "zerva build",
"start": "node dist/main.cjs"
},
"devDependencies": {
"@zerva/bin": "^0.73.0",
"@zerva/core": "^0.73.0",
"@zerva/http": "^0.73.0"
}
}Run Your Server
npm run dev
# Server starts with hot-reload
# π Zerva: http://localhost:8080 (π§ DEVELOPMENT)π Core Concepts
Event-Driven Architecture
Zerva uses a global event system where modules communicate through events:
import { on, emit } from '@zerva/core'
// Listen to events
on('userRegistered', async (user) => {
console.log(`Welcome ${user.name}!`)
await sendWelcomeEmail(user)
})
// Emit events
await emit('userRegistered', { name: 'Alice', email: '[email protected]' })Module System
Modules are functions that hook into the event system:
function useCounter() {
let count = 0
// Listen to HTTP module initialization
on('httpInit', ({ onGET }) => {
onGET('/counter', () => `Count: ${++count}`)
onGET('/reset', () => {
count = 0
return 'Reset!'
})
})
return {
getCount: () => count
}
}
// Use the module
const counter = useCounter()π Lifecycle Management
Server Lifecycle Events
The serve() function orchestrates the application lifecycle:
import { on, serve } from '@zerva/core'
// Lifecycle events (in order)
on('serveInit', async () => {
console.log('π§ Initializing modules...')
// Setup configuration, database connections, etc.
})
on('serveStart', async () => {
console.log('π Starting services...')
// Start HTTP servers, connect to external services, etc.
})
on('serveStop', async () => {
console.log('π Stopping services...')
// Close connections, cleanup resources
})
on('serveDispose', async () => {
console.log('π§Ή Final cleanup...')
// Release final resources
})
serve()Graceful Shutdown
Zerva handles graceful shutdown automatically:
// Automatic handling of SIGTERM, SIGINT signals
// Calls serveStop() and serveDispose() in order
// Your cleanup code runs before process exitπ Module Dependencies
Declaring Dependencies
Ensure modules load in the correct order:
import { register } from '@zerva/core'
function useDatabase() {
register('database', []) // No dependencies
on('serveInit', async () => {
// Initialize database connection
})
}
function useUserService() {
register('userService', ['database', 'http']) // Depends on database and http
on('serveInit', async () => {
// Setup user routes and database queries
})
}Module Registration
function useAuth() {
register('auth', ['http', 'database'])
let sessions = new Map()
on('httpInit', ({ onPOST, addMiddleware }) => {
// Add auth middleware
addMiddleware((req, res, next) => {
// Check authentication
next()
})
onPOST('/login', async (req) => {
// Handle login
return { token: 'abc123' }
})
})
return {
validateSession: (token) => sessions.has(token)
}
}πͺ Advanced Usage
Custom Module with Events
function useNotifications() {
register('notifications')
const subscribers = new Set()
// Listen to app events
on('userRegistered', async (user) => {
await emit('notificationSend', {
to: user.email,
subject: 'Welcome!',
body: 'Thanks for joining!'
})
})
on('notificationSend', async (notification) => {
console.log(`π§ Sending: ${notification.subject}`)
// Send email, push notification, etc.
})
return {
subscribe: (callback) => subscribers.add(callback),
unsubscribe: (callback) => subscribers.delete(callback)
}
}Multiple Contexts
For advanced use cases with isolated contexts:
import { createContext, withContext } from '@zerva/core'
const mainContext = createContext()
const workerContext = createContext()
// Main application
withContext(mainContext, () => {
useHttp({ port: 3000 })
serve()
})
// Worker process
withContext(workerContext, () => {
useBackgroundJobs()
serve()
})Conditional Module Loading
function setupEnvironment() {
if (process.env.NODE_ENV === 'development') {
useDevelopmentTools()
useHotReload()
}
if (process.env.ENABLE_METRICS === 'true') {
useMetrics()
useHealthChecks()
}
}
function useDevelopmentTools() {
on('httpInit', ({ onGET }) => {
onGET('/dev/reload', () => {
process.exit(0) // Will be restarted by zerva-bin
})
})
}π§ͺ Testing
Testing Modules
// test/counter.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { createContext, withContext, emit, on } from '@zerva/core'
describe('Counter Module', () => {
let context
beforeEach(() => {
context = createContext()
})
it('should increment counter', async () => {
let counter = 0
await withContext(context, async () => {
on('increment', () => counter++)
await emit('increment')
expect(counter).toBe(1)
})
})
})Integration Testing
// test/integration.test.ts
import request from 'supertest'
import { serve } from '@zerva/core'
import { useHttp } from '@zerva/http'
describe('Server Integration', () => {
it('should start server and respond', async () => {
const { app } = useHttp({ port: 0 }) // Random port
on('httpInit', ({ onGET }) => {
onGET('/health', () => ({ status: 'ok' }))
})
await serve()
const response = await request(app)
.get('/health')
.expect(200)
expect(response.body.status).toBe('ok')
})
})ποΈ Project Structure
Recommended Structure
my-zerva-app/
βββ src/
β βββ main.ts # Entry point
β βββ modules/ # Custom modules
β β βββ auth.ts # Authentication module
β β βββ users.ts # User management
β β βββ notifications.ts # Notification system
β βββ routes/ # HTTP route handlers
β βββ services/ # Business logic
β βββ utils/ # Shared utilities
β βββ types/ # TypeScript types
βββ test/ # Test files
βββ dist/ # Built files (auto-generated)
βββ package.json
βββ tsconfig.jsonModule Organization
// src/modules/users.ts
import { register, on } from '@zerva/core'
export function useUsers() {
register('users', ['database', 'http'])
const users = new Map()
on('httpInit', ({ onGET, onPOST }) => {
onGET('/users', () => Array.from(users.values()))
onPOST('/users', async (req) => {
const user = { id: Date.now(), ...req.body }
users.set(user.id, user)
await emit('userCreated', user)
return user
})
})
return {
getUser: (id) => users.get(id),
getAllUsers: () => Array.from(users.values())
}
}π§ Conditional Compilation
Use build-time defines for environment-specific code:
// This code only runs in development
if (ZERVA_DEVELOPMENT) {
console.log('Development mode - enabling debug features')
useDevelopmentTools()
}
// This code only runs in production
if (ZERVA_PRODUCTION) {
useProductionOptimizations()
useMonitoring()
}
// Alternative syntax
if (process.env.ZERVA_DEVELOPMENT === 'true') {
// Development-only code
}Available Defines
ZERVA_DEVELOPMENT:truein development modeZERVA_PRODUCTION:truein production builds
π Available Modules
Core ecosystem modules you can use with @zerva/core:
@zerva/http- HTTP server with Express@zerva/websocket- WebSocket support@zerva/vite- Vite development server integration@zerva/sqlite- SQLite database@zerva/mqtt- MQTT client/server@zerva/email- Email sending@zerva/rpc- RPC communication@zerva/openapi- OpenAPI/Swagger docs
π³ Docker Integration
Zerva works perfectly with Docker:
# Dockerfile
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci --only=production
# Copy built application
COPY dist/ ./dist/
# Expose port
EXPOSE 3000
# Start the server
CMD ["node", "dist/main.cjs"]# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- ZERVA_PRODUCTION=trueπ Examples
Check out the examples directory for complete applications:
- Minimal - Simplest possible setup
- Full-Stack - Complete app with frontend
- WebSocket Chat - Real-time communication
- RPC Service - Service-to-service communication
π€ Related Projects
zeed- Utility library and logging systemzeed-dom- Lightweight offline DOM for server-side rendering
π License
MIT License - see LICENSE file for details.
πββοΈ Support
- π Documentation
- π Bug Reports
- π¬ Discussions
- β€οΈ Sponsor
