devnotes.

Structure my Application

A single server.js file works fine for a demo, but as an Express app grows, splitting routes, controllers, models, and configuration into folders keeps things sane.

Why structure matters

  • Organization: related files live together.
  • Maintainability: easier to find and change code.
  • Team-friendly: multiple developers can work on different folders without stepping on each other.
  • Scalability: new features slot in without cluttering existing files.

A workable folder layout

bookstore-api/
├── config/
├── controllers/
├── database/
├── middleware/
├── models/
├── public/
├── routes/
├── services/
├── tests/
├── utils/
├── views/
├── .env
├── app.js
├── server.js
├── package.json
└── README.md

config/ — environment and connection settings.

module.exports = {
    dbUrl: process.env.DB_URL || "mongodb://localhost:27017/bookstore",
};

controllers/ — request handling logic, kept out of route files.

exports.listBooks = (req, res) => {
    res.status(200).json([
        { id: 1, title: "Atomic Habits" },
        { id: 2, title: "Deep Work" },
    ]);
};

database/ — connection setup.

const { Pool } = require("pg");

const pool = new Pool({
    host: "localhost",
    user: "admin",
    password: "",
    database: "bookstore",
});

module.exports = pool;

.env — secrets and environment-specific values, never committed to source control.

DB_URL=mongodb://localhost:27017/bookstore
JWT_SECRET=super-secret-key

middleware/ — reusable request/response logic such as auth checks.

module.exports = (req, res, next) => {
    const token = req.headers["authorization"];
    if (!token) return res.status(403).send("No token provided");
    next();
};

models/ — shape of your data.

const bookSchema = {
    id: 1,
    title: "Atomic Habits",
    author: "James Clear",
};
module.exports = bookSchema;

public/ — static assets served directly to the client.

routes/ — map URLs to controller functions.

const express = require("express");
const router = express.Router();
const bookController = require("../controllers/bookController");

router.get("/books", bookController.listBooks);

module.exports = router;

services/ — business logic kept separate from controllers.

exports.findBookById = (id) => {
    return { id, title: "Atomic Habits" };
};

tests/ — unit and integration tests.

utils/ — small reusable helpers.

module.exports = (message) => {
    console.log(`[LOG]: ${message}`);
};

views/ — templates for a server-rendered app (EJS, Pug, etc.).

server.js — the entry point that starts listening.

const app = require("./app");
const PORT = process.env.PORT || 4000;

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

app.js — assembles middleware and routes, then exports the app.

const express = require("express");
const app = express();

app.use(express.json());
app.use("/", require("./routes/bookRoutes"));

module.exports = app;

package.json — dependencies, scripts, metadata.

README.md — setup and usage instructions for anyone new to the project.

Structure my Application | DevNotes