Skip to content

Architecture

System Diagram

┌─────────────────────────────────────────────────────────────────────────┐
│                          myeasyguide.com                                │
│                    Next.js 15 — Public Frontend                          │
│                                                                          │
│   Serves: Visitors, Travelers, Suppliers, Authors, Admins                │
│   Port (dev): 3001                                                       │
└──────────────┬──────────────────────────────┬───────────────────────────┘
               │                              │
               │  NEXT_PUBLIC_API_BASE_URL     │  NEXT_PUBLIC_POST_URL
               │  (api/v1 proxied via          │  (direct fetch to CMS)
               │   Next.js rewrites)           │
               ▼                              ▼
┌──────────────────────────────┐  ┌──────────────────────────────────────┐
│   api.myeasyguide.com        │  │   cms-myeasyguide                    │
│   Express 5 — REST API       │  │   Next.js 16 — CMS                  │
│                              │  │                                      │
│   DB: walkthroughnepal       │  │   DB: cms_myeasyguide                │
│   (PostgreSQL)               │  │   (PostgreSQL)                       │
│                              │  │                                      │
│   ■ Activities               │  │   ■ Posts CRUD                      │
│   ■ Users / Auth             │  │   ■ Authors                          │
│   ■ Suppliers                │  │   ■ Categories / Subcategories       │
│   ■ Bookings / Payments      │  │   ■ AI Blog Generation (Claude)      │
│   ■ Reviews                  │  │   ■ Cities / Countries               │
│   ■ Wishlists                │  │   ■ Tags                             │
│   ■ Availability             │  │   ■ Image Upload                     │
│   ■ Featured Tags            │  │   ■ OpenAPI Spec                     │
│   ■ Newsletter               │  │                                      │
│   ■ Media Library            │  │                                      │
│   ■ Legal Documents          │  │                                      │
│   ■ Recombee ML              │  │                                      │
│   ■ Popularity Scoring       │  │                                      │
└──────────────────────────────┘  └──────────────────────────────────────┘

Data Flow

Guest-facing content

  1. Activities, trips, suppliers, bookings, reviews — The frontend fetches all commerce data from api.myeasyguide.com via NEXT_PUBLIC_API_BASE_URL (default http://localhost:3000/api/v1). Next.js rewrites() in next.config.ts proxy all /api/v1/* requests to the Express backend.

  2. Blog posts, travel guides — The frontend fetches content directly from cms-myeasyguide via NEXT_PUBLIC_POST_URL (default https://cms.myeasyguide.com). The CMS runs independently with its own database. Blog reading never touches the Express API.

  3. Search — The search page queries both backends in parallel: blogs from CMS (/api/posts/published?search=...) and activities from Express API (/activity?search=...).

  4. Sitemap — During build, next-sitemap fetches published post slugs from the CMS to include in the XML sitemap.

Admin content

  • Admin panels — The frontend has admin routes under app/admin/ that manage activities, suppliers, users, bookings, etc. via the Express API. The CMS has its own admin at app/admin/ for content management via CMS API routes.

  • The two admin panels are separate — main platform admin (activities/commerce) lives in myeasyguide.com/app/admin/, while content admin (posts/categories) lives in cms-myeasyguide/app/admin/.

URL Routing

The frontend uses a catch-all route [...slug] with a sophisticated resolution chain:

Request: /nepal/pokhara/trekking/everest-base-camp-trek-123

1. Starts with "travel-guide"? No
2. Slug ends with "-{number}"? Yes → It's an activity
   → Fetch /api/v1/activity/123 from Express API
   → Render activity detail page

Request: /travel-guide/nepal/best-time-to-visit

1. Starts with "travel-guide"? Yes → It's a CMS post
   → Fetch /api/posts/{slug} from CMS
   → Render PostPageComponent

Request: /nepal/blog/best-time-to-visit

1. Starts with "travel-guide"? No
2. Slug ends with "-{number}"? No → Could be a blog
3. Try CMS first: getPostBySlug("best-time-to-visit")
   → If found, render PostPageComponent
4. If not found, fall back to Express API legacy resolve
5. Not found? → 404

Database Independence

The API and CMS use completely separate PostgreSQL databases:

PropertyAPICMS
Database namewalkthroughnepalcms_myeasyguide
Hostlocalhostlocalhost
Port54325432

There are no cross-database foreign keys or direct database-level communication between them. They are independent systems that share the same Postgres instance but operate on separate databases.

Key Architectural Decisions

Why split the CMS from the API?

Originally, blog management (controllers, routes, validations) lived inside the Express API. As the content needs grew, this created problems:

  • Blog CRUD competed with commerce endpoints for API resources
  • Blog authors needed CMS-only features (rich text editing, scheduling, AI generation)
  • The Express API's deployment cycle was tied to commerce changes
  • Content editors had to authenticate through the same system as suppliers

The CMS was extracted as a standalone Next.js application with its own database, admin UI, and API layer. This separation allows:

  • Independent deployment and scaling
  • Tailored admin experience for content editors
  • Dedicated AI generation pipeline (Claude batch API)
  • No impact on commerce endpoints when deploying content changes

Why not a monolith?

The project started as a monolith but the editorial migration documented here is part of a larger effort to split concerns. Future migrations may include extracting supplier/activity management into the CMS or a dedicated admin app.

Built with VitePress