Express.js + TypeScript Industrial Project Structure (2025)

In small Express.js apps, everything often lives inside a single file:

server.js

But in real-world applications — especially with TypeScript — a good folder structure is essential because it gives you:

  • Clean separation of responsibilities
  • A place for each layer (routes, controllers, services, db, validation)
  • Easier debugging
  • Easier testing
  • Easier scalability
  • Predictability for future developers

Express is extremely flexible, which is good — but it also means beginners get confused.
This guide shows the most common and practical structure used in the industry.


⭐ 1. The Real Industrial Standard (Used by ~90% of Express + TS teams)

The following structure is simple, clear, and scalable. It works for small apps and grows with your project: This structure is called Feature-based Architecture (a.k.a. vertical slices)

project/
│
├── src/
│ ├── app.ts
│ ├── server.ts
│ │
│ ├── config/
│ │ └── env.ts
│ │
│ ├── db/
│ │ └── prisma.ts (or db.ts for SQLite/Knex/Sequelize)
│ │
│ ├── common/
│ │ ├── middleware/
│ │ │ ├── errorHandler.ts
│ │ │ └── validate.ts
│ │ └── utils/
│ │ └── asyncHandler.ts
│ │
│ ├── modules/
│ │ └── todos/
│ │ ├── todo.routes.ts
│ │ ├── todo.controller.ts
│ │ ├── todo.service.ts
│ │ ├── todo.repository.ts
│ │ ├── todo.schema.ts
│ │ └── todo.types.ts
│ │
│ └── types/
│ └── global.d.ts
│
├── prisma/
│ └── schema.prisma
│
├── .env
├── package.json
├── tsconfig.json
└── README.md

The above project structure is a very good representation of modern industrial best practices for building scalable applications with Express and TypeScript.

It uses a module-based (or feature-based) architecture, where all the code related to a specific feature (like todos) is grouped together. This includes routes, controllers, services, and data access layers.

This approach is highly favored in the industry for larger applications because it:

  • Is Scalable: It’s easy to add or remove features without affecting other parts of the application.
  • Is Maintainable: Code is easier to find and reason about since related files are co-located.
  • Promotes High Cohesion: Keeps related logic tightly packed.

While some projects use a “layer-based” structure (e.g., a single controllers folder with all controllers), the modular approach are widely considered a more robust and scalable standard for professional development. The above structure is an example of a Feature-based Architecture, often referred to as vertical slices.

The core idea is to group all related components (routes, controllers, services, repositories, schemas, types) for a specific feature (like todos) into its own module.

Regarding “100% self-contained”: While the goal of this architecture is to make features as independent as possible, “100% self-contained” is a very strong claim and rarely achievable in practice. Features will still rely on shared infrastructure like:

  • Common utilities (src/common/utils)
  • Middleware (src/common/middleware)
  • Configuration (src/config)
  • Database connections (src/db)
  • Global types (src/types)

So, while each feature is largely self-contained and encapsulates its specific logic, it still operates within the broader application context and utilizes shared foundational elements


⭐ 2. Why This Structure Is the “Most Realistic” Industrial Standard

🔹 Reason 1 — Feature-based modules

Most companies group code by feature:

modules/
  todos/
  users/
  orders/

This is cleaner than grouping by technical layers (routes/, controllers/ etc.).

🔹 Reason 2 — Separate app.ts and server.ts

  • app.ts configures Express
  • server.ts actually starts the server

This separation allows:

  • Testing app without starting server
  • Cleaner bootstrapping
  • Easier deployment

🔹 Reason 3 — Dedicated config folder

Environment handling is critical in real projects:

config/
  env.ts

You validate environment variables with Zod.

🔹 Reason 4 — A thin, simple “common” folder

Contains things widely reused:

common/middleware
common/utils
common/errors (optional)

🔹 Reason 5 — Modular, layered design inside each feature

Each feature (like todos) has:

router → defines endpoints
controller → handles request/response
service → business logic
repository → database queries
schema → Zod validation
types → TS interfaces

This mirrors clean architecture principles without over-engineering.


⭐ 3. How This Structure Compares to FastAPI

We can use horizontal or layered architecutre which i used with FastAPI. Every layer contains all features:

  • All routes inside web/
  • All schemas inside schemas/
  • All services inside services/
  • All repositories inside repository/

✔ This is a real industrial pattern: It is used widely in FastAPI, Django, Laravel, and simple Express projects.

  • routers
  • schemas (Pydantic)
  • services
  • repository
  • db
  • app/main

Express.js + TypeScript equivalent:

FastAPIExpress.js + TS
main.pyserver.ts
app initializationapp.ts
routersroutes.ts
Pydantic schemasZod schemas
servicesservice.ts
repositoryrepository.ts
modelsPrisma/ORM models
DB sessionprisma.ts/db.ts

The patterns are extremely similar. This structure feels natural to FastAPI developers.

Which One Is Industrial?

Both are. But they are used for different project sizes.

ArchitectureIndustry Usage
Layered Architecture (a.k.a. horizontal architecture)Small → medium projects, FastAPI apps, monoliths
Feature-based (modules/)Medium → large projects, microservices, TypeScript Express apps

Neither is wrong. Both are clean architecture styles.

⭐ Why Express.js Often Uses Feature-Based (modules)

TypeScript projects tend to grow quickly. Industries prefer:

modules/
  auth/
  todos/
  orders/

Because:

  • everything for 1 feature is in one place
  • easier to delete or move modules
  • easier for big teams
  • easier to migrate to microservices

But again: This does NOT mean your FastAPI structure is wrong.


⭐ 4. How Your FastAPI Structure Would Look in Express.js

If we translate fastapi structure EXACTLY into Express, it would be:

src/
│── web/             (routes)
│── schemas/         (Zod schemas)
│── services/        (todo.service.ts, user.service.ts)
│── repository/      (todo.repo.ts, user.repo.ts)
│── db/              (db.ts / prisma.ts)
app.ts
server.ts

Yes — this is completely valid for Express.js. Many companies use this “layered” structure.


⭐ 5. How This Differs from DeepSeek’s Enterprise Structure suggestion

DeepSeek’s version added:

  • CI/CD
  • workflows folder
  • docs folder
  • rate limiter
  • helmet/cors/compression
  • versioned API structure
  • full logging system
  • global error classes
  • pagination utilities
  • seeders + migrations
  • graceful shutdown logic
  • full testing structure
project/
│
├── 📁 .github/                    # GitHub workflows
│   └── 📁 workflows/
│       ├── ci.yml
│       └── cd.yml
│
├── 📁 docs/                       # API documentation
│   ├── api.md
│   └── setup.md
│
├── 📁 src/
│   ├── main.ts                    # Server entry point
│   ├── app.ts                     # Express app configuration
│   │
│   ├── 📁 common/                 # Shared utilities
│   │   ├── errors/                # Custom error classes
│   │   │   ├── AppError.ts
│   │   │   ├── HttpStatus.ts
│   │   │   └── index.ts
│   │   ├── middleware/            # Global middleware
│   │   │   ├── errorHandler.ts
│   │   │   ├── validation.ts
│   │   │   ├── rateLimiter.ts
│   │   │   └── logger.ts
│   │   └── utils/
│   │       ├── apiResponse.ts
│   │       └── helpers.ts
│   │
│   ├── 📁 config/                 # Configuration management
│   │   ├── environment.ts
│   │   ├── database.ts
│   │   └── constants.ts
│   │
│   ├── 📁 modules/                # Feature-based modules (RECOMMENDED)
│   │   └── 📁 todos/              # Todo feature module
│   │       ├── todo.routes.ts
│   │       ├── todo.controller.ts
│   │       ├── todo.service.ts
│   │       ├── todo.repository.ts
│   │       ├── todo.schema.ts     # Zod validation
│   │       ├── todo.types.ts      # TypeScript interfaces
│   │       └── todo.test.ts
│   │
│   ├── 📁 db/                     # Database layer
│   │   ├── connection.ts
│   │   ├── migrations/            # If using raw SQL
│   │   └── seeders/               # Test data
│   │
│   └── 📁 types/                  # Global TypeScript types
│       ├── express.d.ts           # Express type extensions
│       └── global.d.ts
│
├── 📁 tests/                      # Test suites
│   ├── unit/
│   ├── integration/
│   └── fixtures/
│
├── 📁 public/                     # Static files
│   └── 📁 uploads/                # File uploads
│
├── .env.example
├── .env
├── package.json
├── tsconfig.json
├── docker-compose.yml             # For local development
├── Dockerfile
├── jest.config.ts                 # Testing framework
└── README.md

✔ All of these are valid: But they are enterprise-level and unnecessary for learning.

❌ Too heavy for small projects: Your goal right now is to learn Express, not build a microservice architecture.

  • Industrial
  • Used by 90% of teams
  • Much simpler
  • Easier to re-use
  • Easier to modify
  • Perfect for small/medium apps

⭐ 6. Folder-by-Folder Explanation (Easy to Understand) For Featured based Architecture

🔷 src/app.ts

Creates and configures the Express app:

  • middleware
  • routes
  • JSON parser
  • CORS
  • error handler

🔷 src/server.ts

Starts the server listening on a port.

Useful in tests because you can test app without running the server.


🔷 src/config/

Environment and configuration logic.

Example:

env.ts → validates and exports env vars using Zod

🔷 src/db/

Responsible for database connection:

prisma.ts → new PrismaClient()

or

sqlite.ts → sqlite3 db connection

🔷 src/common/

Shared functions that don’t belong to any feature.

  • middleware/ → error handling, validation
  • utils/ → reusable helpers

🔷 src/modules/

This is where the real application lives.

Each feature is isolated.

Example:

todos/
  todo.routes.ts
  todo.controller.ts
  todo.service.ts
  todo.repository.ts
  todo.schema.ts
  todo.types.ts

This follows the same separation as FastAPI.


🔷 src/types/

Global TypeScript definitions.


⭐ 7. When to Expand This Structure

When your project grows, you can add:

For medium projects:

tests/
docs/
public/

For enterprise scale:

.github/workflows/
migrations/
seeders/
global error classes
request logging system
rate limiters
role-based auth
modular versioned APIs (v1, v2)

⭐ 8. Summary — The Best Structure for Learning + Real Jobs

✔ Use the simplified industrial structure

Not heavy weight version.

✔ This structure matches FastAPI logic

So it will feel familiar.

✔ This structure is used by the majority of companies

Not too small, not too enterprise.

✔ Perfect for:

  • learning
  • building real apps
  • preparing for interviews
  • future scalability

Related Articles

  1. Express Js Lesson 1 – Introduction
Scroll to Top