Building a Modern Node.js Backend Template with TypeScript, ESM, TypeORM, and SQLite
Learn how to build a professional Node.js backend template using TypeScript, ESM, TypeORM, and SQLite. This guide explains the folder structure, technology choices, and best practices for creating scalable and reusable backend projects.
When starting a new backend project, having a solid template can save a lot of time and ensure your architecture follows best practices. In this article, I’ll walk you through a professional Node.js backend template I built with TypeScript, ESM modules, TypeORM, and SQLite, explaining the reasoning behind every design decision.
Why a Template?
Backend projects often share common requirements:
- Express server setup
- Database configuration
- Entity definitions and CRUD endpoints
- Environment management
- Hot reload during development
By creating a reusable template, you can spin up new projects in minutes while maintaining consistency and best practices.
Project Goals
Here’s what I wanted this template to achieve:
-
Modern JavaScript / TypeScript setup
- Use ES modules (
import/export) for forward compatibility and consistency with modern tooling. - Use TypeScript for type safety and maintainability.
- Use ES modules (
-
Scalable folder structure
- Separate concerns (routes, controllers, entities, app, server).
- Make it easy to extend with services, middleware, authentication, or additional modules.
-
Developer-friendly workflow
- Hot reload during development with minimal setup.
- Lightweight database (SQLite) for quick demos and prototyping.
-
Production-ready foundation
- Clear distinction between configuration (
app.ts) and server startup (server.ts). - Explicit database column types to avoid issues in ESM + TypeScript projects.
- Clear distinction between configuration (
Technology Choices
Here’s why I chose each technology:
| Technology | Reasoning |
|---|---|
| Node.js | Lightweight and widely used for backend development. |
| TypeScript | Adds type safety, reduces runtime errors, and works well with decorators used by TypeORM. |
| ESM modules | Modern module system; avoids common issues with require vs import and is future-proof. |
| Express | Minimal web framework; easy to configure routes, middlewares, and request handling. |
| TypeORM | A popular ORM that works well with TypeScript decorators, supports SQLite, and can easily switch to other DBs later. |
| SQLite | Zero-config, lightweight database ideal for demo projects or prototypes. |
Folder Structure
One of the first decisions was designing a folder structure that balances simplicity and scalability:
src/
├── app.ts # Express configuration
├── server.ts # Server bootstrap + database initialization
├── data-source.ts # TypeORM DataSource configuration
├── entities/ # Database entities
│ └── User.ts
├── routes/ # Route definitions
│ └── user.routes.ts
└── controllers/ # Business logic / request handlers
└── user.controller.ts
Why this structure?
app.ts– only configures Express, middlewares, and routes. No database logic here.server.ts– handles database initialization and starts the server. This separation allows importingappin tests without starting the server.controllers/– all business logic lives here. Keeps endpoints clean and easy to test.routes/– maps endpoints to controllers. Makes it easy to see all routes in one place.entities/– TypeORM entities with explicit column types to avoid ESM-related metadata issues.
TypeORM + ESM Considerations
TypeORM works well with TypeScript decorators, but there are some important considerations for ESM projects:
- Always import
"reflect-metadata"at the very top of the main entry file (app.ts). This ensures TypeScript decorator metadata is available before any entity is imported. - Explicitly define column types in entities. For example:
@Column("varchar", { length: 150 })
name!: string;Without explicit types, TypeORM may throw ColumnTypeUndefinedError in ESM + TypeScript projects.
- Use
synchronize: truein development to auto-generate tables, but disable in production.
Hot Reloading with tsx
Instead of ts-node-dev, I chose tsx:
- Fully supports ESM + TypeScript 5+
- Zero configuration
- Hot reload built-in
Development script in package.json:
"dev": "tsx watch src/server.ts"SQLite for Demos
SQLite was chosen for its simplicity:
- No external server needed
- Works cross-platform
- Perfect for testing and demos
Switching to PostgreSQL, MySQL, or MariaDB in production only requires changing the TypeORM DataSource configuration.
REST API Example
The template includes basic CRUD for a User entity:
| Method | Endpoint | Description |
|---|---|---|
| GET | /users | List all users |
| POST | /users | Create a user |
| GET | /users/:id | Get user by ID |
| DELETE | /users/:id | Delete a user |
All routes are defined in routes/user.routes.ts and implemented in controllers/user.controller.ts. This separation makes the code easy to read, maintain, and extend.
Reusability
To make this template reusable:
- Use it as a GitHub template repository
- Keep
.env.examplefor environment variables - Use placeholder names in
package.json - Remove demo database files before using as a template
This way, you can quickly generate new backend projects with the same architecture and tooling.
Best Practices Implemented
- Separation of concerns — app, server, routes, controllers, entities
- ESM + TypeScript best practices — explicit types,
reflect-metadataloaded first - Hot reload for fast development
- Scalable folder structure for growing projects
- Minimal external dependencies — only what’s necessary for a backend API
Conclusion
This Node.js backend template provides a modern, scalable starting point for any project. It balances simplicity with professional practices, uses ESM + TypeScript effectively, and can be easily extended for production-level features like authentication, services, or complex database models.
By using this template, you can focus on building your application's business logic instead of boilerplate setup — all while learning the best practices for a clean, maintainable Node.js backend.
Check out the full template on GitHub.