DEV Community

Cover image for Dream Tour Management Backend Development – Part 1: Foundational Setup
Alifa Ara Heya
Alifa Ara Heya

Posted on

Dream Tour Management Backend Development – Part 1: Foundational Setup

This is a progress recap of Part 1 of the backend development for the DreamTourManagement system. The goal was to set up a clean, scalable, and production-ready Express.js architecture using TypeScript, Zod, and modular patterns.


πŸš€ Step-by-Step Implementation Journey

Here’s the logical order in which the backend was built.


βœ… Step 1: Core Application Setup

This step established the basic server infrastructure.

  • Express Server (app.ts)

    The Express app was initialized with essential middleware:

    • express.json() for parsing JSON request bodies
    • cors() for enabling cross-origin requests
  • Database Connection (server.ts)


    Mongoose was configured to connect to MongoDB. The server only starts if the database connection succeeds.

  • Environment Configuration (app/config/env.ts)


    A type-safe system was created to manage environment variables like PORT, DB_URL, etc., ensuring better security and easier environment switching (dev, prod).


πŸ‘€ Step 2: Building the First Feature Module – User

The user module was used as a pattern for all future modules. This modular architecture helps in scaling and maintaining the app.

  • Interface (user.interface.ts)

    Defined the IUser interface with proper typing for consistent and type-safe data across the app.

  • Model (user.model.ts)

    Created a Mongoose userSchema with field rules like required and unique constraints.

  • Service (user.service.ts)

    Contains business logic like User.create() and User.find(). It acts as the β€œbrain” behind each module.

  • Controller (user.controller.ts)

    Acts as the middle layer between HTTP requests and services. Handles incoming data and formats outgoing responses.

  • Route (user.route.ts)

    Sets up REST endpoints such as POST /register and GET /all-users and connects them to controller functions.


🧱 Step 3: Architecting for Scalability

With the base module built, scalable patterns were introduced.

  • Centralized Routing (routes/index.ts)

    All module routes are registered in one place. New routes can be added simply by adding them to a moduleRoutes array.

  • Standardized Response Utility (utils/sendResponse.ts)

    A utility to send all success responses in a uniform structure:

  {
    success,
    statusCode,
    message,
    data,
    meta
  }
Enter fullscreen mode Exit fullscreen mode

⚠️ Step 4: Robust Error Handling System

Handling errors effectively is key to building a stable application.

  • Async Wrapper (utils/catchAsync.ts)
    A higher-order function that wraps async controllers and automatically forwards errors to next(). This eliminates repetitive try...catch blocks.

  • Custom Error Class (errorHelpers/appError.ts)
    Extends the built-in Error class to support custom status codes. Helpful for throwing HTTP-aware errors like new AppError(404, "User not found").

  • Global Error Handler (middlewares/globalErrorHandler.ts)
    Catches and formats all errors into a consistent JSON response.

  • Not Found Handler (middlewares/notfound.ts)
    Captures all undefined routes and sends a clean 404 Not Found response.


πŸ” Step 5: Server Stability & Graceful Shutdown

In server.ts, listeners were added for:

  • unhandledRejection
  • uncaughtException
  • SIGTERM

These allow the server to exit cleanly in case of unexpected errors or shutdown signals β€” crucial for production readiness.


πŸ”„ The Complete Request Flow (Example: POST /api/v1/user/register)

Let’s break down what happens from request to response.

πŸ“₯ Request Flow:

  1. A client sends a POST request with user data to /api/v1/user/register.

  2. app.ts:

  • express.json() parses the JSON body.
  • The route /api/v1/user is forwarded to the router from routes/index.ts.
  1. routes/index.ts:
  • The router matches /user and forwards to userRoutes.
  1. user.route.ts:
  • The POST /register route is matched.
  • Input validation happens here using validateRequest middleware with a Zod schema.
   router.post(
     "/register",
     validateRequest(UserValidations.createUserValidationSchema),
     UserControllers.createUser
   );
Enter fullscreen mode Exit fullscreen mode
  1. user.controller.ts:
  • The createUser controller is wrapped in catchAsync.
  • It receives the validated req.body, calls UserServices.createUser(), and sends a formatted success response via sendResponse.
  1. user.service.ts:
  • Handles the actual database logic with User.create() and returns the new user.
  1. user.model.ts:
  • Mongoose validates the data and saves it to the MongoDB users collection.

⚠️ Error Flow:

  1. If Zod validation fails β†’ validateRequest calls next(error).

  2. If Mongoose fails (e.g., duplicate email) β†’ the promise rejects.

  3. catchAsync catches and forwards the error to Express.

  4. globalErrorHandler.ts receives the error and responds with:

{
  success: false,
  message: "Detailed error message",
  stack: "only in development"
}
Enter fullscreen mode Exit fullscreen mode

🧠 Summary

In Part 1, the focus was on building a professional-grade foundation that supports:

  • 🧩 Modular design
  • βœ… Consistent responses
  • πŸ§ͺ Centralized validation and error handling
  • 🧱 Scalable route management
  • 🧯 Stable production-ready behavior

With this groundwork in place, the application is ready to move on to authentication, authorization, OTP, and role-based access in the next part.


Coming up in Part 2:
πŸ” Authentication with JWT
πŸ“¨ OTP system
πŸ›‚ Role-based Access Control (Admin, Guide, User)
🧾 Guide approval workflows

Stay tuned!

#nodejs #typescript #expressjs #zod #backend #webdev #cleancode #dreamtourmanagement #programminghero

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.