@rhino-ai/orchard9-expresstsx
v0.3.0
Published
A zero-configuration, production-ready server framework for TypeScript services
Downloads
5
Maintainers
Readme
🌲 Orchard9 Express TSX
A zero-configuration, production-ready server framework for TypeScript services.
Features
- Zero-config startup - Just provide routes and go!
- Batteries included - Database, Redis, WebSockets, Config, Logging - all ready to use
- Production ready - Kubernetes health checks, metrics, and security best practices built-in
- Fully typed - Complete TypeScript support with intelligent autocomplete
- Extensible - Easily add custom middleware, routes, and services
Installation
npm install @rhino-ai/orchard9-expresstsxDocker Installation
You can also use Docker to run the framework:
# Clone the repository
# Start development environment
npm run docker:dev
# Or build and start
npm run docker:dev:buildQuick Start
Create an app.ts file:
import { createApp } from '@rhino-ai/orchard9-expresstsx';
import userRoutes from './routes/users';
// That's it - everything is automatically configured from your config files!
const app = createApp({
routes: [
{
path: '/api/users',
router: userRoutes
}
]
});
app.start().then(() => {
app.logger.info('🚀 Application started successfully!');
});Create a route in routes/users.ts:
import { Router, Request, Response } from '@rhino-ai/orchard9-expresstsx';
const router = Router();
router.get('/', async (req: Request, res: Response) => {
const users = await req.db('users').select('*');
return res.json(users);
});
export default router;Create a default configuration in config/default.yaml:
app:
name: my-awesome-service
port: 3000
database:
host: localhost
port: 5432
user: postgres
password: postgres
database: my_app
redis:
host: localhost
port: 6379Start your application:
ts-node app.tsProject Structure
We recommend the following project structure:
my-service/
├── config/
│ ├── default.yaml # Default configuration
│ ├── development.yaml # Development overrides
│ └── production.yaml # Production overrides
├── src/
│ ├── routes/ # Express route definitions
│ │ └── users.ts
│ ├── services/ # Business logic
│ │ └── user-service.ts
│ ├── websocket/ # WebSocket handlers
│ │ └── chat-handler.ts
│ ├── types/ # TypeScript type definitions
│ │ └── index.ts
│ └── app.ts # Main application entry point
├── package.json
└── tsconfig.jsonCore Concepts
The App Instance
The app instance provides unified access to all underlying services:
const app = createApp();
// Access database directly
const users = await app.db('users').select('*');
// Access Redis
await app.redis.set('key', 'value');
// Access logger
app.logger.info('Something happened');
// Access WebSocket server
app.websocket.broadcast('event', { data: 'message' });
// Access config
const dbConfig = app.config.get('database');Request Extensions
Every request is automatically extended with core services:
router.get('/profile', async (req, res) => {
// Request-scoped logger with request ID
req.logger.info('Fetching profile');
// Database access
const user = await req.db('users').where('id', req.params.id).first();
// Validation helpers
const { valid, data, errors } = req.validate(user, userSchema);
return res.json(user);
});WebSockets
WebSocket support is built-in and easy to use:
import { createApp } from '@rhino-ai/orchard9-expresstsx';
const app = createApp({
websocket: {
handlers: [
{
event: 'chat.message',
handler: (message, socket, metadata) => {
// Handle the message
app.websocket.broadcastToRoom('chat', 'chat.message', message.payload);
}
}
]
}
});
// Client connection handler
app.websocket.onConnection((socket, req, metadata) => {
app.logger.info(`Client connected: ${metadata.clientId}`);
app.websocket.joinRoom('chat', metadata.clientId, socket, metadata);
});Configuration
Configuration is automatically loaded from the config/ directory:
// Access configuration
const dbConfig = app.config.get('database');
const appName = app.config.get('app.name');
// Watch for configuration changes
app.config.onChange('database', (newValue, oldValue) => {
app.logger.info('Database configuration changed');
});Protobuf Support
Protocol Buffers are fully supported:
import { createApp } from '@rhino-ai/orchard9-expresstsx';
const app = createApp({
protobuf: {
schemaPath: './proto',
handlers: [
{
messageType: 'UserMessage',
handler: async (data, context) => {
// Handle the protobuf message
return { status: 'success' };
}
}
]
}
});
// Later, serialize/deserialize messages
const userProto = await app.protobuf.loadSchema('user.proto');
const userType = app.protobuf.getType(userProto, 'User');
const buffer = app.protobuf.serialize(userType, { id: 1, name: 'John' });API Reference
createApp(options?)
Creates and configures the application.
const app = createApp({
// App configuration
name: 'my-service',
port: 3000,
// Route configuration
routes: [
{ path: '/api/users', router: userRoutes },
{ path: '/api/products', router: productRoutes }
],
// Middleware configuration
middleware: {
before: [customMiddleware1, customMiddleware2],
after: [customMiddleware3]
},
// WebSocket configuration
websocket: {
path: '/ws',
handlers: [{
event: 'message',
handler: messageHandler
}]
},
// Health check configuration
health: {
path: '/health',
kubernetes: true,
checks: [customHealthCheck]
},
// Protobuf configuration
protobuf: {
schemaPath: './proto',
handlers: [{
messageType: 'UserMessage',
handler: userMessageHandler
}]
}
});App Methods
app.start()- Starts the applicationapp.stop()- Gracefully stops the applicationapp.db- Access the database (Knex instance)app.redis- Access the Redis clientapp.logger- Access the loggerapp.config- Access the configurationapp.websocket- Access the WebSocket serverapp.events- Access the event busapp.protobuf- Access the Protocol Buffers utilitiesapp.validation- Access the validation utilities
Examples
Complete Example with All Features
import { createApp, Router } from '@rhino-ai/orchard9-expresstsx';
import { z } from 'zod';
// Define routes
const userRouter = Router();
userRouter.get('/', async (req, res) => {
const users = await req.db('users').select('*');
return res.json(users);
});
userRouter.post('/', async (req, res) => {
// Validation schema
const userSchema = z.object({
name: z.string().min(2),
email: z.string().email()
});
// Validate request body
const { valid, data, errors } = req.validate(req.body, userSchema);
if (!valid) {
return res.status(400).json({ errors });
}
// Insert into database with transaction
const userId = await req.transaction(async (trx) => {
const [id] = await trx('users').insert(data).returning('id');
await trx('user_logs').insert({ user_id: id, action: 'created' });
return id;
});
// Publish event
req.events.publish('user.created', { id: userId, name: data.name });
return res.status(201).json({ id: userId });
});
// WebSocket handlers
const chatHandler = (message, socket, metadata) => {
req.logger.info({ userId: metadata.user?.id }, 'Chat message received');
req.websocket.broadcastToRoom('chat', 'message', message.payload);
};
// Create the application
const app = createApp({
name: 'example-service',
port: 3000,
// Mount routes
routes: [
{ path: '/api/users', router: userRouter }
],
// Configure WebSockets
websocket: {
path: '/ws',
handlers: [
{ event: 'chat.message', handler: chatHandler }
]
}
});
// Start the application
app.start().then(() => {
app.logger.info(`🚀 ${app.config.get('app.name')} started on port ${app.config.get('app.port')}`);
});Testing
For detailed information about testing, please see TESTING.md.
Docker Support
Orchard9 Express TSX comes with full Docker support for both development and production environments.
Development Environment
# Start development environment
npm run docker:dev
# Build and start development environment
npm run docker:dev:build
# Run all tests (uses Docker)
npm test
# Stop containers
npm run docker:down
# Remove containers and volumes
npm run docker:cleanThe development environment includes:
- Node.js application container with hot reloading
- PostgreSQL database
- Redis cache
- pgAdmin for database management (http://localhost:5050)
- Redis Commander for Redis inspection (http://localhost:8081)
Production Environment
# Build and start production environment
npm run docker:prod
# Build production Docker image
npm run docker:buildThe production environment:
- Uses multi-stage builds for smaller images
- Includes health checks for all services
- Configures resource limits for containers
- Supports environment variable configuration
- Implements security best practices
Environment Variables
You can customize the Docker environment by creating a .env file based on the provided .env.example:
# Copy example environment file
cp .env.example .env
# Edit environment variables
nano .env
# Start with custom environment
npm run docker:devLicense
MIT
