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