A foundational template for efficiently developing scalable web service applications.
- π Project Introduction
- π Quick Start
- π Project Structure
- π System Architecture
- π» Development Guide
- βοΈ Environment Variables
- π¦ Deployment
- π§ Module Setup Example
- β FAQ
Web Service Template is a backend service template developed based on the Fastify framework, providing developers with an efficient and scalable foundation for web service development.
- Efficient Development: Provides a foundational template for developing scalable web services
- Modular Design: Separates core functionalities from business modules for easy maintenance and scalability
- Simplified Workflow: Offers rich script commands to simplify development, testing, and deployment
- Enhanced Development Efficiency: Supports hot reloading and automatic compilation
- Integrated Core Functionalities: Includes built-in dependency injection, configuration management, error handling, and more
-
Ensure you have the correct version of Node.js installed (see
.nvmrc
for details) -
Install pnpm globally:
npm install -g pnpm
-
Clone the repository and navigate to the project directory
git clone <repository_url> cd <project_directory>
-
Deploy the local development environment
(See the Development Guide section for details) -
Copy the example environment variables file
cp .env.example .env
-
Install dependencies
nvm use # (Optional: switch to the Node.js version specified in .nvmrc) pnpm run install:ci
-
Compile TypeScript files
pnpm run build
-
Start the development server
pnpm start
This project follows a clear separation between core components and business modules:
βββ README.md
βββ index.ts # Application entry point
βββ package.json
βββ tsconfig.json
βββ tsconfig.eslint.json
βββ eslint.config.mjs
βββ jest.config.js
βββ pnpm-lock.yaml
βββ node_modules/
βββ src/
β βββ core/ # Framework core: app initialization, DI, server, config, etc.
β β βββ app/ # Application setup and initialization
β β βββ config/ # Environment and configuration management
β β βββ constants/ # Application-wide constants and enums
β β βββ di/ # Dependency injection setup
β β β βββ di-container.ts
β β β βββ global-di-configs.ts # Global DI configurations (e.g., Logger, Environment, Prisma, etc.)
β β β βββ on-initiate-executor.ts
β β β βββ index.ts
β β βββ server/ # Web server configuration, routing, and error handling
β β β βββ bootstrap/ # Server initialization
β β β βββ modules/ # Startup modules (e.g., WebServerModule)
β β β βββ ...
β β βββ services/ # Core services (e.g., Logger, Environment)
β β βββ types/ # Type definitions and interfaces
β β β βββ startup-module.types.ts # IStartupModule interface
β β β βββ ...
β β βββ utils/ # Utility functions and base module classes
β β βββ index.ts
β βββ modules/ # Business modules (each module can be later extracted as an independent package)
β β βββ hello/ # Example Hello module
β β β βββ constants/
β β β βββ controllers/
β β β βββ dto/
β β β βββ model/
β β β βββ routes/
β β β βββ services/
β β β βββ spec/
β β β βββ types/
β β β βββ hello.module.ts # Module configuration & DI registration
β β β βββ index.ts # Module exports
β β βββ index.ts # Aggregated module registration
β βββ index.ts # (Optional) Additional entry point for src directory if needed
βββ test-utils/ # Testing utilities and mocks
β βββ containers/
β βββ mocks/
The core
directory contains the essential framework parts:
app/
: Application initialization and server setupconfig/
: Environment loading and configuration managementconstants/
: Definitions for tokens, modes, and other constantsdi/
: Dependency injection container, including global DI configurations inglobal-di-configs.ts
server/
: Fastify server setup, routing, error handling, and Swagger documentationservices/
: Core services like Logger, Environment, etc.types/
: Shared TypeScript types and interfacesutils/
: Base classes and helper functions for modules, including the initializer-pool mechanism
The application uses a modular startup process that allows different types of services (web server, cron jobs, message
queue consumers, etc.) to be initialized, started, and stopped in a coordinated way. This is implemented through the
IStartupModule
interface:
export interface IStartupModule {
readonly name: string;
initialize(container: AppContainer): Promise<void>;
start(): Promise<void>;
stop(): Promise<void>;
}
- WebServerModule: Initializes and starts the Fastify web server. This module is registered by default.
To register a startup module:
import { setupApp } from 'src/core/app';
import { CronJobModule } from 'src/core';
import { globalDIConfigs } from 'src/core/di';
import { WebServerModule } from 'src/core/server';
import modules from 'src/modules';
(async () => {
const app = await setupApp(globalDIConfigs, modules);
// Register a default startup module
app.registerStartupModule(new WebServerModule());
await app.initialize();
await app.start();
})();
For more details, see the Startup Modules documentation.
The application includes an Initializer Pool utility that follows the Plugin pattern / Initialization Registry pattern. This mechanism allows for registering and executing initialization functions in a coordinated way.
// Example of using the initializer pool
import { serviceInitializerPool } from 'src/core/utils/initializer-pool';
// Register an initialization function
serviceInitializerPool.register(async () => {
// Initialization logic here
console.log('Initializing service...');
});
// Execute all registered initialization functions
await serviceInitializerPool.initializeAll();
Key features of the Initializer Pool:
- Registration: Register initialization functions to be executed later
- Sequential Execution: Execute all registered functions in sequence
- Singleton Instance: A singleton instance (
serviceInitializerPool
) is available for application-wide use - Custom Pools: Create custom initializer pools using the
createInitializerPool()
function
This mechanism is particularly useful for initializing services, setting up event listeners, or performing any other startup tasks that need to be coordinated across different parts of the application.
All business or feature modules are organized under src/modules
. Each module is self-contained and registers its own
dependencies. This design improves clarity and allows modules to be later extracted as independent packages.
Web Service Template adopts a modular architecture that separates core framework functionalities from business logic.
-
Core Layer
- Provides infrastructure: dependency injection, configuration management, server setup
- Handles cross-cutting concerns: logging, error handling, request context
-
Module Layer
- Contains independent business feature modules
- Each module is self-contained and can be developed independently
-
Service Layer
- Implements business logic
- Handles data transformation and business rules
-
Controller Layer
- Processes HTTP requests and responses
- Validates input and formats output
-
Route Layer
- Defines API endpoints
- Connects HTTP requests to controllers
-
install:ci: Install dependencies with a frozen lockfile (ideal for CI)
pnpm run install:ci
-
install:dev: Install dependencies without freezing the lockfile (for development)
pnpm run install:dev
-
build: Compile TypeScript files
pnpm run build
-
start: Run the development server with automatic recompilation
pnpm start
-
lint: Check code quality via linting
pnpm run lint
-
lint:fix: Automatically fix linting issues
pnpm run lint:fix
-
unittest:coverage: Run unit tests and generate a coverage report
pnpm run unittest:coverage
-
Live Reloading
During development, changes are automatically compiled and the server is restarted:pnpm start
-
Code Quality
Ensures consistent code formatting and style:pnpm run lint:fix
-
Testing
Run unit tests and generate coverage reports:pnpm run unittest:coverage
-
Install Docker
-
Navigate to the project's
.dev-app-projects
directorycd .dev-app-projects
-
Start the Docker containers
docker-compose up -d
-
Prepare the database
Make sure your Prisma models are defined inschema.prisma
before running migrations.pnpm run prisma:migrate
Or generate the Prisma client:
pnpm run prisma:generate
This project uses an .env
file to configure environment-specific settings. Below are the main environment variables:
Variable Name | Description | Default Value |
---|---|---|
APP_NAME |
Application name for identification across services | WebServiceTemplate |
APP_ENV |
Application runtime environment | development |
PORT |
Port number where the service runs | 3000 |
For complete configuration, please refer to the .env.example
file.
Business modules reside in src/modules
. Here's an example using a "Hello" module:
src/
βββ modules/
β βββ hello/
β β βββ constants/ # Module-specific constants (e.g., routes, injection tokens)
β β βββ controllers/ # HTTP request handlers
β β βββ dto/ # Request/response schemas
β β βββ model/ # Data models
β β βββ routes/ # Route definitions
β β βββ services/ # Business logic
β β βββ spec/ # Unit tests
β β βββ types/ # Type definitions
β β βββ hello.module.ts # Module configuration & DI registration
β β βββ index.ts # Module exports
β βββ index.ts # Aggregated module registration for the application
The aggregated module registration is defined in src/modules/index.ts
:
// src/modules/index.ts
import { IModule } from 'src/core/types';
import { HelloModule } from './hello';
const modules: IModule[] = [
new HelloModule(), // Register the Hello module
];
export default modules;
The Hello module demonstrates how to register its own dependencies:
// src/modules/hello/hello.module.ts
import { BaseModule } from 'src/core/utils';
import { InjectionTokens } from './constants/injection-tokens';
import { InjectionResolverMode } from 'src/core/constants';
import { HelloRoute } from './routes/hello.route';
import { HelloController } from './controllers/hello.controller';
import { HelloService } from './services/hello.service';
export class HelloModule extends BaseModule {
registerDependencies() {
this.registerDependency(
InjectionTokens.HELLO_ROUTE,
HelloRoute,
InjectionResolverMode.SINGLETON,
)
.registerDependency(
InjectionTokens.HELLO_CONTROLLER,
HelloController,
InjectionResolverMode.SINGLETON,
)
.registerDependency(
InjectionTokens.HELLO_SERVICE,
HelloService,
InjectionResolverMode.SINGLETON,
);
}
}
pnpm run build
To deploy database changes in a production environment:
pnpm run prisma:deploy
- Create a new module directory under
src/modules
- Implement necessary components (constants, controllers, services, etc.)
- Create a module class that extends
BaseModule
- Register the new module in
src/modules/index.ts
Swagger documentation is automatically generated through the Fastify Swagger plugin.
You can use TypeBox in your
controllers to define request and response schemas.
Use Prisma CLI commands to manage database migrations:
# Create a new migration
pnpm run prisma:migrate
# Deploy migrations to production environment
pnpm run prisma:deploy
# Reset database (development environment)
pnpm run prisma:reset
- Add new variables to the
.env.example
file - Update the environment configuration schema in
src/core/config
- Use environment variables through dependency injection
The framework provides a centralized error handling mechanism. You can:
- Create custom error classes
- Throw these errors in controllers or services
- The global error handler will automatically catch and format responses