Dynamic Homepage and Personalization
Description / Background
Get personalized item recommendations based on the specific user you select. With this use case, Amazon Personalize automatically filters out items that the user purchased based on Purchase events and also the userId that you specify.
We expect that the result will be different for each user with the “Dynamic Homepage Personalization” feature. The difference will be based upon searches, views, country, and bookings on our platform.
Glossary
https://app.clickup.com/9003122396/docs/8ca1fpw-35796/8ca1fpw-41516
Objectives
- User can find their personalized recommendation on the
Recommended For Yousection - We use AWS Recommended For You for the system
- Phase 1:
- Registered user feature:
- User can see the customized restaurant list on
Recommended For You - Get user behavior data from restaurant view and booking
- User can see the customized restaurant list on
- Registered user feature:
- Phase 2:
- Adding recommendation based on user country
- The default country will be Bangkok for user outside Thailand or Singapore
- Rewrite the API from REST to GraphQL
- Add new country parameters when the user is not from Thailand or Singapore
- Add cache on app layer using Aiven
- Change the transaction database from PostgreSQL to Aiven
- Use Cloudflare R2 for object storage
- Add Nuxt Tailwind template for the admin dashboard
Scope
Homepage, Restaurant detail page
Sequence Diagram / Flow
Flow Homepage Personalization:

Backend Implementation
- Created hh-felidae monorepo using Turborepo with microservices architecture containing Jaguar (admin dashboard), Puma (search API), and Tiger (backend API) applications
- Implemented comprehensive admin dashboard using Nuxt.js 3 with vue-shadcn/ui, TailwindCSS, TanStack Query Vue for data fetching, and URQL GraphQL client
- Built homepage management system with full CRUD operations for homepages, sections, banners, home icons, and tag sections with drag-and-drop reordering
- Implemented TranslationManager component for multi-language content management across all entities
- Created reusable UI components including file upload with drag-and-drop, city selector with multi-select, and dynamic form validation using vee-validate and Zod
- Set up GraphQL API using Fastify and Mercurius with auto-generated TypeScript types from centralized schema definitions
- Implemented Drizzle ORM with multi-database support (db1: external, db2: data warehouse, db3: core application) with separate configurations
- Created BullMQ job queue system with Redis for async background processing and data pipeline tasks
- Integrated OpenSearch for search and analytics capabilities with proper indexing and query support
- Set up Apache Kafka for event streaming and real-time data pipeline processing
- Implemented Better Auth authentication solution across all services for secure admin and API access
- Configured AWS SDK integration for cloud services and file storage operations
- Created comprehensive seed data system with Faker.js for development and testing environments
- Set up Docker Compose local development infrastructure with Redis, Kafka, OpenSearch, and OpenSearch Dashboard services
- Implemented environment configuration validation using env-schema with proper type safety
- Created Metarank ML-powered personalization engine integration for restaurant recommendations
- Set up GrowthBook feature flagging platform for A/B testing and gradual feature rollouts
- Implemented hh-personalize GraphQL API service using Fastify with Amazon Personalize integration for “Recommended For You” functionality
- Created event tracking system for user behavior (restaurant views, bookings, purchases) to feed AWS Personalize training datasets
- Set up index reset and data reseeding utilities for OpenSearch restaurant data management
- Configured proper CORS, authentication plugins, and caching strategies across all Fastify services
- Implemented file-based routing in Nuxt with middleware for route protection and user session management
- Added
jaguar_hostadmin setting in hh-server AdminSetting model with default URL configuration and documentation for Jaguar dashboard host - Implemented URL normalization method in AdminSetting to handle trailing slashes and whitespace for safe Jaguar host URLs
- Updated admin navbar in hh-server to redirect “Homepage Setting” menu link to Jaguar dashboard
/homepagesroute whenjaguar_hostis configured - Configured external link security with
noopener noreferrerattributes and new tab opening for Jaguar dashboard links - Implemented fallback to internal restaurant sections path when Jaguar host is not configured in admin settings
PR #5 - Feat/homepage-dashboard
PR #7427 - homepage setting redirect url
AWS Personalize - Recommended For You
Frontend Implementation
- Added
NUXT_PUBLIC_PERSONALIZE_DOMAINenvironment variable in.env.examplefor personalization service configuration - Created
RecommendedForYou.vuecomponent (34 lines) for displaying personalized restaurant recommendations section - Implemented
RecommendedForYouView.vue(156 lines) with complete logic for fetching and rendering recommended restaurants using GraphQL - Integrated “Recommended For You” section into
HomeBody.astrohomepage template with conditional rendering for logged-in users and tag section support - Added
getRecommendedForYouGraphql.tsAPI module (133 lines) with GraphQL queries for fetching personalized restaurant data from hh-personalize service - Created personalized GraphQL client in
urql.tswith dedicated configuration for personalization domain and proper authentication headers, consolidated tocreateTigerUrqlClientfor unified access - Updated GraphQL client configurations for
getRestaurantTagsGraphql.ts,searchSuggestionGraphql.ts, andsearchRestaurantGraphql.tsto use modular client creation - Implemented session ID management utility in
sessionId.ts(25 lines) with localStorage persistence and UUID generation for tracking user sessions - Added
SESSION_ID_KEYconstant toconstants.tsfor consistent session storage key across the application - Created comprehensive event tracking system in
trackEvents.ts(44 lines) with support for VIEW and PURCHASE event types to AWS Personalize - Implemented
baseTrackEvents.ts(27 lines) base function for sending personalized events to hh-personalize API with proper payload formatting, updated to use Tiger URQL client - Enhanced
BookingConfirmation.vueto integrate user tracking for completed purchase events with session ID and user ID, migrated to use HH-Tiger banner API and HomeBanner type - Added user behavior tracking to
RestaurantDetailEntry.vue(14 lines) for capturing restaurant view events with restaurant ID, user ID, and session ID - Defined TypeScript event types in
EventType.ts(33 lines) includingPersonalizeEventInput,ViewEvent,PurchaseEventinterfaces with proper type safety - Configured proper error handling and validation for mandatory parameters (sessionId, userId, restaurantId) in analytics tracking functions
- Integrated automatic event sending on restaurant detail page visits and booking confirmations to feed AWS Personalize training datasets
- Migrated banners/icons to HH-Tiger GraphQL API, replacing old REST endpoints with GraphQL queries
- Introduced
HomeBannerinterface inBanner.tsto unify banner types across components - Refactored
getBanner.tsto implement GetBanners via Tiger GraphQL schema (+61/-88 lines) - Updated
getDeals.tsto implement GetHomeIcons via Tiger GraphQL (+51/-70 lines) - Created
getHomeTagSection.ts(105 lines) new API to fetch tag section items with country-aware banners - Added
getHomepageListGraphql.ts(221 lines) with comprehensive GraphQL homepage sections APIs for fetching layout data - Simplified homepage cache logic by removing cache clearing in
home-page.tsAPI (+15/-24 lines) - Replaced cached layout with direct GraphQL fetch in
layout.tsAPI (+5/-64 lines) - Updated
HomePromotion.vueto fetch promotion banners via new service with simplified props (+11/-46 lines) - Modified
HomeTagIcon.vueto fetch home icons by cityId using global isDesktop utility (+7/-20 lines) - Enhanced
BenefitPromos.vueto migrate member promos to section banner GraphQL (+27/-40 lines) - Updated
PromotionCard.vueto adapt to HomeBanner image fields (+4/-7 lines) - Refactored
homeSection.tsservice to use GraphQL for promotions/icons with HomeBanner integration (+15/-25 lines) - Added HH-Tiger fallback for group landing data in
groupLanding.tsservice (+102/-12 lines) - Updated server-side rendering components:
Home.astroto use hero banner via GraphQL with countryCode (+32/-53 lines),HomePromotion.astroto server-fetch promotions (+55/-12 lines),HomeTagIcon.astroto server-fetch icons by cityId (+6/-20 lines) - Created
TopCuisineSorted.astro(48 lines) component to render sorted tag section - Simplified
HomeBanner.astroprops to use direct URLs (+7/-19 lines) - Updated
url.tsutility to skip CDN rewrite for tiger-hosted assets - Modified
growthbook.tsto switch GrowthBook events to Tiger URQL client - Removed deprecated layout cookie cleanup from
middleware.ts
PR #1363 - personalization v2 CU-86cx6d57u
PRD & Task
https://app.clickup.com/t/86cx5qhc1
https://app.clickup.com/t/86cza5hjz Dynamic homepage PRD: https://docs.google.com/document/d/1MgRrTpqVCJA0Oa0QmkBsJn-IvSvjp9iaEhJTFccb_44/edit
API Blueprint
How to get Grapghql query:
- install graphql network extension on chrome
- Open Hungryhub Webview, click recomended for you or restaurant detail
- It will show the query when you inspect the page element
- copy the query and insert in to https://personalize.hh-engineering.my.id/graphiql to check the response
| Method | Path | URL | Description | Payload |
|---|---|---|---|---|
| GET | /graphql | staging: https://hh-personalize.hh-engineering.my.id/ | get recomended for you data for registered user | |
| POST(?) | /services/analytics/track_events | Sent data event when user open detail restaurant and purchase |
DB Schema / Database Migration
Database migrations are managed through hh-server and related services.
Improvement:
| Feature Name | Date | What Changed | Description |
|---|---|---|---|
| phase 1 | 3 Feb 2025 | initial document | - |