DEV Community

Gerald Hamilton Wicks
Gerald Hamilton Wicks

Posted on

How to Create a Scalable Folder/File Structure for Your Express Application

Throughout my journey as a developer, I've encountered a variety of methods for organizing folder structures, from the most straightforward to the highly complex.

Before I explain why I prefer the following folder structure, let's delve into the motivation behind it.

Motivation

When considering an effective folder structure, it should possess the following qualities:

  1. Ease of Use: It should be simple to determine where a file or folder should be created and where to find existing ones.
  2. Consistency: Developers should consistently apply the proposed folder/file structure.

To achieve ease of use, the folder/file structure should be clear and intuitive, so developers don't spend excessive time locating or creating files and folders.

For consistency, code reviewers should ensure the structure is adhered to during code reviews. From my experience, simpler folder structures tend to be easier to follow. Complex structures can lead to confusion and misplaced files/folders due to the extra effort required to understand them.

Structure

I advocate for simple folder structures to minimize the time spent organizing files. Here’s my recommended structure:

├── src
│   ├── controllers
│   ├── routes
│   ├── models
│   ├── app.ts
Enter fullscreen mode Exit fullscreen mode

app.ts

In this file, initialize the server, connect to a database, and import routes from the /routes folder. Here’s an example:

import express from 'express';
import { usersRouter } from './routes';
import { productsRouter } from './routes';
import bodyParser from 'body-parser';
import cors from 'cors';
import mongoose from 'mongoose';
import "dotenv/config";

const app = express();
const PORT = process.env.PORT || 3001;
const DB_URI = process.env.DB_URI || 'mongodb://localhost:27017/mydatabase';

// Middleware
app.use(bodyParser.json());
app.use(cors());

// Database Connection
mongoose.connect(DB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('Connected to the database'))
  .catch(err => console.error('Database connection error:', err));

// Routes
app.use('/users', usersRouter);
app.get('/products', productsRouter);

// Server
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

/models

The models folder is crucial for defining schemas and data interactions. It includes the representation of your database tables and the logic to interact with them. Here's an example of a simple Mongoose model for a User in a file named User.model.ts:

// src/models/User.model.ts

import mongoose, { Schema, Document } from 'mongoose';

export interface IUser extends Document {
  name: string;
  email: string;
  password: string;
}

const UserSchema: Schema = new Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
});

export default mongoose.model<IUser>('User', UserSchema);
Enter fullscreen mode Exit fullscreen mode

/routes

To keep things clean, use a /routes folder. Instead of declaring all routes in app.ts, isolate them. For example, in a file named users.routes.ts:

// src/routes/users.routes.ts

import express from 'express';
import { getUsers, createUser, deleteUser, updateUser } from '../controllers/users';

export const usersRouter = express.Router();

usersRouter.get('/', getUsers);
usersRouter.post('/', createUser);
usersRouter.delete('/', deleteUser);
usersRouter.put('/', updateUser);
Enter fullscreen mode Exit fullscreen mode

/controllers

The controllers folder will likely have the most files and code. To manage this, break it down into smaller pieces. For instance:

├── controllers
│   ├── users
│   ├── products
Enter fullscreen mode Exit fullscreen mode

Within each folder, isolate each method into individual files to keep them manageable:

├── users
│   ├── getUsers.controller.ts
│   ├── createUser.controller.ts
│   ├── deleteUser.controller.ts
│   ├── updateUser.controller.ts
Enter fullscreen mode Exit fullscreen mode

Conclusion

While this file structure is simple, it's also very easy to understand and follow. This results in a concise, modularized, and clear folder structure. Remember, you can adapt the folder structure according to your needs, but it’s essential to keep it simple to maintain clarity and organization.

I'm excited to hear your thoughts—what folder structure works best for you? Let me know in the comments below! 🌟

Top comments (4)

Collapse
 
yourakshaw profile image
Ayush Kumar Shaw

Nice writeup Gerald! 👍

Have used a similar directory structure in the past, and yes, this is a good way of structuring our code and easing collaboration amongst our peers.

Collapse
 
geraldhamiltonwicks profile image
Gerald Hamilton Wicks

Thanks for your feedback and sharing your experience, that's show this structure really works haha ! Off course we should adapt this structure according to our needs but I always recommend to let it simple.

Collapse
 
yourakshaw profile image
Ayush Kumar Shaw

Here's a template that I recently curated, where I've taken inspiration from NestJS for their modular structure for their projects. I'm kind of biased towards this, since I'm a NestJS geek you might argue, haha!

microservice-template-nodejs-ts

Thread Thread
 
geraldhamiltonwicks profile image
Gerald Hamilton Wicks

Awesome, thanks for sharing !