| name | steedos-server-internals |
| description | Steedos Server internal architecture: NestJS 11 + Moleculer 0.14 hybrid.
TRIGGER: NestJS module organization; Moleculer broker (namespace, transporter,
cacher, serializer); AppMoleculer events ($packages.changed, $metadata.*,
@objectRecordEvent); Socket.IO AppGateway, WebSocket rooms; bootstrap sequence,
middleware, guards, dependency injection; broker.call, broker.emit;
edition system, @builder6/* ecosystem.
SKIP: use REST API โ steedos-server-api or steedos-builder6-api;
configure server โ steedos-configuration; Builder6 internals โ
steedos-builder6-internals; overview โ steedos-getting-started.
|
Steedos Server Architecture | Steedos ๆๅก็ซฏๆถๆ
Overview | ๆฆ่ฟฐ
Steedos Server (builder6/server) is a NestJS + Moleculer hybrid backend. NestJS handles HTTP/REST, Moleculer handles microservice orchestration, and Socket.IO provides real-time communication.
Technology Stack | ๆๆฏๆ
- HTTP Framework: NestJS 11 (Express adapter)
- Microservices: Moleculer 0.14
- Real-time: Socket.IO via
@nestjs/websockets
- Database: MongoDB 3.7 via
@steedos/objectql
- Session Store: Redis via
connect-redis + ioredis
- Cache: Redis via Moleculer cacher
- Auth: Passport (local + OIDC) + JWT via
@builder6/core
- API Docs: Swagger/OpenAPI at
/api/v6
Source Layout | ๆบ็ ็ปๆ
builder6/server/src/
โโโ main.ts # Entry point โ bootstrap()
โโโ bootstrap.ts # App creation, middleware, Swagger
โโโ app.module.ts # Root NestJS module (25+ imports)
โโโ app.controller.ts # Health checks, public settings
โโโ app.gateway.ts # WebSocket gateway (Socket.IO)
โโโ app.moleculer.ts # Moleculer service + events
โโโ config/
โ โโโ steedos.config.ts # YAML config loader
โ โโโ moleculler.config.ts # Moleculer broker settings
โโโ api/
โ โโโ data/
โ โโโ data.controller.ts # CRUD at /api/v6/data/:objectName
โ โโโ data.service.ts # ObjectQL data access
โโโ objects/
โ โโโ objects.controller.ts # GET /api/v6/objects/:objectApiName
โ โโโ objects.service.ts # ObjectQL schema + function runner
โ โโโ functions.controller.ts # GET|POST /api/v6/functions/:obj/:func
โโโ workflow/
โโโ file.controller.ts # File upload endpoint
NestJS Modules | NestJS ๆจกๅ
The root AppModule imports 25+ modules:
| Module | Package | Purpose |
|---|
ConfigModule | @nestjs/config | Environment/config loading |
MoleculerModule | @builder6/moleculer | Moleculer broker integration |
AuthModule | @builder6/core | Authentication guards + strategies |
MongodbModule | @builder6/core | MongoDB connection management |
SteedosModule | @builder6/steedos | Core Steedos metadata + ObjectQL |
TablesModule | @builder6/tables | Data table management |
FilesModule | @builder6/files | File upload/download |
PagesModule | @builder6/pages | Micro page management |
PluginModule | @builder6/core | Dynamic plugin loading |
MicroserviceModule | @builder6/microservices | Inter-service communication |
Bootstrap Sequence | ๅฏๅจๆต็จ
main.ts โ bootstrap()
1. NestFactory.create<NestExpressApplication>(AppModule)
2. Connect Redis cluster microservice transport
3. Set up Logger (pino), GlobalFilters, LoggerErrorInterceptor
4. Set up HybridAdapter for WebSocket (Socket.IO)
5. Enable CORS (all origins, credentials: true)
6. Create Redis session store
7. Apply Express middleware stack
8. Configure Swagger at /api/v6
9. Start all microservices
10. Mount static router + SPA fallback
11. Listen on B6_PORT (default: 5100)
Middleware Stack | ไธญ้ดไปถๆ
- Session โ Redis-backed via
connect-redis (prefix: steedos-session:)
- Cookie Parser โ
cookie-parser
- JSON Body โ
express.json() (limit: 50mb)
- URL Encoded โ
express.urlencoded() (limit: 100mb)
- Compression โ
compression()
- Cloud Proxy โ
http-proxy-middleware โ STEEDOS_CLOUD_URL (if configured)
- Static Router โ
@steedos/router for platform assets
- SPA Fallback โ
@steedos/webapp index.html
Guards | ่ฎค่ฏๅฎๅซ
| Guard | Usage |
|---|
AuthGuard | All data/objects/functions controllers |
AdminGuard | Admin-only endpoints (Direct MongoDB API) |
Authentication: cookie-based X-Space-Id + X-Auth-Token headers/cookies.
ObjectQL Data Access | ObjectQL ๆฐๆฎ่ฎฟ้ฎ
const obj = getObject("orders");
await obj.find(query, userSession);
await obj.insert(doc, userSession);
await obj.update(id, data, userSession);
await obj.delete(id, userSession);
Edition System | ็ๆฌ็ณป็ป
| Edition | Condition | Services |
|---|
| Community (ce) | Default | @steedos/service-community |
| Enterprise (ee) | STEEDOS_LICENSE set | + @steedos/service-license + @steedos/service-enterprise |
Moleculer Integration | Moleculer ้ๆ
Broker Configuration | ไปฃ็้
็ฝฎ
{
namespace: "steedos",
transporter: process.env.B6_TRANSPORTER,
cacher: process.env.B6_CACHER,
serializer: "JSON",
requestTimeout: 0,
heartbeatInterval: 10,
heartbeatTimeout: 30,
logger: { type: "Console", level: process.env.B6_LOG_LEVEL || "warn" },
registry: { strategy: "RoundRobin", preferLocal: true },
}
NestJS Integration
MoleculerModule.forRoot(getMoleculerConfig())
@Injectable()
export class MyService {
constructor(@InjectBroker() private broker: ServiceBroker) {}
}
AppMoleculer Event Handlers | ไบไปถๅค็
$packages.changed
Fired when all packages finish loading. Sets global.STEEDOS_STARTED = true, loads tenant, broadcasts @steedos/server.started.
$metadata.*
Forwards all metadata changes to WebSocket:
appGateway.metadataChange({
type: payload.type,
action: payload.action,
_id, name, objectName
});
@objectRecordEvent.*.*
Record change events โ WebSocket room broadcast to {spaceId}-record:{objectApiName}:change-{id}.
$broadcast.$notification.users
Routes user notifications to per-user WebSocket rooms.
Inter-Service Communication | ่ทจๆๅก้ไฟก
const records = await broker.call("objectql.directFind", {
objectName: "spaces", query: { top: 1 }
});
broker.emit("$metadata.objects", { type: "objects", action: "update", data: {...} });
broker.broadcast("@steedos/server.started");
Package Loader Flow | ่ฝฏไปถๅ
ๅ ่ฝฝๆต็จ
Package starts โ loads YAML metadata โ emits $metadata.* events
โ AppMoleculer handles โ forwards to WebSocket
โ All packages loaded โ emits $packages.changed
โ initializes tenant + broadcasts server.started
WebSocket (Socket.IO) | WebSocket ๅฎๆถ้ไฟก
Gateway Configuration | ็ฝๅ
ณ้
็ฝฎ
@WebSocketGateway({ path: "/socket.io/", cors: true })
export class AppGateway implements OnGatewayConnection, OnGatewayDisconnect
Connection Authentication | ่ฟๆฅ่ฎค่ฏ
- Parse
cookie header from socket.handshake.headers
- Extract
X-Space-Id + X-Auth-Token
- Validate via
AuthService.getUserByToken(token)
Room System | ๆฟ้ด็ณป็ป
Rooms are scoped by tenant: {tenantId}-{roomPart}. Individual rooms: {roomPart}-{userId}.
socket.emit("subscribe", { roomParts: ["orders"], individual: false });
socket.emit("subscribe", { roomParts: "notification-change", individual: true });
socket.emit("unsubscribe", { roomParts: ["orders"] });
Server โ Client Events
| Event | Description |
|---|
s:metadata:change | Metadata change (objects, fields, apps, etc.) |
s:notification-change | User notification (per-user room) |
s:record:{obj}:change-{id} | Record change event |
Moleculer โ WebSocket Mapping
| Moleculer Event | WebSocket Event |
|---|
$metadata.* | s:metadata:change (global) |
$broadcast.$notification.users | s:notification-change (per-user) |
$broadcast.socket.emit | Generic emit with optional room |
@objectRecordEvent.*.* | s:record:{obj}:change-{id} (record rooms) |
Broadcasting from Moleculer Services
broker.emit("$broadcast.socket.emit", {
data: { eventName: "custom-event", eventParams: {...}, room: "tenantId-room" }
});
broker.emit("$broadcast.$notification.users", {
data: { tenantId: "space_id", users: ["user1"], message: "New task" }
});