[{"data":1,"prerenderedAt":724},["ShallowReactive",2],{"navigation_docs":3,"-getting-started-backend":100,"-getting-started-backend-surround":719},[4,35,80,90],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":6},"Getting Started",false,"\u002Fgetting-started","1.getting-started",[10,15,20,25,30],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F2.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F3.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Project Structure","\u002Fgetting-started\u002Fproject-structure","1.getting-started\u002F4.project-structure","i-lucide-folder-tree",{"title":26,"path":27,"stem":28,"icon":29},"Backend Server","\u002Fgetting-started\u002Fbackend","1.getting-started\u002F6.backend","i-lucide-server",{"title":31,"path":32,"stem":33,"icon":34},"Troubleshooting","\u002Fgetting-started\u002Ftroubleshooting","1.getting-started\u002F8.troubleshooting","i-lucide-wrench",{"title":36,"icon":6,"path":37,"stem":38,"children":39,"page":6},"API","\u002Fapi","2.api",[40,45,50,55,60,65,70,75],{"title":41,"path":42,"stem":43,"icon":44},"API Overview","\u002Fapi\u002Foverview","2.api\u002F1.overview","i-lucide-zap",{"title":46,"path":47,"stem":48,"icon":49},"TMDB Integration","\u002Fapi\u002Ftmdb-proxy","2.api\u002F2.tmdb-proxy","i-si-movie-line",{"title":51,"path":52,"stem":53,"icon":54},"Watched and My List APIs","\u002Fapi\u002Fwatched-movies","2.api\u002F3.watched-movies","i-lucide-eye",{"title":56,"path":57,"stem":58,"icon":59},"Recommendation Generation","\u002Fapi\u002Fgemini-recommendations","2.api\u002F4.gemini-recommendations","i-lucide-sparkles",{"title":61,"path":62,"stem":63,"icon":64},"Movie Search API","\u002Fapi\u002Fmovie-search","2.api\u002F5.movie-search","i-lucide-search",{"title":66,"path":67,"stem":68,"icon":69},"TMDB Import","\u002Fapi\u002Ftmdb-import","2.api\u002F6.tmdb-import","i-lucide-database",{"title":71,"path":72,"stem":73,"icon":74},"Movie Details & Caching","\u002Fapi\u002Fmovie-details","2.api\u002F7.movie-details","i-lucide-film",{"title":76,"path":77,"stem":78,"icon":79},"Recommendations (Cached)","\u002Fapi\u002Frecommendations-cache","2.api\u002F8.recommendations-cache","i-lucide-brain",{"title":81,"icon":6,"path":82,"stem":83,"children":84,"page":6},"Frontend","\u002Ffrontend","4.frontend",[85],{"title":86,"path":87,"stem":88,"icon":89},"Components","\u002Ffrontend\u002Fcomponents","4.frontend\u002F1.components","i-lucide-layout",{"title":91,"icon":6,"path":92,"stem":93,"children":94,"page":6},"Reference","\u002Freference","5.reference",[95],{"title":96,"path":97,"stem":98,"icon":99},"Rate Limiting","\u002Freference\u002Frate-limiting","5.reference\u002F2.rate-limiting","i-lucide-shield",{"id":101,"title":26,"body":102,"description":712,"extension":713,"links":714,"meta":715,"navigation":716,"path":27,"seo":717,"stem":28,"__hash__":718},"docs\u002F1.getting-started\u002F6.backend.md",{"type":103,"value":104,"toc":700},"minimark",[105,114,119,122,132,136,317,326,334,337,372,375,389,393,396,428,431,443,446,453,457,464,481,484,499,503,546,550,584,588,591,678,682,696],[106,107,108,109,113],"p",{},"The ",[110,111,112],"code",{},"app\u002Fserver\u002F"," folder contains the Nuxt server-side logic for Supabase-backed user state, recommendation generation, TMDB-backed movie lookup, and TMDB import handling.",[115,116,118],"h2",{"id":117},"architecture-overview","Architecture Overview",[106,120,121],{},"The server is organized around the API layer:",[123,124,130],"pre",{"className":125,"code":127,"language":128,"meta":129},[126],"language-text","app\u002Fserver\u002F\n├── api\u002F\n│   ├── recommend.get.ts\n│   ├── watched\u002F\n│   ├── mylist\u002F\n│   ├── movies\u002F\n│   └── admin\u002F\n└── utils\u002F\n","text","",[110,131,127],{"__ignoreMap":129},[115,133,135],{"id":134},"api-routes","API Routes",[137,138,139,158],"table",{},[140,141,142],"thead",{},[143,144,145,149,152,155],"tr",{},[146,147,148],"th",{},"Route",[146,150,151],{},"Method",[146,153,154],{},"Auth",[146,156,157],{},"Purpose",[159,160,161,180,196,214,230,246,265,282,298],"tbody",{},[143,162,163,169,174,177],{},[164,165,166],"td",{},[110,167,168],{},"\u002Fapi\u002Frecommend",[164,170,171],{},[110,172,173],{},"GET",[164,175,176],{},"Bearer token",[164,178,179],{},"Fetch cached recommendations as TMDB IDs",[143,181,182,187,191,193],{},[164,183,184],{},[110,185,186],{},"\u002Fapi\u002Frecommend\u002Fquota",[164,188,189],{},[110,190,173],{},[164,192,176],{},[164,194,195],{},"Return the authenticated user's remaining daily recommendation quota",[143,197,198,203,208,211],{},[164,199,200],{},[110,201,202],{},"\u002Fapi\u002Fauth\u002Fsignup",[164,204,205],{},[110,206,207],{},"POST",[164,209,210],{},"None",[164,212,213],{},"Validate signup input, check for an existing email, and create the account server-side",[143,215,216,221,225,227],{},[164,217,218],{},[110,219,220],{},"\u002Fapi\u002Fmovies\u002Fsearch",[164,222,223],{},[110,224,173],{},[164,226,210],{},[164,228,229],{},"Search TMDB movies for the search page",[143,231,232,237,241,243],{},[164,233,234],{},[110,235,236],{},"\u002Fapi\u002Fmovies\u002Fpopular",[164,238,239],{},[110,240,173],{},[164,242,210],{},[164,244,245],{},"Return a cached popular-movies feed from TMDB",[143,247,248,253,257,259],{},[164,249,250],{},[110,251,252],{},"\u002Fapi\u002Fmovies\u002F:id",[164,254,255],{},[110,256,173],{},[164,258,210],{},[164,260,261,262],{},"Fetch movie details and cache them in ",[110,263,264],{},"movies",[143,266,267,272,277,279],{},[164,268,269],{},[110,270,271],{},"\u002Fapi\u002Fwatched",[164,273,274],{},[110,275,276],{},"GET\u002FPOST\u002FDELETE",[164,278,176],{},[164,280,281],{},"Read\u002Fadd\u002Fremove watched movies",[143,283,284,289,293,295],{},[164,285,286],{},[110,287,288],{},"\u002Fapi\u002Fmylist",[164,290,291],{},[110,292,276],{},[164,294,176],{},[164,296,297],{},"Read\u002Fadd\u002Fremove My List entries",[143,299,300,305,309,314],{},[164,301,302],{},[110,303,304],{},"\u002Fapi\u002Fadmin\u002Ftmdb-import",[164,306,307],{},[110,308,207],{},[164,310,311],{},[110,312,313],{},"x-admin-token",[164,315,316],{},"Trigger the TMDB import manually or from GitHub Actions",[318,319,321,322,325],"callout",{"type":320},"warning","Protected routes require valid ",[110,323,324],{},"Authorization: Bearer \u003Ctoken>"," headers before any user-specific data operation.",[115,327,329,330,333],{"id":328},"recommendation-flow-get-apirecommend","Recommendation Flow (",[110,331,332],{},"GET \u002Fapi\u002Frecommend",")",[106,335,336],{},"Key behavior:",[338,339,340,344,347,350,353,356,359,369],"ul",{},[341,342,343],"li",{},"Requires Bearer auth",[341,345,346],{},"Reads watched history from Supabase",[341,348,349],{},"Reads My List from Supabase",[341,351,352],{},"Builds a compact taste profile from watched movies and My List",[341,354,355],{},"Calls the AI client when regeneration is needed",[341,357,358],{},"Validates AI candidates against watched movies, previous recommendations, TMDB resolution, and My List limits",[341,360,361,362,365,366],{},"Stores valid recommendations in Supabase table ",[110,363,364],{},"recommendations"," as ",[110,367,368],{},"tmdb_ids",[341,370,371],{},"Reuses cached recommendations for 7 days if watch history hash is unchanged",[106,373,374],{},"Query flags:",[338,376,377,383],{},[341,378,379,382],{},[110,380,381],{},"?refresh=true"," forces regeneration",[341,384,385,388],{},[110,386,387],{},"?getNew=true"," forces regeneration and excludes prior cached recommendations from the next result set",[115,390,392],{"id":391},"current-supabase-model","Current Supabase Model",[106,394,395],{},"The current implementation relies on four Supabase tables:",[338,397,398,403,412,421],{},[341,399,400,402],{},[110,401,264],{}," stores the imported TMDB information and cached movie-detail fields",[341,404,405,408,409],{},[110,406,407],{},"user_watched_movies"," stores one row per ",[110,410,411],{},"(user_id, tmdb_id)",[341,413,414,417,418],{},[110,415,416],{},"user_my_list"," stores one row per user with ",[110,419,420],{},"tmdb_ids integer[]",[341,422,423,425,426],{},[110,424,364],{}," stores cached recommendation IDs in ",[110,427,368],{},[106,429,430],{},"My List writes are atomic through RPC functions:",[338,432,433,438],{},[341,434,435],{},[110,436,437],{},"append_my_list(target_tmdb_id integer)",[341,439,440],{},[110,441,442],{},"remove_my_list(target_tmdb_id integer)",[106,444,445],{},"Signup also depends on a private RPC helper:",[338,447,448],{},[341,449,450],{},[110,451,452],{},"auth_email_exists(target_email text)",[115,454,456],{"id":455},"tmdb-access","TMDB Access",[106,458,459,460,463],{},"TMDB requests are performed internally through ",[110,461,462],{},"server\u002Futils\u002Ftmdb\u002Fclient.ts",", which currently allows only:",[338,465,466,471,476],{},[341,467,468],{},[110,469,470],{},"search\u002Fmovie",[341,472,473],{},[110,474,475],{},"movie\u002Fpopular",[341,477,478],{},[110,479,480],{},"movie\u002F:id",[106,482,483],{},"That helper is used by:",[338,485,486,491,496],{},[341,487,488],{},[110,489,490],{},"GET \u002Fapi\u002Fmovies\u002Fsearch",[341,492,493],{},[110,494,495],{},"GET \u002Fapi\u002Fmovies\u002F:id",[341,497,498],{},"recommendation title-to-ID resolution",[115,500,502],{"id":501},"important-server-utilities","Important Server Utilities",[338,504,505,511,517,522,528,534,540],{},[341,506,507,510],{},[110,508,509],{},"server\u002Futils\u002Fauth\u002Fauthorize-user.ts"," - bearer-token validation and current-user resolution",[341,512,513,516],{},[110,514,515],{},"server\u002Futils\u002Fauth\u002Fsignup.ts"," - signup validation, duplicate-email check, and server-side Supabase signup flow",[341,518,519,521],{},[110,520,462],{}," - TMDB request wrapper with rate limiting and path allow-listing",[341,523,524,527],{},[110,525,526],{},"server\u002Futils\u002Frecommendations\u002Fai-client.ts"," - OpenAI-compatible provider wrapper",[341,529,530,533],{},[110,531,532],{},"server\u002Futils\u002Frecommendations\u002Frecommendations.ts"," - taste-profile prompting, TMDB ID enrichment, and backend validation of AI candidates",[341,535,536,539],{},[110,537,538],{},"server\u002Futils\u002Ftmdb\u002Fsearch-movies.ts"," - Supabase title lookup used to map recommendation titles back to TMDB IDs",[341,541,542,545],{},[110,543,544],{},"server\u002Futils\u002Ftmdb\u002Fimport-runner.ts"," - import job runner for TMDB exports",[115,547,549],{"id":548},"utility-dependency-map","Utility Dependency Map",[338,551,552,558,576,581],{},[341,553,554,557],{},[110,555,556],{},"server\u002Futils\u002Fshared\u002F"," owns API error handling, Redis, and Supabase client factories. It must not import domain utilities.",[341,559,560,563,564,567,568,571,572,575],{},[110,561,562],{},"server\u002Futils\u002Fauth\u002F",", ",[110,565,566],{},"server\u002Futils\u002Ftmdb\u002F",", and ",[110,569,570],{},"server\u002Futils\u002Frecommendations\u002F"," may import from ",[110,573,574],{},"shared\u002F",".",[341,577,578,580],{},[110,579,570],{}," may import TMDB helpers for movie lookup and title-to-ID resolution.",[341,582,583],{},"API routes compose utilities from the domain folders and should not depend on old top-level utility paths.",[115,585,587],{"id":586},"environment-variables","Environment Variables",[106,589,590],{},"Required environment variables for the server:",[123,592,596],{"className":593,"code":594,"language":595,"meta":129,"style":129},"language-env shiki shiki-themes material-theme-lighter github-light github-dark","NUXT_TMDB_API_KEY=your_key_here\nNUXT_GOOGLE_API_KEY=your_google_ai_studio_key\nNUXT_GOOGLE_MODELS=gemini-flash-lite-latest,gemini-2.5-flash-lite,gemini-2.0-flash-lite\nNUXT_OPENROUTER_API_KEY=your_openrouter_api_key\nNUXT_OPENROUTER_MODELS=google\u002Fgemini-2.5-flash-lite\nNUXT_PUBLIC_SUPABASE_URL=your_supabase_url\nNUXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key\nNUXT_SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key\nNUXT_PUBLIC_HCAPTCHA_SITE_KEY=your_hcaptcha_site_key\nNUXT_HCAPTCHA_SECRET=your_hcaptcha_secret\nADMIN_API_TOKEN=your_admin_token\nUPSTASH_REDIS_REST_URL=your_upstash_redis_url\nUPSTASH_REDIS_REST_TOKEN=your_upstash_redis_token\n","env",[110,597,598,606,612,618,624,630,636,642,648,654,660,666,672],{"__ignoreMap":129},[599,600,603],"span",{"class":601,"line":602},"line",1,[599,604,605],{},"NUXT_TMDB_API_KEY=your_key_here\n",[599,607,609],{"class":601,"line":608},2,[599,610,611],{},"NUXT_GOOGLE_API_KEY=your_google_ai_studio_key\n",[599,613,615],{"class":601,"line":614},3,[599,616,617],{},"NUXT_GOOGLE_MODELS=gemini-flash-lite-latest,gemini-2.5-flash-lite,gemini-2.0-flash-lite\n",[599,619,621],{"class":601,"line":620},4,[599,622,623],{},"NUXT_OPENROUTER_API_KEY=your_openrouter_api_key\n",[599,625,627],{"class":601,"line":626},5,[599,628,629],{},"NUXT_OPENROUTER_MODELS=google\u002Fgemini-2.5-flash-lite\n",[599,631,633],{"class":601,"line":632},6,[599,634,635],{},"NUXT_PUBLIC_SUPABASE_URL=your_supabase_url\n",[599,637,639],{"class":601,"line":638},7,[599,640,641],{},"NUXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key\n",[599,643,645],{"class":601,"line":644},8,[599,646,647],{},"NUXT_SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key\n",[599,649,651],{"class":601,"line":650},9,[599,652,653],{},"NUXT_PUBLIC_HCAPTCHA_SITE_KEY=your_hcaptcha_site_key\n",[599,655,657],{"class":601,"line":656},10,[599,658,659],{},"NUXT_HCAPTCHA_SECRET=your_hcaptcha_secret\n",[599,661,663],{"class":601,"line":662},11,[599,664,665],{},"ADMIN_API_TOKEN=your_admin_token\n",[599,667,669],{"class":601,"line":668},12,[599,670,671],{},"UPSTASH_REDIS_REST_URL=your_upstash_redis_url\n",[599,673,675],{"class":601,"line":674},13,[599,676,677],{},"UPSTASH_REDIS_REST_TOKEN=your_upstash_redis_token\n",[115,679,681],{"id":680},"next-steps","Next Steps",[338,683,684,691],{},[341,685,686,687,690],{},"See ",[688,689,41],"a",{"href":42}," for endpoint-by-endpoint request and response details",[341,692,686,693,695],{},[688,694,21],{"href":22}," for full layout",[697,698,699],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":129,"searchDepth":608,"depth":608,"links":701},[702,703,704,706,707,708,709,710,711],{"id":117,"depth":608,"text":118},{"id":134,"depth":608,"text":135},{"id":328,"depth":608,"text":705},"Recommendation Flow (GET \u002Fapi\u002Frecommend)",{"id":391,"depth":608,"text":392},{"id":455,"depth":608,"text":456},{"id":501,"depth":608,"text":502},{"id":548,"depth":608,"text":549},{"id":586,"depth":608,"text":587},{"id":680,"depth":608,"text":681},"Overview of the current Nuxt server architecture, API routes, and Supabase data model","md",null,{},{"icon":29},{"title":26,"description":712},"LTcNvutyptcwclVORFPiQ5jujlf-h7fyiVL95pzoans",[720,722],{"title":21,"path":22,"stem":23,"description":721,"icon":24,"children":-1},"Understand the current directory structure of Movie Recommender",{"title":31,"path":32,"stem":33,"description":723,"icon":34,"children":-1},"Development setup issues and solutions",1782138811963]