Isen Kasa

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.

nodejstypescripttypeorm

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:

  1. Modern JavaScript / TypeScript setup

    • Use ES modules (import / export) for forward compatibility and consistency with modern tooling.
    • Use TypeScript for type safety and maintainability.
  2. Scalable folder structure

    • Separate concerns (routes, controllers, entities, app, server).
    • Make it easy to extend with services, middleware, authentication, or additional modules.
  3. Developer-friendly workflow

    • Hot reload during development with minimal setup.
    • Lightweight database (SQLite) for quick demos and prototyping.
  4. 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.

Technology Choices

Here’s why I chose each technology:

TechnologyReasoning
Node.jsLightweight and widely used for backend development.
TypeScriptAdds type safety, reduces runtime errors, and works well with decorators used by TypeORM.
ESM modulesModern module system; avoids common issues with require vs import and is future-proof.
ExpressMinimal web framework; easy to configure routes, middlewares, and request handling.
TypeORMA popular ORM that works well with TypeScript decorators, supports SQLite, and can easily switch to other DBs later.
SQLiteZero-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?

  1. app.ts – only configures Express, middlewares, and routes. No database logic here.
  2. server.ts – handles database initialization and starts the server. This separation allows importing app in tests without starting the server.
  3. controllers/ – all business logic lives here. Keeps endpoints clean and easy to test.
  4. routes/ – maps endpoints to controllers. Makes it easy to see all routes in one place.
  5. 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: true in 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:

MethodEndpointDescription
GET/usersList all users
POST/usersCreate a user
GET/users/:idGet user by ID
DELETE/users/:idDelete 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.example for 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

  1. Separation of concerns — app, server, routes, controllers, entities
  2. ESM + TypeScript best practices — explicit types, reflect-metadata loaded first
  3. Hot reload for fast development
  4. Scalable folder structure for growing projects
  5. 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.