A configurable Node.js service template with TypeScript and JavaScript trees, Express REST APIs, Socket.IO events, cron jobs, Redis clients, and gRPC server/client support. It is designed for service-based backends that can run against SQL databases through Sequelize or MongoDB through Mongoose, with validation, Swagger documentation, structured logging, and generated gRPC types.
- Node.js, Express, TypeScript: Robust server setup using Node.js, Express, and TypeScript.
- Sequelize: Integration with Sequelize for SQL database operations.
- Mongoose: Integration with Mongoose for MongoDB database operations.
- Database Compatibility: Interact with MySQL, PostgreSQL, MariaDB, Sqlite, MSSql, MongoDB.
- Validation Mechanism: Pre-built validations for request payloads.
- Automated Swagger Documentation: Automatically generated documentation available at
/api-docs. - gRPC Server + Client Helpers: Local protos, generated TypeScript types, health checks, reflection, and reusable client caching.
- Redis Clients: Named lazy Redis clients for cache/session workloads with graceful shutdown.
- Service-Based Architecture: Modular approach for better organization and scalability.
- Socket Events: Socket modules self-register events from
app/sockets. - Docker: Dockerized for easy deployment.
- Cron Jobs: Cron modules self-register jobs from
app/crons. - Structured Logging: Pino logger with pretty development output and JSON production output.
Both source trees expose the same application shape. The TypeScript tree includes generated proto types; the JavaScript tree loads proto files at runtime.
src-typescript/
server.ts # HTTP, Socket.IO, cron, Redis, and gRPC bootstrap
app/
apis/ # REST + gRPC controllers, repositories, services
common/
grpc.client.ts # Generic remote gRPC client helper
redis.client.ts # Named Redis clients and lifecycle helpers
crons/*.cron.ts # Auto-loaded cron modules
grpc/*.grpc.ts # Auto-loaded gRPC service modules
middlewares/ # REST + gRPC auth/token middleware
routes/*.routes.ts # Auto-loaded REST route modules
sockets/*.socket.ts # Auto-loaded Socket.IO event modules
utils/ # MasterController, builders, logger, gRPC client factory
config/
grpcConfig.ts # gRPC server, route loader, health, reflection
redisConfig.ts # Redis host/TLS/password/client config
socketConfig.ts # Socket.IO init + module loader
cronConfig.ts # Cron module loader + scheduler
proto/
shared/*.proto # Shared messages + health contract
user/*.proto # Demo user RPC contract
generated/ # ts-proto output, git-ignored
JavaScript mirrors live in src-javascript/ with the same directory names and .js extensions.
- Swagger documentation auto-generated for all routes.
- Accessible at
/api-docs. - Generated using the
docmethod in theMasterControllerclass and Joi validation schemas.
The MasterController is the backbone, providing functionality for REST requests, gRPC handlers,
Socket.IO events, cron jobs, payload validation, and Swagger documentation.
- Controller Logic Handling:
restControllermethod manages HTTP requests. - gRPC Handling:
grpcControllerplus the staticrpc()facade manage unary and streaming gRPC methods. - Socket Event Handling:
socketControllermethod manages socket events. - Cron Job Scheduling:
cronControllermethod schedules cron jobs. - Payload Validation:
RequestBuildervalidates REST body/query/path data and gRPC payloads. - Swagger Documentation Generation:
docmethod generates Swagger documentation. - Route Handling:
get,post,put, anddeletemethods register routes within the Express router.
Extend the MasterController to create controller classes for REST, gRPC, socket, or cron work. Use
the provided static registration methods so each transport can be auto-loaded from its own module
directory.
gRPC is bootstrapped from src-typescript/config/grpcConfig.ts and
src-javascript/config/grpcConfig.js.
- Service modules live in
app/grpcand must be named*.grpc.tsor*.grpc.js. - Protos live in
src-typescript/proto; keep.protofiles committed and generated output ignored. - TypeScript stubs are generated into
src-typescript/proto/generatedwithpnpm proto:build-ts. pnpm buildrunsproto:build-tsbefore compiling TypeScript.- The JS tree uses
@grpc/proto-loaderat runtime and does not require generated JS stubs. - Health checks are always enabled from
proto/shared/health.proto. - gRPC reflection is enabled in development-like environments (
development,dev,local). - Remote service consumers should use
app/common/grpc.client.tsor.js, backed byGrpcClientFactoryfor cached clients and normalized errors.
Redis is configured in config/redisConfig.ts and config/redisConfig.js, then exposed from
app/common/redis.client.ts and app/common/redis.client.js.
cacheClientuses Redis DB0for cache-like workloads.sessionClientuses Redis DB1for session-like workloads.redisConnect()is called during server bootstrap after the database connection succeeds.redisDisconnect()is called during graceful shutdown.REDIS_ADDRESS,REDIS_PASSWORD, andREDIS_TLScontrol the connection.
Sockets are initialized from config/socketConfig.ts and config/socketConfig.js.
- Socket modules live in
app/socketsand must be named*.socket.tsor*.socket.js. - Each module imports a controller and calls
Controller.socketIO('event-name'). SocketConfig.InitSocketModules()recursively loads socket modules at startup.socketController(io, socket, payload)handles incoming event payloads.
Cron jobs are initialized from config/cronConfig.ts and config/cronConfig.js.
- Cron modules live in
app/cronsand must be named*.cron.tsor*.cron.js. - Each module calls
Controller.cronJob(pattern)with a crontab string orCronBuilderoutput. CronConfig.InitCronJobs()recursively loads cron modules at startup.CronConfig.startCronJobs()registers and starts all collected cron jobs.
Easily initialize a Node.js server project with custom configurations using our CLI tool:
npx node-server-init <folder-name>If you want to use the current directory as the project folder, run the following command:
npx node-server-init .For more information, visit the CLI Tool page or the GitHub repository.
git clone https://github.com/Thre4dripper/NodeTs-Express-Service-Based-Templatepnpm installpnpm proto:build-tspnpm dev-tspnpm dev-jspnpm buildpnpm preview
Additional dependencies are required for database operations. Install the appropriate dependencies for your database dialect.
pnpm add mysql2pnpm add pg pg-hstorepnpm add sqlite3pnpm add mariadbpnpm add tediouspnpm add ibm_dbpnpm add snowflake-sdkpnpm add oracledbpnpm add mongoose
import Joi from 'joi';
import MasterController from './app/utils/MasterController';
import RequestBuilder from './app/utils/RequestBuilder';
import ResponseBuilder from './app/utils/ResponseBuilder';
import { StatusCodes } from './app/enums/StatusCodes';
import { RegisterUserResponse } from './proto/generated/user/user';
class Controller extends MasterController<IParams, IQuery, IBody> {
// swagger documetation for the api
static doc() {
return {
tags: ['User'],
summary: 'Register User',
description: 'Register User',
};
}
// add your validations here,
// rest of the swagger documentation will be generated automatically from the validation
public static validate(): RequestBuilder {
const payload = new RequestBuilder();
// request body validation
payload.addToBody(
Joi.object().keys({
name: Joi.string().required(),
lastName: Joi.string().required(),
email: Joi.string().email().required(),
password: Joi.string().min(8).max(20).required(),
})
);
// gRPC payload validation can reuse the same Joi schema shape.
payload.addToGrpcPayload(
Joi.object().keys({
name: Joi.string().required(),
email: Joi.string().email().required(),
password: Joi.string().min(8).max(20).required(),
})
);
// request query validation
payload.addToQuery(
Joi.object().keys({
limit: Joi.number().required(),
offset: Joi.number().required(),
})
);
// request params validation
payload.addToPath(
Joi.object().keys({
id: Joi.number().required(),
})
);
return payload;
}
// controller function
async restController(
params: IParams,
query: IQuery,
body: IBody,
headers: any,
allData: any
): Promise<ResponseBuilder> {
// your code here
return new ResponseBuilder(200, Response, 'Success Message');
}
// gRPC controller function
async grpcController(payload: IBody): Promise<ResponseBuilder> {
const grpcResponse: RegisterUserResponse = {
user: {
id: '1',
name: payload.name,
email: payload.email,
},
message: 'User created',
};
const response = new ResponseBuilder(StatusCodes.CREATED, grpcResponse, 'User created');
return response;
}
// socket controller function
socketController(io: Server, socket: Socket, payload: any): void {
// your code here
// Socket data will be available in payload, recieved from the client on socket event, which is setup in the route file
// You can emit data back to the client using io.emit or socket.emit
}
// cron controller function
cronController(): void {
// your scheduled code here (if any)
}
}
export default Controller;- IParams: Request params interface/type
- IQuery: Request query interface/type
- IBody: Request body interface/type
- params: Request params (eg. /user/:id)
- query: Request query (eg. /user?limit=10&offset=0)
- body: Request body
- headers: Request headers
- allData: All request data (all the above-combined + custom data from middlewares)
- io: Socket.io instance
- socket: Socket instance
- payload: Data sent from the client
- payload: Validated gRPC request payload from the proto method.
For TypeScript gRPC responses, type the data object with the generated proto response type before
wrapping it in ResponseBuilder. This makes the proto contract enforce the controller response
shape at compile time.
import express from 'express';
import Controller from '../Controller';
export default (app: express.Application) => {
// REST Routes
Controller.get(app, '/user/:id', [
/* Comma separated middlewares */
]);
Controller.post(app, '/user/:id', [
/* Comma separated middlewares */
]);
Controller.put(app, '/user/:id', [
/* Comma separated middlewares */
]);
Controller.delete(app, '/user/:id', [
/* Comma separated middlewares */
]);
Controller.patch(app, '/user/:id', [
/* Comma separated middlewares */
]);
};Important: Make sure to name your router file as
*.routes.tsor*.routes.js
Note: You don't need to import your router file to anywhere, put it in the routes directory, and it will be automatically taken care by the package.
import * as grpc from '@grpc/grpc-js';
import { loadProto } from '../../config/grpcConfig';
import RegisterUserController from '../apis/user/controllers/register.user.controller';
export default (server: grpc.Server) => {
const proto = loadProto('user/user.proto') as any;
const userService = proto.user.UserRpc.service;
server.addService(userService, {
register: RegisterUserController.rpc([]),
});
};Important: Make sure to name your gRPC module as
*.grpc.tsor*.grpc.jsand place it inapp/grpc.
import getRpcClient from '../../common/grpc.client';
import { GrpcClientFactory } from '../../utils/GrpcClientFactory';
import { UserRpcClient } from '../../../proto/generated/user/user';
const client = getRpcClient('userService', UserRpcClient, 'REMOTE_SERVICE_GRPC_ADDRESS');
const result = await GrpcClientFactory.unary(client, 'register', {
name: 'Jane Doe',
email: 'jane@example.com',
password: 'password123',
});Note:
REMOTE_SERVICE_GRPC_ADDRESSis only an example env var. Pass any env var name that points to the remote service address.
import RegisterUserController from '../apis/user/controllers/register.user.controller';
RegisterUserController.socketIO('hello');Important: Make sure to name your socket module as
*.socket.tsor*.socket.jsand place it inapp/sockets.
import MasterController from '../utils/MasterController';
import CronBuilder from '../utils/CronBuilder';
import { CronWeekday } from '../enums/CronJob';
import { createLogger } from '../utils/Logger';
const log = createLogger('cron');
class DemoCron extends MasterController<null, null, null> {
cronController() {
log.info('Cron job is running');
}
}
// Unix Crontab format
DemoCron.cronJob('*/5 * * * * *');
// Using CronBuilder
DemoCron.cronJob(
new CronBuilder()
.every()
.second()
.every()
.specificMinute([10, 20, 30])
.every()
.dayOfMonth()
.every()
.dayOfWeek(CronWeekday.Friday)
.build()
);Important: Make sure to name your cron file as
*.cron.tsor*.cron.js
Note: You don't need to import your cron file to anywhere, put it in cron directory, and it will be automatically taken care by the package.
PORT- Port number for the server to run on.GRPC_PORT- Port number for the gRPC server to run on.DB_DIALECT- Database dialect to use. (Options: mysql, postgres, mariadb, sqlite, mssql, mongodb)DB_HOST- Database host.DB_PORT- Database port.DB_USER- Database username.DB_PASS- Database password.DB_NAME- Database name.MONGO_URI- MongoDB URI (Only for MongoDB Dialect).DB_SSL,DB_SSL_REJECT_UNAUTHORIZED,DB_SSL_CA_FILE- Database TLS settings.REDIS_ADDRESS,REDIS_PASSWORD,REDIS_TLS- Redis connection settings.JWT_PRIVATE_KEYorJWT_PRIVATE_KEY_PATH- RS256 signing key input.LOG_LEVEL,LOG_TRANSPORT- Pino logging settings.REMOTE_SERVICE_GRPC_ADDRESS- Example remote gRPC service address for client helpers.$ docker build -t <image-name> .$ docker run -e <env-variable>=<value> -p <port>:<port> <image-name>$ docker run -d -e <env-variable>=<value> -p <port>:<port> <image-name>$ docker stop <container-id>$ docker rm <container-id>$ docker rmi <image-name>
Contributions are welcome! Please feel free to submit a Pull Request.