nstarter Framework

website·Indexed 31 days ago

https://nstarter-docs.jdydevelop.com

A Node.js development framework and CLI tool for initializing projects based on engineering templates. It implements a 3-tier application architecture and provides a suite of plugins including nstarter-mongodb for database migration, nstarter-http-request for secure HTTP clients with Axios/Undici support, nstarter-config for standardized configuration management, and nstarter-otel-trace for OpenTelemetry-based observability.

Tokens
21.2K
Snippets
99
Records
132
Agent score
50%

What's inside nstarter

  1. Overview of nstarter 3-tier architecture

    nstarter uses a 3-tier application architecture model to describe layer relationships, which can be extended to an n-tier architecture for heavy enterprise applications. The layers are:

    1. User Services Layer (USL) / Presentation Layer: The top layer responsible for displaying data, receiving user input, and organizing API responses for Web applications.
    2. Business Logic Layer (BLL): The core of the system where business rules and processes are implemented. It acts as an intermediary between the presentation layer and the data access layer.
    3. Data Access Layer (DAL): The persistence layer responsible for CRUD operations via databases, files, or third-party APIs.
  2. Manage application configuration with nstarter-config

    The nstarter-config component provides a standardized way to manage application configurations. It supports loading configuration objects from JSON and YAML files, hot-reloading configurations without restarting the application, and injecting additional configuration files via an includes option.
  3. Propagate and create child spans for distributed tracing

    In a microservices architecture, you should receive the traceparent from an upstream request and create a child span to pass to the next downstream service. This maintains the trace lineage while identifying the specific operation in the current service.
    // Get traceparent from upstream request headers
    const incomingTraceparent = request.headers.traceparent;
    
    // Create a child span (inherits traceId and sampling, generates new spanId)
    const childTraceparent = TraceContext.createChild(incomingTraceparent);
    
    const response = await client.post('https://downstream-service.com/api', data, {
        traceparent: childTraceparent
    });
  4. Deploy a project using nstarter

    Deploy a project based on a template. The interactive deployment is recommended as it guides you through component selection, npm dependency installation, and git initialization.
    # Interactive deployment (Recommended)
    nstarter deploy ./demo
    
    # Non-interactive deployment
    nstarter deploy -t default -n demo -y ~/projects/demo
  5. Install nstarter-entity and dependencies

    Install nstarter-entity as a production dependency. Additionally, typescript-json-schema must be installed as a development dependency to generate the JSON Schema files from TypeScript definitions during the build process.
    npm install -S nstarter-entity
    npm install -D typescript-json-schema
  6. Handle Inversify v7 migration in nstarter-core

    nstarter-core v1.3.0 upgrades inversify to v7.x. Key behavioral changes include:

    • The injection process no longer contains implicit logic for base classes by default.
    • The timing of @injectable behavior has been adjusted to occur during the execution of service, component, or controller decorators.
  7. Update nstarter-cli to v1.0.2 requirements

    Updating to nstarter-cli v1.0.2 introduces new environment requirements and a breaking change regarding project templates. The CLI now requires Node.js version 20.16.0 or higher and TypeScript 5.x. Additionally, starting from version 1.x, the CLI only supports v2 version template projects.
  8. Implement the Repository pattern with nstarter-mongodb

    nstarter-express uses the Repository pattern to decouple business logic from the Mongoose ORM. This ensures that changes to the database schema or ORM configuration do not directly impact the business layer. Repositories are defined as classes extending MongodbRepo and are instantiated via repoProvider to support database session contexts, allowing independent database sessions to be bound to specific repository instances.
    import { MongodbRepo, repoProvider } from 'nstarter-mongodb';
    
    class UserRepo extends MongodbRepo {
        public createOne(user: IUserModel) {
            return userModel.create([user], {
                session: this._session
            });
        }
    
        public findOneByUsername(username: string) {
            return userModel.findOne({ username }).setOptions({
                session: this._session
            }).lean(true);
        }
    }
    
    export const userRepo = repoProvider(UserRepo);
  9. Implement controllers in nstarter-express

    Controllers act as the bridge between business logic and external data input/output. They handle user input and structure the return results (e.g., rendering HTML for pages or returning JSON for APIs). Controllers should not contain complex business logic; instead, they should delegate that to the business service layer. Controllers are typically located in src/controller and should use the @controller() decorator from nstarter-core for standardized exception handling and support for both synchronous and asynchronous methods.
    import { controller } from 'nstarter-core';
    import { Request, Response } from 'express';
    
    @controller()
    export class DemoController {
        // GET request for page rendering
        public async goWelcomeView(req: Request, res: Response) {
            const { params } = req;
            return res.render('welcome', {
                title: 'To Infinity and Beyond!'
            });
        };
        
        // Error handling example
        public async goErrorView(req: Request, res: Response) {
            const { params } = req;
            throw Errors.user(1001);
        };
    
        // POST request returning JSON data
        public async doPing(req: Request, res: Response) {
            const { body } = req;
            return res.json({ 'msg': 'pong' });
        }
    }
  10. Define and register business services in nstarter

    In nstarter, business logic is managed in the service layer. Services are registered in an IoC container for unified calling. Services should be defined in the src/services directory and registered in src/services/index.ts using registerSvc and getSvc to expose the instantiated service objects.
    // src/services/ping.service.ts
    import { service, injectService } from 'nstarter'; // Assuming these are the imports
    
    @service()
    export class PingService {
        @injectService()
        private pongService: PongService;
    
        public ping() {
            console.log('ping');
        }
    
        public pong () {
            this.pongService.pong();
        }
    }
    
    // src/services/index.ts
    import { registerSvc, getSvc } from 'nstarter';
    
    registerSvc(PingService);
    registerSvc(PongService);
    
    export const pingService = getSvc<PingService>(PingService);
    export const pongService = getSvc<PongService>(PongService);
  11. Define and register controllers in nstarter

    In the nstarter framework, controllers carry the core business logic. Controllers are defined as classes and managed via an IoC container. To define a controller, use the @controller() decorator. By default, the decorator generates an identifier based on the class name, but you can optionally pass a string or Symbol as a custom identifier.

    Because the IoC mechanism does not automatically scan for controllers, you must manually register them using registerCtl. To retrieve a registered controller instance from outside the controller layer, use the getCtl utility method.

    import { controller, registerCtl, getCtl } from 'nstarter-core';
    
    // 1. Define the controller
    @controller()
    export class PingController {
        public ping(req: Request, res: Response): void {
            res.end('pong');
        }
    }
    
    // 2. Register the controller
    registerCtl(PingController);
    
    // 3. Retrieve the controller instance
    export const pingController = getCtl<PingController>(PingController);