- Features
- Quick Start
- Core Components
- Server Initialization
- Development
- Project Structure
- Configuration Options
- Best Practices
- License
- Contributing
- Database Integration (RWS DB)
- Manager & Build Configuration
RWS is a comprehensive Node.js framework built on top of NestJS that provides a robust foundation for building real-time web applications. It integrates WebSocket support, MongoDB/Prisma ORM, authentication, and routing in a cohesive package.
- Built on NestJS for robust server-side architecture
- WebSocket support for real-time communication
- MongoDB integration with Prisma ORM
- Automatic model schema generation
- Built-in authentication system
- Configurable routing system for HTTP and WebSocket endpoints
- Command-line interface for project management
- TypeScript support with decorators for clean code structure
- Install RWS Framework:
npm install @rws-framework/server
# or
yarn add @rws-framework/server- Initialize a new RWS project:
yarn rws-server init- Create a configuration file (e.g.,
src/config/config.ts):
import { IAppConfig } from "@rws-framework/server";
import dotenv from 'dotenv';
export default (): IAppConfig => {
dotenv.config();
return {
features: {
ws_enabled: true,
routing_enabled: true,
ssl: false
},
db_type: 'mongodb',
db_url: process.env.MONGO_URL,
db_name: process.env.DB_NAME, // Use db_name for consistency
port: parseInt(process.env.APP_PORT),
domain: process.env.APP_DOMAIN,
user_models: [], // Your models here
controller_list: [], // Your controllers here
ws_routes: {}, // Your WebSocket routes here
http_routes: [], // Your HTTP routes here
commands: [], // Your custom commands here
pub_dir: 'public' // Public assets directory
}
}Models in RWS use decorators to define schema and relationships:
import { RWSannotations, RWSModel } from "@rws-framework/server";
const { TrackType } = RWSannotations.modelAnnotations;
class User extends RWSModel<User> {
@TrackType(String, { required: true }, ['unique'])
username: string;
@TrackType(String, { required: true })
email: string;
static _collection = 'users';
constructor(data?: any) {
super(data);
}
}
export default User;import { RWSRoute, RWSController } from "@rws-framework/server/nest";
import User from '../models/User';
@RWSController('user')
export class UserController {
@RWSRoute('user:get')
public async getUser(params: IRequestParams): Promise<any> {
return {
success: true,
data: {
// Your response data
}
}
}
}import { RWSAutoApiController } from "@rws-framework/server";
import { RWSRoute, RWSController } from "@rws-framework/server/nest";
import User from '../models/User';
@RWSController('user', () => User)
export class UserController extends RWSAutoApiController {
@RWSRoute('user:get')
public async getUser(params: IRequestParams): Promise<any> {
return {
success: true,
data: {
// Your response data
}
}
}
}RWS provides a powerful gateway system for real-time communication, enabling WebSocket APIs, pub/sub messaging, and event-driven features. Gateways are classes that handle incoming and outgoing messages, manage client connections, and can broadcast events to connected clients.
Typical use cases:
- Real-time chat and notifications
- Live data updates (dashboards, feeds)
- Pub/sub event distribution
- Custom WebSocket APIs
import { RWSGateway, JSONMessage } from "@rws-framework/server";
class ChatGateway extends RWSGateway {
async handleMessage(message: JSONMessage): Promise<void> {
// Handle incoming WebSocket message
// Example: broadcast to all clients
this.broadcast({ type: 'chat', data: message.data });
}
// Optionally handle connection events
async onConnect(client) {
// Custom logic for new connections
}
async onDisconnect(client) {
// Custom logic for disconnects
}
}
export default ChatGateway;To enable your gateway, add it to the ws_routes in your config:
// ...existing code...
ws_routes: {
chat: ChatGateway,
// ...other gateways
},
// ...existing code...- Each key in
ws_routesis the route name (e.g./ws/chat), and the value is the gateway class. - Gateways can emit/broadcast messages, handle custom events, and manage client state.
Tip: You can define multiple gateways for different real-time features. Gateways can also be used for pub/sub patterns, not just raw WebSocket APIs.
Custom CLI commands can be created:
import { Injectable } from '@nestjs/common';
import {RWSBaseCommand, RWSCommand} from './_command';
import { ParsedOptions } from '../../exec/src/application/cli.module';
@Injectable()
@RWSCommand({name: 'setup', description: 'Systems init command.'})
export class SetupCommand extends RWSBaseCommand {
async run(
passedParams: string[],
options?: ParsedOptions
): Promise<void> {
console.log({passedParams, options})
}
}
export default new SetupCommand();Default services in CLI command class:
import { ConsoleService } from "../../services/ConsoleService";
import { DBService } from "../../services/DBService";
import { ProcessService } from "../../services/ProcessService";
import { UtilsService } from "../../services/UtilsService";
import { RWSConfigService } from "../../services/RWSConfigService";
export interface ICommandBaseServices {
utilsService: UtilsService;
consoleService: ConsoleService;
configService: RWSConfigService;
processService: ProcessService;
dbService: DBService;
}Create an entry point file (e.g., src/index.ts):
import { RWSConfigInjector, RWSBootstrap } from "@rws-framework/server/nest";
import config from './config/config';
import { AppModule } from "./app/app.module";
@RWSConfigInjector(config())
class Bootstrap extends RWSBootstrap {}
await Bootstrap.run(AppModule, {
authorization: false,
transport: 'websocket'
});RWS WebSocket gateways are an extension of NestJS gateways that provide a structured approach to real-time communication. They enable you to create WebSocket servers with organized routing through realtime points.
A WebSocket gateway extends RWSGateway and serves as the entry point for WebSocket connections:
import { RWSGateway } from "@rws-framework/server";
import { SubscribeMessage } from '@nestjs/websockets';
class XXXGateway extends RWSGateway {
// Optional port source setup
protected setPortHandler(){
const port = this.appConfigService.get<number>('second_port');
if(port){
this.server.listen(port);
console.log(`WebSocket server is running on port ${port}`);
}
}
}
export default MarathonChatGateway;By default gateway uses port from ws_port RWSConfigService setting.
All WebSocket messages follow a standardized JSON structure:
interface JSONMessage<T = unknown> {
method: string; // The method/route to call
msg: T; // The message payload
user_id: string; // User identifier
}Realtime points are the core routing mechanism for WebSocket messages. They extend the RealtimePoint abstract class and handle specific message types:
import { RealtimePoint, RWSConfigService, RWSJSONMessage, Socket } from "@rws-framework/server";
import { BlackLogger, RWSRealtimePoint, RWSRealtimeRoute } from "@rws-framework/server/nest";
@RWSRealtimePoint('chat_point', MarathonChatGateway)
export class ChatPoint extends RealtimePoint {
constructor(
// Inject your services here
private someService: SomeService
) {
super();
}
@RWSRealtimeRoute('marathon_chat_message')
async getChatMessage(params: RWSJSONMessage<IWSChatPromptRequest>, socket: Socket) {
const userMessage: string = params.msg.message;
const userId: string | number | null | undefined = params.msg.user_id !== '' ? params.msg.user_id : null;
// Handle the message logic here
// Emit responses back to the client
this.emitMessage('response_method', socket, { data: 'response' });
}
}@RWSRealtimePoint(pointName, gatewayClass): Registers a realtime point with a specific name and associates it with a gateway@RWSRealtimeRoute(methodName): Maps a WebSocket method to a handler function
Realtime points can emit messages back to clients using the emitMessage method:
// Emit a successful response
this.emitMessage<ResponseType>('method_name', socket, responseData, true);
// Emit an error response
this.emitMessage<ErrorType>('error_method', socket, errorData, false);On the frontend, you can connect to WebSocket gateways using the @rws-framework/nest-interconnectors package:
import { RWSViewComponent, RWSView, RWSInject, observable } from "@rws-framework/client";
import { WSService, WSServiceInstance } from "@rws-framework/nest-interconnectors";
@RWSView('web-chat')
class WebChatComponent extends RWSViewComponent {
constructor(@RWSInject(WSService) private wsService: WSServiceInstance) {
super();
}
async connectedCallback(): Promise<void> {
super.connectedCallback();
// Listen for messages from the realtime point
this.wsService.listenForMessage('chat_point', (wsResponse) => {
// Handle the response
console.log('Received:', wsResponse.data);
}, 'response_method');
}
sendMessage() {
// Send message to the realtime point
this.wsService.sendMessage('chat_point', 'marathon_chat_message', {
message: 'Hello World',
user_id: 'user123',
extra: {}
});
}
}Register your gateway in the application configuration:
// In your config file
export default (): IAppConfig => {
return {
// ... other config
ws_routes: {
'chat': MarathonChatGateway,
// Add more gateways as needed
},
// ... other config
}
}For real-time streaming (like chat responses), you can emit multiple messages:
@RWSRealtimeRoute('stream_chat')
async streamChat(params: RWSJSONMessage<IChatRequest>, socket: Socket) {
const messageId = uuid();
// Start streaming
this.emitMessage('chat_response_start', socket, { messageId, date: new Date() });
// Stream chunks
for (const chunk of responseChunks) {
this.emitMessage(`chat_response_chunk_${messageId}`, socket, { chunkWord: chunk });
}
// End streaming
this.emitMessage(`chat_response_end_${messageId}`, socket, { finished: true });
}Define TypeScript interfaces for your message types:
export interface IWSChatPromptRequest {
message: string;
user_id?: string | number;
extra?: unknown;
}
export interface IWSChatPromptStartResponse {
messageId: string;
date: Date;
}
export interface IWSChatPromptChunkResponse {
chunkWord: string;
date: Date;
}- Use descriptive point names: Choose clear, meaningful names for your realtime points
- Implement proper error handling: Always handle exceptions and emit appropriate error responses
- Type your messages: Define TypeScript interfaces for all message types
- Organize by feature: Group related WebSocket functionality into dedicated realtime points
- Clean up resources: Properly handle disconnections and clean up any resources
- Use dependency injection: Leverage NestJS dependency injection for services in realtime points
- Start in development mode:
yarn dev- Build for production:
yarn build- Run production server:
yarn serversrc/
├── app/ # Main application module
├── commands/ # CLI commands
├── config/ # Configuration files
├── controllers/ # HTTP controllers
├── gateways/ # WebSocket gateways
├── models/ # Data models
├── routing/ # Route definitions
├── services/ # Business logic services
└── types/ # TypeScript type definitions
The IAppConfig interface supports the following options:
features: Enable/disable framework featuresws_enabled: Enable WebSocket supportrouting_enabled: Enable HTTP routingssl: Enable SSL/TLS
db_type: DB connection driver type (mongodb by default)db_url: DB connection stringdb_name: Database nameport: HTTP server portws_port: WebSocket server portdomain: Application domainuser_class: User model classuser_models: Array of model classescontroller_list: Array of controllersws_routes: WebSocket route definitionshttp_routes: HTTP route definitionscommands: Custom CLI commandspub_dir: Public assets directory
- Always run
yarn rws-server initafter modifying model schemas. - Use decorators for clean and maintainable code.
- Implement proper error handling in controllers and gateways.
- Keep configuration in environment variables.
- Follow the provided project structure for consistency.
- Use TypeScript for type safety and better development experience.
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
RWS uses a model system that integrates with Prisma for type-safe, scalable database access. Models are defined using decorators and can be automatically converted to Prisma schemas.
- Models extend
RWSModeland use decorators like@TrackType,@Relation, and@InverseRelationfor schema and relationship definition. - Example:
import { TrackType, RWSCollection, RWSModel } from '@rws-framework/db';
@RWSCollection('users')
class User extends RWSModel<User> {
@TrackType(String)
username: string;
// ...
}
export default User;- Use
@Relationfor many-to-one and one-to-one relations. - Use
@InverseRelationfor one-to-many relations.
- Models are converted to Prisma schemas for use with PrismaClient.
- Use the CLI to generate and sync schemas:
npx rws-db "<mongo_url>" <db_name> <db_type> <models_dir>
# or
yarn rws-db "<mongo_url>" <db_name> <db_type> <models_dir>- See the DB package README for advanced relation options and CLI usage.
The RWS Manager handles build, workspace, and environment configuration for both frontend and backend. The main configuration file is rws.config.ts at the project root.
- Exports a function returning an
IManagerConfigobject. - Handles environment variables, build directories, output files, and builder options for frontend, backend, and CLI.
- Example structure:
export default function config(): IManagerConfig {
return {
dev: true,
build: {
front: { /* frontend build options */ },
back: { /* backend build options */ },
cli: { /* CLI build options */ }
}
}
}- The config supports hot reload, custom output paths, TypeScript path mappings, and more.
- See the file for all available options and customize as needed for your project.