Backend API Overview
Relevant source files
The following files were used as context for generating this wiki page:The OnChainSage backend is a robust, modular API service built with the NestJS framework. It serves as the central hub for the entire platform, handling core business logic, data persistence, user authentication, and real-time communication. Its primary responsibilities include serving data to the frontend, processing requests, managing trading signals, and integrating with the database and other services.
The architecture is designed to be data-first and event-driven, promoting scalability and loose coupling between its various components. This overview details the backend's structure, bootstrap process, core modules, and key features like error handling and security.
Sources: apps/backend/README.md, ARCHITECTURE.md
Architecture and Tech Stack
The backend is built on a modern technology stack, leveraging NestJS for its scalable and modular architecture. It acts as an API Gateway, handling requests, authentication, and coordinating signals.
Sources: apps/backend/README.md, ARCHITECTURE.md
Key Technologies
| Technology | Purpose | Source |
|---|---|---|
| NestJS | Core application framework | apps/backend/README.md |
| TypeScript | Primary programming language | apps/backend/README.md |
| PostgreSQL | Relational database for data persistence | apps/backend/README.md |
| TypeORM | Object-Relational Mapper (ORM) for database interaction | apps/backend/README.md |
| JWT | Secure user authentication and authorization | apps/backend/README.md |
| Swagger/OpenAPI | API documentation generation | apps/backend/README.md |
| Redis | In-memory data store for caching | ARCHITECTURE.md |
| WebSockets | Real-time updates and communication | ARCHITECTURE.md |
High-Level Architecture
The backend layer is composed of several services that manage different aspects of the application's functionality.
This diagram illustrates the backend's role in handling user requests and interacting with other system components.
Sources: ARCHITECTURE.md
Application Bootstrap
The application's entry point is main.ts, which is responsible for creating and configuring the NestJS application instance.
Sources: apps/backend/src/main.ts
Bootstrap Flow
The bootstrap process involves several key steps:
- Creating a NestJS application instance from the root
AppModule. - Enabling Cross-Origin Resource Sharing (CORS).
- Applying a global
ValidationPipeto automatically validate incoming request payloads. - Applying a global
HttpExceptionFilterfor consistent error handling. - Setting up Swagger for API documentation.
- Starting the server to listen for incoming connections on a configured port.
This flow ensures the application is correctly configured with all necessary global middleware, pipes, and filters before it starts accepting requests.
Sources: apps/backend/src/main.ts:18-44
Code Example: main.ts
// apps/backend/src/main.ts:18-44
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: WinstonModule.createLogger(winstonConfig),
});
// Enable CORS
app.enableCors();
// Global validation pipe
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
// Global exception filter
app.useGlobalFilters(new HttpExceptionFilter());
// Swagger setup
const config = new DocumentBuilder()
.setTitle('OnChainSage API')
.setDescription('API documentation for OnChainSage')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
// Start the application
const port = process.env.PORT || 3000;
await app.listen(port);
}
bootstrap();
This snippet from main.ts shows the core setup sequence of the NestJS application.
Core Module (AppModule)
The AppModule is the root module of the backend application. It imports and configures all other feature modules, services, and providers required for the application to function.
Sources: apps/backend/src/app.module.ts
Module Structure
The AppModule aggregates various feature-specific modules, demonstrating the modular design of the backend.
This diagram shows the main modules imported by the root AppModule.
Sources: apps/backend/src/app.module.ts:37-88
Key Imported Modules
ConfigModule: Manages environment variables and application configuration.TypeOrmModule: Integrates TypeORM for database connectivity using PostgreSQL.AuthModule: Handles user authentication and JWT strategy.UsersModule: Manages user-related operations.SignalsModule: Core module for handling trading signals.HealthModule: Provides health check endpoints.ExportModule: Manages data export functionalities.GatewayModule: Contains WebSocket gateways for real-time communication.
Sources: apps/backend/src/app.module.ts:37-88
Error Handling
A global HttpExceptionFilter is implemented to catch all exceptions of type HttpException and its subclasses. This ensures that all HTTP error responses are standardized across the application, providing a consistent and predictable error format to clients.
Sources: apps/backend/src/common/filters/http-exception.filter.ts
Exception Filter Flow
When a controller or service throws an HttpException, the filter intercepts it and formats a JSON response.
This sequence diagram illustrates how the HttpExceptionFilter processes an error and returns a structured response.
Sources: apps/backend/src/common/filters/http-exception.filter.ts:11-30
Code Example: HttpExceptionFilter
// apps/backend/src/common/filters/http-exception.filter.ts:11-30
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
const exceptionResponse = exception.getResponse();
const errorResponse = {
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message:
typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message,
};
response.status(status).json(errorResponse);
}
}
This filter captures the exception and constructs a standardized JSON error object.
Security and Authorization
The backend employs several security mechanisms, primarily through NestJS Guards, to protect endpoints and authorize access.
Sources: apps/backend/src/export/controllers/export.controller.ts
Guards
Guards are used to determine whether a given request will be handled by the route handler or not, based on certain conditions (e.g., roles, permissions, authentication status).
JwtAuthGuard: Protects routes by verifying a JWT token provided in the request'sAuthorizationheader.AdminGuard: A role-based guard that restricts access to administrative users only.WalletAuthGuard: A custom guard, likely for verifying wallet signatures, used on specific endpoints like data exports.@RateLimitDecorator: A custom decorator to apply rate limiting to specific endpoints, preventing abuse.
The ExportController demonstrates the use of these guards to secure its endpoints. For example, the /export/signal-history endpoint requires a valid wallet authentication, while the /export/signals endpoint is restricted to admins.
// apps/backend/src/export/controllers/export.controller.ts:36-40
@Get('signal-history')
@UseGuards(WalletAuthGuard)
@ApiBearerAuth()
@RateLimit(5, 60) // 5 requests per minute
@ApiOperation({ summary: 'Export user signal history' })
// apps/backend/src/export/controllers/export.controller.ts:174-176
@Post('signals')
@UseGuards(JwtAuthGuard, AdminGuard)
@ApiOperation({ summary: 'Export signals data (Admin only)' })
This layered approach to security ensures that data and operations are protected based on user identity and roles.
Sources: apps/backend/src/export/controllers/export.controller.ts:37-39, 175