[{"data":1,"prerenderedAt":5826},["ShallowReactive",2],{"navigation_docs":3,"-tu-hoc-database-supabase-platform-deploy":420,"-tu-hoc-database-supabase-platform-deploy-surround":5821},[4,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":26},"Phỏng vấn .NET","i-lucide-briefcase","\u002Fphong-van","1.phong-van",[10,27,66,111,128],{"title":11,"icon":12,"path":13,"stem":14,"children":15,"page":26},"Tổng quan","i-lucide-compass","\u002Fphong-van\u002Ftong-quan","1.phong-van\u002F1.tong-quan",[16,21],{"title":17,"path":18,"stem":19,"icon":20},"Thị trường HCM","\u002Fphong-van\u002Ftong-quan\u002Fthi-truong","1.phong-van\u002F1.tong-quan\u002F1.thi-truong","i-lucide-trending-up",{"title":22,"path":23,"stem":24,"icon":25},"Lộ trình 7 ngày","\u002Fphong-van\u002Ftong-quan\u002Flo-trinh","1.phong-van\u002F1.tong-quan\u002F2.lo-trinh","i-lucide-calendar-days",false,{"title":22,"icon":25,"path":28,"stem":29,"children":30,"page":26},"\u002Fphong-van\u002Flo-trinh","1.phong-van\u002F2.lo-trinh",[31,36,41,46,51,56,61],{"title":32,"path":33,"stem":34,"icon":35},"Ngày 1 — C# cơ bản","\u002Fphong-van\u002Flo-trinh\u002Fngay-1-csharp-co-ban","1.phong-van\u002F2.lo-trinh\u002F1.ngay-1-csharp-co-ban","i-lucide-1",{"title":37,"path":38,"stem":39,"icon":40},"Ngày 2 — OOP và SOLID","\u002Fphong-van\u002Flo-trinh\u002Fngay-2-oop-solid","1.phong-van\u002F2.lo-trinh\u002F2.ngay-2-oop-solid","i-lucide-2",{"title":42,"path":43,"stem":44,"icon":45},"Ngày 3 — C# nâng cao","\u002Fphong-van\u002Flo-trinh\u002Fngay-3-csharp-nang-cao","1.phong-van\u002F2.lo-trinh\u002F3.ngay-3-csharp-nang-cao","i-lucide-3",{"title":47,"path":48,"stem":49,"icon":50},"Ngày 4 — Database và SQL","\u002Fphong-van\u002Flo-trinh\u002Fngay-4-database-sql","1.phong-van\u002F2.lo-trinh\u002F4.ngay-4-database-sql","i-lucide-4",{"title":52,"path":53,"stem":54,"icon":55},"Ngày 5 — ASP.NET Core và EF Core","\u002Fphong-van\u002Flo-trinh\u002Fngay-5-aspnet-efcore","1.phong-van\u002F2.lo-trinh\u002F5.ngay-5-aspnet-efcore","i-lucide-5",{"title":57,"path":58,"stem":59,"icon":60},"Ngày 6 — DSA coding","\u002Fphong-van\u002Flo-trinh\u002Fngay-6-dsa","1.phong-van\u002F2.lo-trinh\u002F6.ngay-6-dsa","i-lucide-6",{"title":62,"path":63,"stem":64,"icon":65},"Ngày 7 — Mock và HR","\u002Fphong-van\u002Flo-trinh\u002Fngay-7-mock-interview","1.phong-van\u002F2.lo-trinh\u002F7.ngay-7-mock-interview","i-lucide-7",{"title":67,"icon":68,"path":69,"stem":70,"children":71,"page":26},"Ngân hàng câu hỏi","i-lucide-database","\u002Fphong-van\u002Fngan-hang-cau-hoi","1.phong-van\u002F3.ngan-hang-cau-hoi",[72,77,82,87,92,97,102,107],{"title":73,"path":74,"stem":75,"icon":76},"C# (60+ câu)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fcsharp","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F1.csharp","i-lucide-hash",{"title":78,"path":79,"stem":80,"icon":81},"OOP và SOLID (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Foop","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F2.oop","i-lucide-shapes",{"title":83,"path":84,"stem":85,"icon":86},"SQL (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fsql","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F3.sql","i-lucide-database-zap",{"title":88,"path":89,"stem":90,"icon":91},"ASP.NET và EF (50+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-ef","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F4.aspnet-ef","i-lucide-server",{"title":93,"path":94,"stem":95,"icon":96},"DSA và Coding (30+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdsa-coding","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F5.dsa-coding","i-lucide-binary",{"title":98,"path":99,"stem":100,"icon":101},"HR và Soft skill (25+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fhr-behavior","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F6.hr-behavior","i-lucide-users",{"title":103,"path":104,"stem":105,"icon":106},"Design Pattern (55+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdesign-pattern","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F7.design-pattern","i-lucide-puzzle",{"title":108,"path":109,"stem":110,"icon":91},"ASP.NET MVC và Web API","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-mvc-webapi","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F8.aspnet-mvc-webapi",{"title":112,"itle":113,"icon":114,"path":115,"stem":116,"children":117,"page":26},"Tai Lieu","Tài liệu và Cheat sheet","i-lucide-book-marked","\u002Fphong-van\u002Ftai-lieu","1.phong-van\u002F4.tai-lieu",[118,123],{"title":119,"path":120,"stem":121,"icon":122},"Cheat Sheet","\u002Fphong-van\u002Ftai-lieu\u002Fcheatsheet","1.phong-van\u002F4.tai-lieu\u002F1.cheatsheet","i-lucide-clipboard-list",{"title":124,"path":125,"stem":126,"icon":127},"Nguồn tham khảo","\u002Fphong-van\u002Ftai-lieu\u002Fnguon","1.phong-van\u002F4.tai-lieu\u002F2.nguon","i-lucide-link",{"title":129,"icon":130,"path":131,"stem":132,"children":133,"page":26},"Phỏng vấn English","i-lucide-globe","\u002Fphong-van\u002Fenglish-interview","1.phong-van\u002F5.english-interview",[134,138,143,148],{"title":135,"path":136,"stem":137,"icon":130},"Tổng quan English","\u002Fphong-van\u002Fenglish-interview\u002Ftong-quan","1.phong-van\u002F5.english-interview\u002F1.tong-quan",{"title":139,"path":140,"stem":141,"icon":142},"Vocabulary","\u002Fphong-van\u002Fenglish-interview\u002Fvocabulary","1.phong-van\u002F5.english-interview\u002F2.vocabulary","i-lucide-book-a",{"title":144,"path":145,"stem":146,"icon":147},"Technical Q và A (EN)","\u002Fphong-van\u002Fenglish-interview\u002Ftechnical-en","1.phong-van\u002F5.english-interview\u002F3.technical-en","i-lucide-code",{"title":149,"path":150,"stem":151,"icon":152},"Behavioral (EN)","\u002Fphong-van\u002Fenglish-interview\u002Fbehavioral-en","1.phong-van\u002F5.english-interview\u002F4.behavioral-en","i-lucide-message-square-text",{"title":154,"icon":155,"path":156,"stem":157,"children":158},"Tự học","i-lucide-graduation-cap","\u002Ftu-hoc","2.tu-hoc",[159,161,287,310,333,357,384],{"title":154,"path":156,"stem":160,"icon":155},"2.tu-hoc\u002Findex",{"title":162,"icon":68,"path":163,"stem":164,"children":165},"Database","\u002Ftu-hoc\u002Fdatabase","2.tu-hoc\u002F1.database\u002Findex",[166,167,197,222,243,248],{"title":162,"path":163,"stem":164,"icon":68},{"title":168,"icon":91,"path":169,"stem":170,"children":171,"page":26},"SQL Server","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server","2.tu-hoc\u002F1.database\u002F2.sql-server",[172,177,182,187,192],{"title":173,"path":174,"stem":175,"icon":176},"1. Giới thiệu và Cài đặt","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F1.gioi-thieu-cai-dat","i-lucide-play-circle",{"title":178,"path":179,"stem":180,"icon":181},"2. Cú pháp cơ bản","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F2.cu-phap-co-ban","i-lucide-code-2",{"title":183,"path":184,"stem":185,"icon":186},"3. Truy vấn nâng cao","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F3.truy-van-nang-cao","i-lucide-git-merge",{"title":188,"path":189,"stem":190,"icon":191},"4. Đặc thù và Performance","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F4.dac-thu-performance","i-lucide-gauge",{"title":193,"path":194,"stem":195,"icon":196},"5. Q và A phỏng vấn","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F5.cau-hoi-phong-van","i-lucide-message-circle-question",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":26},"PostgreSQL","i-simple-icons-postgresql","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql","2.tu-hoc\u002F1.database\u002F3.postgresql",[203,206,209,212,215,218],{"title":173,"path":204,"stem":205,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":207,"stem":208,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F2.cu-phap-co-ban",{"title":183,"path":210,"stem":211,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F3.truy-van-nang-cao",{"title":188,"path":213,"stem":214,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F4.dac-thu-performance",{"title":193,"path":216,"stem":217,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F5.cau-hoi-phong-van",{"title":219,"path":220,"stem":221,"icon":96},"6. Tích hợp C# và Postgres","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftich-hop-csharp","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F6.tich-hop-csharp",{"title":223,"icon":224,"path":225,"stem":226,"children":227,"page":26},"MySQL","i-simple-icons-mysql","\u002Ftu-hoc\u002Fdatabase\u002Fmysql","2.tu-hoc\u002F1.database\u002F4.mysql",[228,231,234,237,240],{"title":173,"path":229,"stem":230,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F4.mysql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":232,"stem":233,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F4.mysql\u002F2.cu-phap-co-ban",{"title":183,"path":235,"stem":236,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F4.mysql\u002F3.truy-van-nang-cao",{"title":188,"path":238,"stem":239,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F4.mysql\u002F4.dac-thu-performance",{"title":193,"path":241,"stem":242,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F4.mysql\u002F5.cau-hoi-phong-van",{"title":244,"path":245,"stem":246,"icon":247},"So sánh 3 RDBMS","\u002Ftu-hoc\u002Fdatabase\u002Fso-sanh","2.tu-hoc\u002F1.database\u002F5.so-sanh","i-lucide-columns-3",{"title":249,"path":250,"stem":251,"children":252,"icon":254},"Supabase (BaaS)","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002Findex",[253,255,259,264,269,274,278,283],{"title":249,"path":250,"stem":251,"icon":254},"i-simple-icons-supabase",{"title":256,"path":257,"stem":258,"icon":68},"1. Database","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fdatabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002F1.database",{"title":260,"path":261,"stem":262,"icon":263},"2. Authentication","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fauthentication","2.tu-hoc\u002F1.database\u002F6.supabase\u002F2.authentication","i-lucide-key",{"title":265,"path":266,"stem":267,"icon":268},"3. Storage & Realtime","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fstorage-realtime","2.tu-hoc\u002F1.database\u002F6.supabase\u002F3.storage-realtime","i-lucide-zap",{"title":270,"path":271,"stem":272,"icon":273},"4. Edge Functions & AI","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Ffunctions-ai","2.tu-hoc\u002F1.database\u002F6.supabase\u002F4.functions-ai","i-lucide-brain",{"title":275,"path":276,"stem":277,"icon":181},"5. API & SDKs","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fapi-sdk","2.tu-hoc\u002F1.database\u002F6.supabase\u002F5.api-sdk",{"title":279,"path":280,"stem":281,"icon":282},"6. Platform & Deploy","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fplatform-deploy","2.tu-hoc\u002F1.database\u002F6.supabase\u002F6.platform-deploy","i-lucide-cloud",{"title":284,"path":285,"stem":286,"icon":196},"7. Interview & Q&A","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Finterview","2.tu-hoc\u002F1.database\u002F6.supabase\u002F7.interview",{"title":288,"icon":289,"path":290,"stem":291,"children":292},"TypeScript","i-simple-icons-typescript","\u002Ftu-hoc\u002Ftypescript","2.tu-hoc\u002F2.typescript\u002Findex",[293,294,297,301,306],{"title":288,"path":290,"stem":291,"icon":289},{"title":173,"path":295,"stem":296,"icon":176},"\u002Ftu-hoc\u002Ftypescript\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F2.typescript\u002F1.gioi-thieu-cai-dat",{"title":298,"path":299,"stem":300,"icon":81},"2. Type System","\u002Ftu-hoc\u002Ftypescript\u002Ftype-system","2.tu-hoc\u002F2.typescript\u002F2.type-system",{"title":302,"path":303,"stem":304,"icon":305},"3. Advanced types","\u002Ftu-hoc\u002Ftypescript\u002Fadvanced-types","2.tu-hoc\u002F2.typescript\u002F3.advanced-types","i-lucide-braces",{"title":307,"path":308,"stem":309,"icon":196},"4. Q và A phỏng vấn","\u002Ftu-hoc\u002Ftypescript\u002Fcau-hoi-phong-van","2.tu-hoc\u002F2.typescript\u002F4.cau-hoi-phong-van",{"title":311,"icon":81,"path":312,"stem":313,"children":314},"OOP","\u002Ftu-hoc\u002Foop","2.tu-hoc\u002F3.oop\u002Findex",[315,316,321,326,330],{"title":311,"path":312,"stem":313,"icon":81},{"title":317,"path":318,"stem":319,"icon":320},"1. Bốn tính chất","\u002Ftu-hoc\u002Foop\u002F4-tinh-chat","2.tu-hoc\u002F3.oop\u002F1.4-tinh-chat","i-lucide-circle-dot",{"title":322,"path":323,"stem":324,"icon":325},"2. SOLID","\u002Ftu-hoc\u002Foop\u002Fsolid","2.tu-hoc\u002F3.oop\u002F2.solid","i-lucide-shield",{"title":327,"path":328,"stem":329,"icon":106},"3. Design Pattern","\u002Ftu-hoc\u002Foop\u002Fdesign-pattern","2.tu-hoc\u002F3.oop\u002F3.design-pattern",{"title":307,"path":331,"stem":332,"icon":196},"\u002Ftu-hoc\u002Foop\u002Fcau-hoi-phong-van","2.tu-hoc\u002F3.oop\u002F4.cau-hoi-phong-van",{"title":334,"icon":305,"path":335,"stem":336,"children":337},"Generic","\u002Ftu-hoc\u002Fgeneric","2.tu-hoc\u002F4.generic\u002Findex",[338,339,344,349,354],{"title":334,"path":335,"stem":336,"icon":305},{"title":340,"path":341,"stem":342,"icon":343},"1. Khái niệm và cú pháp","\u002Ftu-hoc\u002Fgeneric\u002Fkhai-niem","2.tu-hoc\u002F4.generic\u002F1.khai-niem","i-lucide-book",{"title":345,"path":346,"stem":347,"icon":348},"2. Constraint và Variance","\u002Ftu-hoc\u002Fgeneric\u002Fconstraint-variance","2.tu-hoc\u002F4.generic\u002F2.constraint-variance","i-lucide-filter",{"title":350,"path":351,"stem":352,"icon":353},"3. C# vs TS","\u002Ftu-hoc\u002Fgeneric\u002Fso-sanh","2.tu-hoc\u002F4.generic\u002F3.so-sanh","i-lucide-git-compare",{"title":307,"path":355,"stem":356,"icon":196},"\u002Ftu-hoc\u002Fgeneric\u002Fcau-hoi-phong-van","2.tu-hoc\u002F4.generic\u002F4.cau-hoi-phong-van",{"title":358,"icon":359,"path":360,"stem":361,"children":362},"Nuxt 4","i-simple-icons-nuxt","\u002Ftu-hoc\u002Fnuxt-4","2.tu-hoc\u002F5.nuxt-4\u002Findex",[363,364,367,372,376,379],{"title":358,"path":360,"stem":361,"icon":359},{"title":173,"path":365,"stem":366,"icon":176},"\u002Ftu-hoc\u002Fnuxt-4\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F5.nuxt-4\u002F1.gioi-thieu-cai-dat",{"title":368,"path":369,"stem":370,"icon":371},"2. Routing và Rendering","\u002Ftu-hoc\u002Fnuxt-4\u002Frouting-rendering","2.tu-hoc\u002F5.nuxt-4\u002F2.routing-rendering","i-lucide-route",{"title":373,"path":374,"stem":375,"icon":91},"3. Data và Server","\u002Ftu-hoc\u002Fnuxt-4\u002Fdata-server","2.tu-hoc\u002F5.nuxt-4\u002F3.data-server",{"title":307,"path":377,"stem":378,"icon":196},"\u002Ftu-hoc\u002Fnuxt-4\u002Fcau-hoi-phong-van","2.tu-hoc\u002F5.nuxt-4\u002F4.cau-hoi-phong-van",{"title":380,"path":381,"stem":382,"icon":383},"5. Practical Todo App","\u002Ftu-hoc\u002Fnuxt-4\u002Fpractical-todo-app","2.tu-hoc\u002F5.nuxt-4\u002F5.practical-todo-app","i-lucide-check-square",{"title":385,"path":386,"stem":387,"children":388,"icon":390},"Tailwind CSS","\u002Ftu-hoc\u002Ftailwindcss","2.tu-hoc\u002F6.tailwindcss\u002Findex",[389,391,396,401,406,411,416],{"title":385,"path":386,"stem":387,"icon":390},"i-simple-icons-tailwindcss",{"title":392,"path":393,"stem":394,"icon":395},"1. Layout & Spacing","\u002Ftu-hoc\u002Ftailwindcss\u002Flayout-spacing","2.tu-hoc\u002F6.tailwindcss\u002F1.layout-spacing","i-lucide-layout",{"title":397,"path":398,"stem":399,"icon":400},"2. Typography & Colors","\u002Ftu-hoc\u002Ftailwindcss\u002Ftypography-colors","2.tu-hoc\u002F6.tailwindcss\u002F2.typography-colors","i-lucide-type",{"title":402,"path":403,"stem":404,"icon":405},"3. Interactive & Effects","\u002Ftu-hoc\u002Ftailwindcss\u002Finteractive-effects","2.tu-hoc\u002F6.tailwindcss\u002F3.interactive-effects","i-lucide-sparkles",{"title":407,"path":408,"stem":409,"icon":410},"4. Responsive & Dark Mode","\u002Ftu-hoc\u002Ftailwindcss\u002Fresponsive-darkmode","2.tu-hoc\u002F6.tailwindcss\u002F4.responsive-darkmode","i-lucide-monitor-smartphone",{"title":412,"path":413,"stem":414,"icon":415},"5. Customization & Theme","\u002Ftu-hoc\u002Ftailwindcss\u002Fcustomization","2.tu-hoc\u002F6.tailwindcss\u002F5.customization","i-lucide-settings-2",{"title":417,"path":418,"stem":419,"icon":181},"6. Practical Projects","\u002Ftu-hoc\u002Ftailwindcss\u002Fpractical-projects","2.tu-hoc\u002F6.tailwindcss\u002F6.practical-projects",{"id":421,"title":422,"body":423,"description":5814,"extension":5815,"links":5816,"meta":5817,"navigation":5818,"path":280,"seo":5819,"stem":281,"__hash__":5820},"docs\u002F2.tu-hoc\u002F1.database\u002F6.supabase\u002F6.platform-deploy.md","Supabase Platform, Self-Hosting & Migration",{"type":424,"value":425,"toc":5768},"minimark",[426,431,436,440,581,585,633,637,640,676,680,687,765,772,783,796,800,805,819,824,831,990,995,1090,1194,1200,1206,1210,1213,1435,1444,1448,1451,1489,1493,1550,1554,1690,1694,1705,1708,1712,1716,1719,1815,1819,1826,2072,2090,2094,2101,2498,2513,2517,2520,2554,2558,2561,2565,2576,2581,2692,2696,2699,2725,2729,2787,2791,2806,2808,2812,2816,2827,2889,2894,2899,3037,3041,3044,3068,3072,3087,3090,3094,3109,3112,3144,3162,3166,3230,3235,3238,3715,3720,3838,3849,3853,3858,3867,4071,4078,4137,4144,4305,4323,4330,4334,4337,4588,4591,4694,4699,4797,4801,4873,4877,4930,4934,4937,5011,5015,5018,5074,5078,5081,5119,5125,5131,5133,5137,5141,5144,5273,5279,5283,5286,5376,5381,5407,5411,5418,5422,5428,5432,5435,5453,5457,5460,5478,5482,5519,5521,5525,5756,5758,5764],[427,428,430],"h2",{"id":429},"supabase-platform","Supabase Platform",[432,433,435],"h3",{"id":434},"supabase-studio-dashboard","Supabase Studio (Dashboard)",[437,438,439],"p",{},"Dashboard web để quản lý dự án trực quan, không cần dùng CLI:",[441,442,443,456],"table",{},[444,445,446],"thead",{},[447,448,449,453],"tr",{},[450,451,452],"th",{},"Tính năng",[450,454,455],{},"Mô tả",[457,458,459,471,481,491,501,511,521,531,541,551,561,571],"tbody",{},[447,460,461,468],{},[462,463,464],"td",{},[465,466,467],"strong",{},"Table Editor",[462,469,470],{},"CRUD trực quan trên bảng, tạo bảng, quản lý relationships bằng giao diện kéo thả",[447,472,473,478],{},[462,474,475],{},[465,476,477],{},"SQL Editor",[462,479,480],{},"Chạy SQL queries trực tiếp, lưu snippets thường dùng, schema visualization",[447,482,483,488],{},[462,484,485],{},[465,486,487],{},"Auth",[462,489,490],{},"Quản lý users, providers, email templates, redirect URLs, rate limits, audit logs",[447,492,493,498],{},[462,494,495],{},[465,496,497],{},"Storage",[462,499,500],{},"Upload\u002Fdownload files, quản lý buckets, cấu hình RLS policies cho storage objects",[447,502,503,508],{},[462,504,505],{},[465,506,507],{},"Edge Functions",[462,509,510],{},"Deploy, monitor và manage functions với logs realtime",[447,512,513,518],{},[462,514,515],{},[465,516,517],{},"Realtime",[462,519,520],{},"Kiểm tra Realtime subscriptions, message inspector",[447,522,523,528],{},[462,524,525],{},[465,526,527],{},"API Docs",[462,529,530],{},"Auto-generated API documentation từ schema -- xem tất cả endpoints và parameters",[447,532,533,538],{},[462,534,535],{},[465,536,537],{},"GraphiQL",[462,539,540],{},"Interactive GraphQL IDE để khám phá và test GraphQL API",[447,542,543,548],{},[462,544,545],{},[465,546,547],{},"Logs & Analytics",[462,549,550],{},"Xem query logs, error logs, API usage metrics, performance stats",[447,552,553,558],{},[462,554,555],{},[465,556,557],{},"Database Extensions",[462,559,560],{},"Bật\u002Ftắt 70+ Postgres extensions chỉ với một click",[447,562,563,568],{},[462,564,565],{},[465,566,567],{},"Cron Jobs",[462,569,570],{},"Tạo và quản lý scheduled jobs với cron syntax, xem lịch sử chạy",[447,572,573,578],{},[462,574,575],{},[465,576,577],{},"AI Assistant",[462,579,580],{},"Chat với AI để được hỗ trợ viết SQL, debug lỗi, tối ưu query",[432,582,584],{"id":583},"organizations-teams","Organizations & Teams",[586,587,588,595,627],"ul",{},[589,590,591,594],"li",{},[465,592,593],{},"Organizations",": Nhóm các dự án. Mỗi organization có team members và billing riêng",[589,596,597,600,601],{},[465,598,599],{},"Access Control",": Phân quyền chi tiết theo role:\n",[586,602,603,609,615,621],{},[589,604,605,608],{},[465,606,607],{},"Owner",": Toàn quyền quản lý org, billing, xóa projects",[589,610,611,614],{},[465,612,613],{},"Admin",": Quản lý projects, members, settings",[589,616,617,620],{},[465,618,619],{},"Developer",": Truy cập đầy đủ vào project resources",[589,622,623,626],{},[465,624,625],{},"Read-only",": Chỉ xem, không sửa",[589,628,629,632],{},[465,630,631],{},"SSO cho Dashboard",": Team plan hỗ trợ SAML SSO -- member đăng nhập dashboard bằng identity provider của công ty",[432,634,636],{"id":635},"projects","Projects",[437,638,639],{},"Mỗi project Supabase bao gồm:",[586,641,642,648,654,660,666,671],{},[589,643,644,647],{},[465,645,646],{},"Dedicated Postgres database",": Instance riêng, không shared với project khác",[589,649,650,653],{},[465,651,652],{},"Compute & Disk",": Cấu hình CPU, RAM, disk theo nhu cầu (xem bảng Compute Add-ons)",[589,655,656,659],{},[465,657,658],{},"Auto-generated APIs",": REST (PostgREST) và GraphQL (pg_graphql) từ schema",[589,661,662,665],{},[465,663,664],{},"Auth và user management",": Tích hợp sẵn, không cần cài đặt thêm",[589,667,668,670],{},[465,669,507],{},": Serverless compute với Deno runtime",[589,672,673,675],{},[465,674,517],{},": WebSocket server cho Broadcast, Presence, Postgres Changes",[432,677,679],{"id":678},"database-branching","Database Branching",[437,681,682,683,686],{},"Supabase hỗ trợ ",[465,684,685],{},"database branching"," (Preview Branches) để phát triển an toàn:",[688,689,694],"pre",{"className":690,"code":691,"language":692,"meta":693,"style":693},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Tạo branch từ production để test migration mới\nsupabase branches create feature-x --source main\n\n# Mỗi branch có database riêng biệt -- test schema changes không ảnh hưởng production\nsupabase branches list\nsupabase branches delete feature-x\n","bash","",[695,696,697,706,729,736,742,752],"code",{"__ignoreMap":693},[698,699,702],"span",{"class":700,"line":701},"line",1,[698,703,705],{"class":704},"sHwdD","# Tạo branch từ production để test migration mới\n",[698,707,709,713,717,720,723,726],{"class":700,"line":708},2,[698,710,712],{"class":711},"sBMFI","supabase",[698,714,716],{"class":715},"sfazB"," branches",[698,718,719],{"class":715}," create",[698,721,722],{"class":715}," feature-x",[698,724,725],{"class":715}," --source",[698,727,728],{"class":715}," main\n",[698,730,732],{"class":700,"line":731},3,[698,733,735],{"emptyLinePlaceholder":734},true,"\n",[698,737,739],{"class":700,"line":738},4,[698,740,741],{"class":704},"# Mỗi branch có database riêng biệt -- test schema changes không ảnh hưởng production\n",[698,743,745,747,749],{"class":700,"line":744},5,[698,746,712],{"class":711},[698,748,716],{"class":715},[698,750,751],{"class":715}," list\n",[698,753,755,757,759,762],{"class":700,"line":754},6,[698,756,712],{"class":711},[698,758,716],{"class":715},[698,760,761],{"class":715}," delete",[698,763,764],{"class":715}," feature-x\n",[437,766,767,768,771],{},"Mỗi branch là một ",[465,769,770],{},"database instance riêng biệt"," với data riêng. Dùng để:",[586,773,774,777,780],{},[589,775,776],{},"Test schema changes trước khi merge vào production",[589,778,779],{},"Review migrations trong PR trước khi deploy",[589,781,782],{},"Chạy CI\u002FCD pipeline với database isolated",[784,785,788,791,792,795],"callout",{"color":786,"icon":787},"primary","i-lucide-star",[465,789,790],{},"Branching flow lý tưởng:"," Tạo branch từ production -> chạy migration mới trên branch -> test -> merge migration vào main -> ",[695,793,794],{},"supabase db push"," lên production. Branch có thể tự động xóa sau khi merge.",[432,797,799],{"id":798},"logs-monitoring","Logs & Monitoring",[437,801,802],{},[465,803,804],{},"Logs viewer trong Dashboard:",[586,806,807,810,813,816],{},[589,808,809],{},"Query logs: xem tất cả SQL queries đã chạy, thời gian thực thi, errors",[589,811,812],{},"Auth logs: sign up, sign in, token refresh events",[589,814,815],{},"Storage logs: upload, download, delete events",[589,817,818],{},"Edge Function logs: console output, errors, execution time",[437,820,821],{},[465,822,823],{},"Log Drains (Pro tier) — Export logs ra external service:",[437,825,826,827,830],{},"Có thể export logs từ ",[465,828,829],{},"9 nguồn (log sources)"," khác nhau trong Supabase:",[441,832,833,848],{},[444,834,835],{},[447,836,837,840,843,845],{},[450,838,839],{},"#",[450,841,842],{},"Log Source",[450,844,455],{},[450,846,847],{},"Log gì",[457,849,850,866,881,896,911,927,942,958,974],{},[447,851,852,855,860,863],{},[462,853,854],{},"1",[462,856,857],{},[465,858,859],{},"Postgres",[462,861,862],{},"Database logs",[462,864,865],{},"SQL queries, errors, slow queries, connection events",[447,867,868,871,875,878],{},[462,869,870],{},"2",[462,872,873],{},[465,874,487],{},[462,876,877],{},"Authentication logs",[462,879,880],{},"Sign up, sign in, sign out, token refresh, password reset",[447,882,883,886,890,893],{},[462,884,885],{},"3",[462,887,888],{},[465,889,507],{},[462,891,892],{},"Serverless function logs",[462,894,895],{},"Console output, errors, execution time, cold starts",[447,897,898,901,905,908],{},[462,899,900],{},"4",[462,902,903],{},[465,904,497],{},[462,906,907],{},"File storage logs",[462,909,910],{},"Upload, download, delete, signed URL generation, transform",[447,912,913,916,921,924],{},[462,914,915],{},"5",[462,917,918],{},[465,919,920],{},"API Gateway (Kong)",[462,922,923],{},"API gateway logs",[462,925,926],{},"Request path, method, status code, latency, rate limit hits",[447,928,929,932,936,939],{},[462,930,931],{},"6",[462,933,934],{},[465,935,517],{},[462,937,938],{},"WebSocket logs",[462,940,941],{},"Connections, subscriptions, broadcasts, presence events, errors",[447,943,944,947,952,955],{},[462,945,946],{},"7",[462,948,949],{},[465,950,951],{},"PostgREST",[462,953,954],{},"REST API logs",[462,956,957],{},"SQL queries from REST API, auth context, response times",[447,959,960,963,968,971],{},[462,961,962],{},"8",[462,964,965],{},[465,966,967],{},"Edge Network",[462,969,970],{},"CDN\u002Fedge logs",[462,972,973],{},"Cache hits\u002Fmisses, edge latency, bandwidth usage",[447,975,976,979,984,987],{},[462,977,978],{},"9",[462,980,981],{},[465,982,983],{},"Supavisor",[462,985,986],{},"Connection pooler logs",[462,988,989],{},"Pool connections, queue length, timeouts, pool saturation",[437,991,992],{},[465,993,994],{},"Destinations (nơi gửi log đến):",[441,996,997,1010],{},[444,998,999],{},[447,1000,1001,1004,1007],{},[450,1002,1003],{},"Destination",[450,1005,1006],{},"Loại",[450,1008,1009],{},"Use case",[457,1011,1012,1025,1038,1051,1064,1077],{},[447,1013,1014,1019,1022],{},[462,1015,1016],{},[465,1017,1018],{},"Datadog",[462,1020,1021],{},"APM \u002F Monitoring",[462,1023,1024],{},"Full-stack observability, dashboards, alerts",[447,1026,1027,1032,1035],{},[462,1028,1029],{},[465,1030,1031],{},"Grafana Loki",[462,1033,1034],{},"Log aggregation",[462,1036,1037],{},"Open-source log management, tích hợp với Grafana",[447,1039,1040,1045,1048],{},[462,1041,1042],{},[465,1043,1044],{},"Sentry",[462,1046,1047],{},"Error tracking",[462,1049,1050],{},"Track errors và exceptions, stack traces",[447,1052,1053,1058,1061],{},[462,1054,1055],{},[465,1056,1057],{},"Axiom",[462,1059,1060],{},"Log analytics",[462,1062,1063],{},"Query logs với SQL-like syntax, fast search",[447,1065,1066,1071,1074],{},[462,1067,1068],{},[465,1069,1070],{},"S3",[462,1072,1073],{},"Archive",[462,1075,1076],{},"Lưu trữ logs dài hạn, cost-effective",[447,1078,1079,1084,1087],{},[462,1080,1081],{},[465,1082,1083],{},"Custom HTTP Endpoint",[462,1085,1086],{},"Tùy chỉnh",[462,1088,1089],{},"Gửi logs đến bất kỳ HTTP endpoint nào",[688,1091,1093],{"className":690,"code":1092,"language":692,"meta":693,"style":693},"# Tạo log drain\nsupabase log-drain create --project-ref [REF] \\\n  --destination datadog \\\n  --config api_key=xxx,site=datadoghq.com\n\n# Liệt kê log drains\nsupabase log-drain list --project-ref [REF]\n\n# Xóa log drain\nsupabase log-drain delete --project-ref [REF] [DRAIN_ID]\n",[695,1094,1095,1100,1116,1127,1135,1139,1144,1159,1164,1170],{"__ignoreMap":693},[698,1096,1097],{"class":700,"line":701},[698,1098,1099],{"class":704},"# Tạo log drain\n",[698,1101,1102,1104,1107,1109,1112],{"class":700,"line":708},[698,1103,712],{"class":711},[698,1105,1106],{"class":715}," log-drain",[698,1108,719],{"class":715},[698,1110,1111],{"class":715}," --project-ref",[698,1113,1115],{"class":1114},"sTEyZ"," [REF] \\\n",[698,1117,1118,1121,1124],{"class":700,"line":731},[698,1119,1120],{"class":711},"  --destination",[698,1122,1123],{"class":715}," datadog",[698,1125,1126],{"class":1114}," \\\n",[698,1128,1129,1132],{"class":700,"line":738},[698,1130,1131],{"class":715},"  --config",[698,1133,1134],{"class":715}," api_key=xxx,site=datadoghq.com\n",[698,1136,1137],{"class":700,"line":744},[698,1138,735],{"emptyLinePlaceholder":734},[698,1140,1141],{"class":700,"line":754},[698,1142,1143],{"class":704},"# Liệt kê log drains\n",[698,1145,1147,1149,1151,1154,1156],{"class":700,"line":1146},7,[698,1148,712],{"class":711},[698,1150,1106],{"class":715},[698,1152,1153],{"class":715}," list",[698,1155,1111],{"class":715},[698,1157,1158],{"class":1114}," [REF]\n",[698,1160,1162],{"class":700,"line":1161},8,[698,1163,735],{"emptyLinePlaceholder":734},[698,1165,1167],{"class":700,"line":1166},9,[698,1168,1169],{"class":704},"# Xóa log drain\n",[698,1171,1173,1175,1177,1179,1181,1184,1188,1191],{"class":700,"line":1172},10,[698,1174,712],{"class":711},[698,1176,1106],{"class":715},[698,1178,761],{"class":715},[698,1180,1111],{"class":715},[698,1182,1183],{"class":1114}," [REF] ",[698,1185,1187],{"class":1186},"sMK4o","[",[698,1189,1190],{"class":1114},"DRAIN_ID",[698,1192,1193],{"class":1186},"]\n",[437,1195,1196,1199],{},[465,1197,1198],{},"Pricing:"," $60\u002Fdrain\u002Ftháng (Pro tier). Mỗi drain có thể chọn nhiều log sources.",[784,1201,1202,1205],{"color":786,"icon":787},[465,1203,1204],{},"Interview point — Log Drains:"," Khi app scaling, bạn cần centralized logging để debug và monitoring. Log Drains cho phép export logs từ tất cả 9 services của Supabase ra external tools. Phổ biến nhất: Datadog cho enterprise, Grafana Loki cho open-source stack, Sentry cho error tracking.",[432,1207,1209],{"id":1208},"compute-add-ons","Compute Add-ons",[437,1211,1212],{},"Tùy chỉnh CPU\u002FRAM cho database instance:",[441,1214,1215,1237],{},[444,1216,1217],{},[447,1218,1219,1222,1225,1228,1231,1234],{},[450,1220,1221],{},"Size",[450,1223,1224],{},"CPU",[450,1226,1227],{},"RAM",[450,1229,1230],{},"Direct Connections",[450,1232,1233],{},"Pooler Connections",[450,1235,1236],{},"Giá\u002Ftháng",[457,1238,1239,1259,1278,1297,1316,1336,1356,1376,1396,1416],{},[447,1240,1241,1244,1247,1250,1253,1256],{},[462,1242,1243],{},"Micro",[462,1245,1246],{},"2-core ARM",[462,1248,1249],{},"1 GB",[462,1251,1252],{},"60",[462,1254,1255],{},"200",[462,1257,1258],{},"$10",[447,1260,1261,1264,1266,1269,1272,1275],{},[462,1262,1263],{},"Small",[462,1265,1246],{},[462,1267,1268],{},"2 GB",[462,1270,1271],{},"90",[462,1273,1274],{},"400",[462,1276,1277],{},"$15",[447,1279,1280,1283,1285,1288,1291,1294],{},[462,1281,1282],{},"Medium",[462,1284,1246],{},[462,1286,1287],{},"4 GB",[462,1289,1290],{},"120",[462,1292,1293],{},"600",[462,1295,1296],{},"$60",[447,1298,1299,1302,1304,1307,1310,1313],{},[462,1300,1301],{},"Large",[462,1303,1246],{},[462,1305,1306],{},"8 GB",[462,1308,1309],{},"160",[462,1311,1312],{},"800",[462,1314,1315],{},"$110",[447,1317,1318,1321,1324,1327,1330,1333],{},[462,1319,1320],{},"XL",[462,1322,1323],{},"4-core ARM",[462,1325,1326],{},"16 GB",[462,1328,1329],{},"240",[462,1331,1332],{},"1,000",[462,1334,1335],{},"$210",[447,1337,1338,1341,1344,1347,1350,1353],{},[462,1339,1340],{},"2XL",[462,1342,1343],{},"8-core ARM",[462,1345,1346],{},"32 GB",[462,1348,1349],{},"380",[462,1351,1352],{},"1,500",[462,1354,1355],{},"$410",[447,1357,1358,1361,1364,1367,1370,1373],{},[462,1359,1360],{},"4XL",[462,1362,1363],{},"16-core ARM",[462,1365,1366],{},"64 GB",[462,1368,1369],{},"480",[462,1371,1372],{},"3,000",[462,1374,1375],{},"$960",[447,1377,1378,1381,1384,1387,1390,1393],{},[462,1379,1380],{},"8XL",[462,1382,1383],{},"32-core ARM",[462,1385,1386],{},"128 GB",[462,1388,1389],{},"490",[462,1391,1392],{},"6,000",[462,1394,1395],{},"$1,870",[447,1397,1398,1401,1404,1407,1410,1413],{},[462,1399,1400],{},"12XL",[462,1402,1403],{},"48-core ARM",[462,1405,1406],{},"192 GB",[462,1408,1409],{},"500",[462,1411,1412],{},"9,000",[462,1414,1415],{},"$2,800",[447,1417,1418,1421,1424,1427,1429,1432],{},[462,1419,1420],{},"16XL",[462,1422,1423],{},"64-core ARM",[462,1425,1426],{},"256 GB",[462,1428,1409],{},[462,1430,1431],{},"12,000",[462,1433,1434],{},"$3,730",[784,1436,1439,1440,1443],{"color":1437,"icon":1438},"info","i-lucide-info","Paid plans có ",[465,1441,1442],{},"$10 compute credits\u002Ftháng",", đủ cover Micro instance đầu tiên. Mọi plan đều có thể upgrade compute bất kỳ lúc nào mà không downtime.",[432,1445,1447],{"id":1446},"regions","Regions",[437,1449,1450],{},"Hỗ trợ nhiều region trên toàn cầu:",[586,1452,1453,1459,1465,1471,1477,1483],{},[589,1454,1455,1458],{},[465,1456,1457],{},"US",": East (Virginia), West (Oregon, California)",[589,1460,1461,1464],{},[465,1462,1463],{},"EU",": Ireland, Frankfurt, London, Paris",[589,1466,1467,1470],{},[465,1468,1469],{},"Asia Pacific",": Singapore, Sydney, Tokyo, Seoul, Mumbai",[589,1472,1473,1476],{},[465,1474,1475],{},"South America",": Sao Paulo",[589,1478,1479,1482],{},[465,1480,1481],{},"Canada",": Toronto",[589,1484,1485,1488],{},[465,1486,1487],{},"Middle East",": Tel Aviv",[432,1490,1492],{"id":1491},"disk-configuration","Disk Configuration",[441,1494,1495,1512],{},[444,1496,1497],{},[447,1498,1499,1501,1504,1507,1510],{},[450,1500,1006],{},[450,1502,1503],{},"Dung lượng",[450,1505,1506],{},"IOPS",[450,1508,1509],{},"Throughput",[450,1511,1009],{},[457,1513,1514,1532],{},[447,1515,1516,1521,1524,1526,1529],{},[462,1517,1518],{},[465,1519,1520],{},"General Purpose",[462,1522,1523],{},"8 GB -- 16 TB",[462,1525,1372],{},[462,1527,1528],{},"125 MB\u002Fs",[462,1530,1531],{},"Hầu hết ứng dụng",[447,1533,1534,1539,1541,1544,1547],{},[462,1535,1536],{},[465,1537,1538],{},"High Performance",[462,1540,1086],{},[462,1542,1543],{},"Lên đến 80,000",[462,1545,1546],{},"Lên đến 4,000 MB\u002Fs",[462,1548,1549],{},"Mission-critical, high-traffic",[432,1551,1553],{"id":1552},"sla-compliance","SLA & Compliance",[441,1555,1556,1574],{},[444,1557,1558],{},[447,1559,1560,1562,1565,1568,1571],{},[450,1561,452],{},[450,1563,1564],{},"Free",[450,1566,1567],{},"Pro",[450,1569,1570],{},"Team",[450,1572,1573],{},"Enterprise",[457,1575,1576,1593,1609,1625,1644,1660,1675],{},[447,1577,1578,1583,1586,1589,1591],{},[462,1579,1580],{},[465,1581,1582],{},"SOC2",[462,1584,1585],{},"-",[462,1587,1588],{},"SOC2 Type II",[462,1590,1588],{},[462,1592,1588],{},[447,1594,1595,1600,1602,1604,1607],{},[462,1596,1597],{},[465,1598,1599],{},"ISO 27001",[462,1601,1585],{},[462,1603,1585],{},[462,1605,1606],{},"Có",[462,1608,1606],{},[447,1610,1611,1616,1618,1620,1623],{},[462,1612,1613],{},[465,1614,1615],{},"HIPAA",[462,1617,1585],{},[462,1619,1585],{},[462,1621,1622],{},"Add-on",[462,1624,1606],{},[447,1626,1627,1632,1635,1638,1641],{},[462,1628,1629],{},[465,1630,1631],{},"SLA",[462,1633,1634],{},"Không",[462,1636,1637],{},"99.9%",[462,1639,1640],{},"99.95%",[462,1642,1643],{},"99.99%",[447,1645,1646,1650,1652,1654,1657],{},[462,1647,1648],{},[465,1649,631],{},[462,1651,1585],{},[462,1653,1585],{},[462,1655,1656],{},"SAML",[462,1658,1659],{},"SAML + Custom",[447,1661,1662,1667,1669,1671,1673],{},[462,1663,1664],{},[465,1665,1666],{},"PrivateLink",[462,1668,1585],{},[462,1670,1585],{},[462,1672,1585],{},[462,1674,1606],{},[447,1676,1677,1682,1684,1686,1688],{},[462,1678,1679],{},[465,1680,1681],{},"Network Restrictions",[462,1683,1585],{},[462,1685,1585],{},[462,1687,1606],{},[462,1689,1606],{},[432,1691,1693],{"id":1692},"custom-domains-ssl","Custom Domains & SSL",[586,1695,1696,1699,1702],{},[589,1697,1698],{},"Thêm custom domain cho Supabase project API endpoints",[589,1700,1701],{},"Tự động cấp và renew SSL certificate qua Let's Encrypt",[589,1703,1704],{},"DNS configuration hỗ trợ CNAME flattening",[1706,1707],"hr",{},[427,1709,1711],{"id":1710},"self-hosting","Self-Hosting",[432,1713,1715],{"id":1714},"docker-compose-setup","Docker Compose Setup",[437,1717,1718],{},"Supabase có thể tự host trên infrastructure của bạn:",[688,1720,1722],{"className":690,"code":1721,"language":692,"meta":693,"style":693},"# Clone Supabase repository\ngit clone --depth 1 https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase\n\n# Vào thư mục docker\ncd supabase\u002Fdocker\n\n# Copy và cấu hình biến môi trường\ncp .env.example .env\n\n# Khởi động toàn bộ stack\ndocker compose -f docker-compose.yml up -d\n",[695,1723,1724,1729,1747,1751,1756,1765,1769,1774,1785,1789,1794],{"__ignoreMap":693},[698,1725,1726],{"class":700,"line":701},[698,1727,1728],{"class":704},"# Clone Supabase repository\n",[698,1730,1731,1734,1737,1740,1744],{"class":700,"line":708},[698,1732,1733],{"class":711},"git",[698,1735,1736],{"class":715}," clone",[698,1738,1739],{"class":715}," --depth",[698,1741,1743],{"class":1742},"sbssI"," 1",[698,1745,1746],{"class":715}," https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase\n",[698,1748,1749],{"class":700,"line":731},[698,1750,735],{"emptyLinePlaceholder":734},[698,1752,1753],{"class":700,"line":738},[698,1754,1755],{"class":704},"# Vào thư mục docker\n",[698,1757,1758,1762],{"class":700,"line":744},[698,1759,1761],{"class":1760},"s2Zo4","cd",[698,1763,1764],{"class":715}," supabase\u002Fdocker\n",[698,1766,1767],{"class":700,"line":754},[698,1768,735],{"emptyLinePlaceholder":734},[698,1770,1771],{"class":700,"line":1146},[698,1772,1773],{"class":704},"# Copy và cấu hình biến môi trường\n",[698,1775,1776,1779,1782],{"class":700,"line":1161},[698,1777,1778],{"class":711},"cp",[698,1780,1781],{"class":715}," .env.example",[698,1783,1784],{"class":715}," .env\n",[698,1786,1787],{"class":700,"line":1166},[698,1788,735],{"emptyLinePlaceholder":734},[698,1790,1791],{"class":700,"line":1172},[698,1792,1793],{"class":704},"# Khởi động toàn bộ stack\n",[698,1795,1797,1800,1803,1806,1809,1812],{"class":700,"line":1796},11,[698,1798,1799],{"class":711},"docker",[698,1801,1802],{"class":715}," compose",[698,1804,1805],{"class":715}," -f",[698,1807,1808],{"class":715}," docker-compose.yml",[698,1810,1811],{"class":715}," up",[698,1813,1814],{"class":715}," -d\n",[432,1816,1818],{"id":1817},"kiến-trúc-self-hosted-11-services","Kiến trúc Self-Hosted — 11 Services",[437,1820,1821,1822,1825],{},"Self-hosted Supabase bao gồm chính xác ",[465,1823,1824],{},"11 services"," chạy trong Docker containers:",[441,1827,1828,1846],{},[444,1829,1830],{},[447,1831,1832,1834,1837,1840,1843],{},[450,1833,839],{},[450,1835,1836],{},"Service",[450,1838,1839],{},"Role",[450,1841,1842],{},"Port mặc định",[450,1844,1845],{},"Giải thích",[457,1847,1848,1868,1888,1909,1930,1950,1970,1990,2010,2030,2051],{},[447,1849,1850,1852,1859,1862,1865],{},[462,1851,854],{},[462,1853,1854],{},[465,1855,1856],{},[695,1857,1858],{},"studio",[462,1860,1861],{},"Dashboard UI",[462,1863,1864],{},"3000",[462,1866,1867],{},"Next.js app — giao diện quản lý trực quan (Table Editor, SQL Editor, Auth, Storage, Logs)",[447,1869,1870,1872,1879,1882,1885],{},[462,1871,870],{},[462,1873,1874],{},[465,1875,1876],{},[695,1877,1878],{},"kong",[462,1880,1881],{},"API Gateway",[462,1883,1884],{},"8000, 8443",[462,1886,1887],{},"Routing requests đến đúng service, rate limiting, JWT validation, API key authentication",[447,1889,1890,1892,1900,1903,1906],{},[462,1891,885],{},[462,1893,1894,1899],{},[465,1895,1896],{},[695,1897,1898],{},"auth"," (GoTrue)",[462,1901,1902],{},"Authentication",[462,1904,1905],{},"9999",[462,1907,1908],{},"Xác thực người dùng (email\u002Fpassword, OAuth, SSO, MFA), sinh JWT, quản lý session",[447,1910,1911,1913,1921,1924,1927],{},[462,1912,900],{},[462,1914,1915,1920],{},[465,1916,1917],{},[695,1918,1919],{},"rest"," (PostgREST)",[462,1922,1923],{},"REST API",[462,1925,1926],{},"3001",[462,1928,1929],{},"Tự động sinh REST API từ database schema, hỗ trợ filtering, pagination, joins",[447,1931,1932,1934,1941,1944,1947],{},[462,1933,915],{},[462,1935,1936],{},[465,1937,1938],{},[695,1939,1940],{},"realtime",[462,1942,1943],{},"WebSocket Server",[462,1945,1946],{},"4000",[462,1948,1949],{},"Broadcast, Presence, Postgres Changes (CDC) qua logical replication",[447,1951,1952,1954,1961,1964,1967],{},[462,1953,931],{},[462,1955,1956],{},[465,1957,1958],{},[695,1959,1960],{},"storage",[462,1962,1963],{},"File Storage API",[462,1965,1966],{},"5000",[462,1968,1969],{},"S3-compatible file storage, upload\u002Fdownload, signed URLs, image transformation routing",[447,1971,1972,1974,1981,1984,1987],{},[462,1973,946],{},[462,1975,1976],{},[465,1977,1978],{},[695,1979,1980],{},"meta",[462,1982,1983],{},"Database Metadata API",[462,1985,1986],{},"8080",[462,1988,1989],{},"Cung cấp metadata về database schema cho Studio (tables, columns, relationships)",[447,1991,1992,1994,2001,2004,2007],{},[462,1993,962],{},[462,1995,1996],{},[465,1997,1998],{},[695,1999,2000],{},"db",[462,2002,2003],{},"PostgreSQL Database",[462,2005,2006],{},"5432",[462,2008,2009],{},"Trái tim của Supabase — database chính lưu tất cả dữ liệu, auth schema, storage metadata",[447,2011,2012,2014,2021,2024,2027],{},[462,2013,978],{},[462,2015,2016],{},[465,2017,2018],{},[695,2019,2020],{},"imgproxy",[462,2022,2023],{},"Image Transformation",[462,2025,2026],{},"5001",[462,2028,2029],{},"Resize, crop, format conversion ảnh on-the-fly (dùng bởi Storage CDN URLs)",[447,2031,2032,2035,2042,2045,2048],{},[462,2033,2034],{},"10",[462,2036,2037],{},[465,2038,2039],{},[695,2040,2041],{},"pgbouncer",[462,2043,2044],{},"Connection Pooler",[462,2046,2047],{},"6543",[462,2049,2050],{},"Quản lý pool connections đến Postgres (hoặc có thể dùng Supavisor thay thế)",[447,2052,2053,2056,2063,2066,2069],{},[462,2054,2055],{},"11",[462,2057,2058],{},[465,2059,2060],{},[695,2061,2062],{},"vector",[462,2064,2065],{},"Log Ingestion Pipeline",[462,2067,2068],{},"9000",[462,2070,2071],{},"Thu thập, transform, và route logs từ tất cả services đến Logflare hoặc external destinations",[784,2073,2074,2077,2078,2080,2081,2083,2084,2086,2087,2089],{"color":1437,"icon":1438},[465,2075,2076],{},"Lưu ý:"," ",[695,2079,983],{}," là connection pooler thế hệ mới của Supabase, thay thế dần ",[695,2082,2041],{}," trong managed platform. Trong self-hosted, bạn có thể chọn dùng ",[695,2085,2041],{}," (đơn giản, ổn định) hoặc ",[695,2088,983],{}," (multi-tenant, serverless-friendly).",[432,2091,2093],{"id":2092},"biến-môi-trường-quan-trọng-cho-self-hosted","Biến Môi Trường Quan Trọng cho Self-Hosted",[437,2095,2096,2097,2100],{},"File ",[695,2098,2099],{},".env"," trong Docker Compose chứa các biến môi trường cần thiết để cấu hình toàn bộ stack:",[688,2102,2104],{"className":690,"code":2103,"language":692,"meta":693,"style":693},"# ==================== DATABASE ====================\nPOSTGRES_PASSWORD=your-super-secret-password\n# Mật khẩu của postgres user — PHẢI thay đổi trong production\n\n# ==================== AUTH \u002F JWT ====================\nJWT_SECRET=your-super-secret-jwt-token\n# Secret key dùng để ký JWT token — PHẢI là string ngẫu nhiên, ít nhất 32 chars\n# Shared giữa Kong, GoTrue, PostgREST để verify JWT\n\nANON_KEY=generated-anon-key\n# API key cho anonymous users — có quyền thấp nhất (dùng cho client-side)\n# Được generate từ JWT_SECRET với role=anon\n\nSERVICE_ROLE_KEY=generated-service-role-key\n# API key cho admin — bypass RLS, full quyền (dùng cho backend\u002FEdge Functions)\n# Được generate từ JWT_SECRET với role=service_role\n\n# ==================== URL \u002F NETWORK ====================\nSITE_URL=http:\u002F\u002Flocalhost:3000\n# URL của app frontend — dùng cho redirect URLs (OAuth callback, email confirmation)\n\nAPI_EXTERNAL_URL=http:\u002F\u002Flocalhost:8000\n# URL của Kong API Gateway — endpoint chính cho tất cả API requests\n\nSTUDIO_DEFAULT_ORGANIZATION=My Org\n# Tên organization mặc định hiển thị trong Studio\n\nSTUDIO_DEFAULT_PROJECT=My Project\n# Tên project mặc định hiển thị trong Studio\n\n# ==================== EMAIL (SMTP) ====================\nSMTP_HOST=smtp.sendgrid.net\nSMTP_PORT=587\nSMTP_USER=apikey\nSMTP_PASS=your-sendgrid-key\nSMTP_SENDER_NAME=My App\nSMTP_SENDER_ADDRESS=noreply@myapp.com\n# Cấu hình SMTP để gửi email xác thực, magic link, reset password\n# Hỗ trợ: SendGrid, Resend, AWS SES, Mailgun, custom SMTP\n\n# ==================== STORAGE ====================\nSTORAGE_BACKEND=s3\n# Backend lưu trữ file: 'file' (local disk) hoặc 's3' (S3-compatible)\n# Dùng 's3' trong production với MinIO hoặc AWS S3\n\nS3_ENDPOINT=https:\u002F\u002Fs3.amazonaws.com\nS3_REGION=us-east-1\nS3_BUCKET=my-supabase-storage\nS3_ACCESS_KEY=xxx\nS3_SECRET_KEY=xxx\n# Chỉ cần khi STORAGE_BACKEND=s3\n",[695,2105,2106,2111,2122,2127,2131,2136,2146,2151,2156,2160,2170,2175,2181,2186,2197,2203,2209,2214,2220,2231,2237,2242,2253,2259,2264,2278,2284,2289,2302,2308,2313,2319,2330,2341,2352,2363,2376,2387,2393,2399,2404,2410,2421,2427,2433,2438,2449,2460,2471,2482,2492],{"__ignoreMap":693},[698,2107,2108],{"class":700,"line":701},[698,2109,2110],{"class":704},"# ==================== DATABASE ====================\n",[698,2112,2113,2116,2119],{"class":700,"line":708},[698,2114,2115],{"class":1114},"POSTGRES_PASSWORD",[698,2117,2118],{"class":1186},"=",[698,2120,2121],{"class":715},"your-super-secret-password\n",[698,2123,2124],{"class":700,"line":731},[698,2125,2126],{"class":704},"# Mật khẩu của postgres user — PHẢI thay đổi trong production\n",[698,2128,2129],{"class":700,"line":738},[698,2130,735],{"emptyLinePlaceholder":734},[698,2132,2133],{"class":700,"line":744},[698,2134,2135],{"class":704},"# ==================== AUTH \u002F JWT ====================\n",[698,2137,2138,2141,2143],{"class":700,"line":754},[698,2139,2140],{"class":1114},"JWT_SECRET",[698,2142,2118],{"class":1186},[698,2144,2145],{"class":715},"your-super-secret-jwt-token\n",[698,2147,2148],{"class":700,"line":1146},[698,2149,2150],{"class":704},"# Secret key dùng để ký JWT token — PHẢI là string ngẫu nhiên, ít nhất 32 chars\n",[698,2152,2153],{"class":700,"line":1161},[698,2154,2155],{"class":704},"# Shared giữa Kong, GoTrue, PostgREST để verify JWT\n",[698,2157,2158],{"class":700,"line":1166},[698,2159,735],{"emptyLinePlaceholder":734},[698,2161,2162,2165,2167],{"class":700,"line":1172},[698,2163,2164],{"class":1114},"ANON_KEY",[698,2166,2118],{"class":1186},[698,2168,2169],{"class":715},"generated-anon-key\n",[698,2171,2172],{"class":700,"line":1796},[698,2173,2174],{"class":704},"# API key cho anonymous users — có quyền thấp nhất (dùng cho client-side)\n",[698,2176,2178],{"class":700,"line":2177},12,[698,2179,2180],{"class":704},"# Được generate từ JWT_SECRET với role=anon\n",[698,2182,2184],{"class":700,"line":2183},13,[698,2185,735],{"emptyLinePlaceholder":734},[698,2187,2189,2192,2194],{"class":700,"line":2188},14,[698,2190,2191],{"class":1114},"SERVICE_ROLE_KEY",[698,2193,2118],{"class":1186},[698,2195,2196],{"class":715},"generated-service-role-key\n",[698,2198,2200],{"class":700,"line":2199},15,[698,2201,2202],{"class":704},"# API key cho admin — bypass RLS, full quyền (dùng cho backend\u002FEdge Functions)\n",[698,2204,2206],{"class":700,"line":2205},16,[698,2207,2208],{"class":704},"# Được generate từ JWT_SECRET với role=service_role\n",[698,2210,2212],{"class":700,"line":2211},17,[698,2213,735],{"emptyLinePlaceholder":734},[698,2215,2217],{"class":700,"line":2216},18,[698,2218,2219],{"class":704},"# ==================== URL \u002F NETWORK ====================\n",[698,2221,2223,2226,2228],{"class":700,"line":2222},19,[698,2224,2225],{"class":1114},"SITE_URL",[698,2227,2118],{"class":1186},[698,2229,2230],{"class":715},"http:\u002F\u002Flocalhost:3000\n",[698,2232,2234],{"class":700,"line":2233},20,[698,2235,2236],{"class":704},"# URL của app frontend — dùng cho redirect URLs (OAuth callback, email confirmation)\n",[698,2238,2240],{"class":700,"line":2239},21,[698,2241,735],{"emptyLinePlaceholder":734},[698,2243,2245,2248,2250],{"class":700,"line":2244},22,[698,2246,2247],{"class":1114},"API_EXTERNAL_URL",[698,2249,2118],{"class":1186},[698,2251,2252],{"class":715},"http:\u002F\u002Flocalhost:8000\n",[698,2254,2256],{"class":700,"line":2255},23,[698,2257,2258],{"class":704},"# URL của Kong API Gateway — endpoint chính cho tất cả API requests\n",[698,2260,2262],{"class":700,"line":2261},24,[698,2263,735],{"emptyLinePlaceholder":734},[698,2265,2267,2270,2272,2275],{"class":700,"line":2266},25,[698,2268,2269],{"class":1114},"STUDIO_DEFAULT_ORGANIZATION",[698,2271,2118],{"class":1186},[698,2273,2274],{"class":715},"My",[698,2276,2277],{"class":711}," Org\n",[698,2279,2281],{"class":700,"line":2280},26,[698,2282,2283],{"class":704},"# Tên organization mặc định hiển thị trong Studio\n",[698,2285,2287],{"class":700,"line":2286},27,[698,2288,735],{"emptyLinePlaceholder":734},[698,2290,2292,2295,2297,2299],{"class":700,"line":2291},28,[698,2293,2294],{"class":1114},"STUDIO_DEFAULT_PROJECT",[698,2296,2118],{"class":1186},[698,2298,2274],{"class":715},[698,2300,2301],{"class":711}," Project\n",[698,2303,2305],{"class":700,"line":2304},29,[698,2306,2307],{"class":704},"# Tên project mặc định hiển thị trong Studio\n",[698,2309,2311],{"class":700,"line":2310},30,[698,2312,735],{"emptyLinePlaceholder":734},[698,2314,2316],{"class":700,"line":2315},31,[698,2317,2318],{"class":704},"# ==================== EMAIL (SMTP) ====================\n",[698,2320,2322,2325,2327],{"class":700,"line":2321},32,[698,2323,2324],{"class":1114},"SMTP_HOST",[698,2326,2118],{"class":1186},[698,2328,2329],{"class":715},"smtp.sendgrid.net\n",[698,2331,2333,2336,2338],{"class":700,"line":2332},33,[698,2334,2335],{"class":1114},"SMTP_PORT",[698,2337,2118],{"class":1186},[698,2339,2340],{"class":715},"587\n",[698,2342,2344,2347,2349],{"class":700,"line":2343},34,[698,2345,2346],{"class":1114},"SMTP_USER",[698,2348,2118],{"class":1186},[698,2350,2351],{"class":715},"apikey\n",[698,2353,2355,2358,2360],{"class":700,"line":2354},35,[698,2356,2357],{"class":1114},"SMTP_PASS",[698,2359,2118],{"class":1186},[698,2361,2362],{"class":715},"your-sendgrid-key\n",[698,2364,2366,2369,2371,2373],{"class":700,"line":2365},36,[698,2367,2368],{"class":1114},"SMTP_SENDER_NAME",[698,2370,2118],{"class":1186},[698,2372,2274],{"class":715},[698,2374,2375],{"class":711}," App\n",[698,2377,2379,2382,2384],{"class":700,"line":2378},37,[698,2380,2381],{"class":1114},"SMTP_SENDER_ADDRESS",[698,2383,2118],{"class":1186},[698,2385,2386],{"class":715},"noreply@myapp.com\n",[698,2388,2390],{"class":700,"line":2389},38,[698,2391,2392],{"class":704},"# Cấu hình SMTP để gửi email xác thực, magic link, reset password\n",[698,2394,2396],{"class":700,"line":2395},39,[698,2397,2398],{"class":704},"# Hỗ trợ: SendGrid, Resend, AWS SES, Mailgun, custom SMTP\n",[698,2400,2402],{"class":700,"line":2401},40,[698,2403,735],{"emptyLinePlaceholder":734},[698,2405,2407],{"class":700,"line":2406},41,[698,2408,2409],{"class":704},"# ==================== STORAGE ====================\n",[698,2411,2413,2416,2418],{"class":700,"line":2412},42,[698,2414,2415],{"class":1114},"STORAGE_BACKEND",[698,2417,2118],{"class":1186},[698,2419,2420],{"class":715},"s3\n",[698,2422,2424],{"class":700,"line":2423},43,[698,2425,2426],{"class":704},"# Backend lưu trữ file: 'file' (local disk) hoặc 's3' (S3-compatible)\n",[698,2428,2430],{"class":700,"line":2429},44,[698,2431,2432],{"class":704},"# Dùng 's3' trong production với MinIO hoặc AWS S3\n",[698,2434,2436],{"class":700,"line":2435},45,[698,2437,735],{"emptyLinePlaceholder":734},[698,2439,2441,2444,2446],{"class":700,"line":2440},46,[698,2442,2443],{"class":1114},"S3_ENDPOINT",[698,2445,2118],{"class":1186},[698,2447,2448],{"class":715},"https:\u002F\u002Fs3.amazonaws.com\n",[698,2450,2452,2455,2457],{"class":700,"line":2451},47,[698,2453,2454],{"class":1114},"S3_REGION",[698,2456,2118],{"class":1186},[698,2458,2459],{"class":715},"us-east-1\n",[698,2461,2463,2466,2468],{"class":700,"line":2462},48,[698,2464,2465],{"class":1114},"S3_BUCKET",[698,2467,2118],{"class":1186},[698,2469,2470],{"class":715},"my-supabase-storage\n",[698,2472,2474,2477,2479],{"class":700,"line":2473},49,[698,2475,2476],{"class":1114},"S3_ACCESS_KEY",[698,2478,2118],{"class":1186},[698,2480,2481],{"class":715},"xxx\n",[698,2483,2485,2488,2490],{"class":700,"line":2484},50,[698,2486,2487],{"class":1114},"S3_SECRET_KEY",[698,2489,2118],{"class":1186},[698,2491,2481],{"class":715},[698,2493,2495],{"class":700,"line":2494},51,[698,2496,2497],{"class":704},"# Chỉ cần khi STORAGE_BACKEND=s3\n",[784,2499,2502,2077,2505,2507,2508,2507,2510,2512],{"color":2500,"icon":2501},"warning","i-lucide-shield-alert",[465,2503,2504],{},"Bảo mật:",[695,2506,2140],{},", ",[695,2509,2115],{},[695,2511,2191],{}," là các giá trị nhạy cảm nhất. Phải thay đổi từ default và không commit vào git. Dùng Docker secrets hoặc vault cho production.",[432,2514,2516],{"id":2515},"kubernetes","Kubernetes",[437,2518,2519],{},"Community-maintained Helm charts để deploy Supabase trên Kubernetes. Phù hợp cho enterprise cần auto-scaling và high availability.",[688,2521,2523],{"className":690,"code":2522,"language":692,"meta":693,"style":693},"helm repo add supabase https:\u002F\u002Fsupabase-community.github.io\u002Fhelm-charts\nhelm install supabase supabase\u002Fsupabase\n",[695,2524,2525,2542],{"__ignoreMap":693},[698,2526,2527,2530,2533,2536,2539],{"class":700,"line":701},[698,2528,2529],{"class":711},"helm",[698,2531,2532],{"class":715}," repo",[698,2534,2535],{"class":715}," add",[698,2537,2538],{"class":715}," supabase",[698,2540,2541],{"class":715}," https:\u002F\u002Fsupabase-community.github.io\u002Fhelm-charts\n",[698,2543,2544,2546,2549,2551],{"class":700,"line":708},[698,2545,2529],{"class":711},[698,2547,2548],{"class":715}," install",[698,2550,2538],{"class":715},[698,2552,2553],{"class":715}," supabase\u002Fsupabase\n",[432,2555,2557],{"id":2556},"traefik-setup","Traefik Setup",[437,2559,2560],{},"Community-maintained setup với Traefik làm reverse proxy thay cho Kong. Phù hợp nếu bạn đã dùng Traefik trong infrastructure.",[432,2562,2564],{"id":2563},"khác-biệt-với-managed-platform","Khác biệt với managed platform",[784,2566,2567,2568,2571,2572,2575],{"color":2500,"icon":2501},"Self-hosted ",[465,2569,2570],{},"không phải"," Supabase CLI local dev (chỉ dành cho dev\u002Ftest), và ",[465,2573,2574],{},"không giống"," managed Supabase. Bạn chịu trách nhiệm vận hành toàn bộ stack.",[437,2577,2578],{},[465,2579,2580],{},"Tính năng KHÔNG có trong self-hosted:",[441,2582,2583,2595],{},[444,2584,2585],{},[447,2586,2587,2589,2592],{},[450,2588,452],{},[450,2590,2591],{},"Managed",[450,2593,2594],{},"Self-hosted",[457,2596,2597,2606,2615,2626,2635,2644,2653,2663,2672,2682],{},[447,2598,2599,2602,2604],{},[462,2600,2601],{},"Database Branching (Preview Branches)",[462,2603,1606],{},[462,2605,1634],{},[447,2607,2608,2611,2613],{},[462,2609,2610],{},"Advanced metrics ngoài logs",[462,2612,1606],{},[462,2614,1634],{},[447,2616,2617,2620,2623],{},[462,2618,2619],{},"Managed backups + PITR",[462,2621,2622],{},"Tự động",[462,2624,2625],{},"Tự làm",[447,2627,2628,2631,2633],{},[462,2629,2630],{},"Analytics Buckets (Iceberg)",[462,2632,1606],{},[462,2634,1634],{},[447,2636,2637,2640,2642],{},[462,2638,2639],{},"Vector Buckets",[462,2641,1606],{},[462,2643,1634],{},[447,2645,2646,2649,2651],{},[462,2647,2648],{},"ETL \u002F Pipelines",[462,2650,1606],{},[462,2652,1634],{},[447,2654,2655,2658,2661],{},[462,2656,2657],{},"Log Drains (export logs)",[462,2659,2660],{},"Có (Pro)",[462,2662,1634],{},[447,2664,2665,2668,2670],{},[462,2666,2667],{},"Platform Management API",[462,2669,1606],{},[462,2671,1634],{},[447,2673,2674,2677,2679],{},[462,2675,2676],{},"Studio hỗ trợ nhiều project",[462,2678,1606],{},[462,2680,2681],{},"Chỉ 1 project",[447,2683,2684,2687,2689],{},[462,2685,2686],{},"Custom Domains + SSL tự động",[462,2688,1606],{},[462,2690,2691],{},"Tự cấu hình",[432,2693,2695],{"id":2694},"trách-nhiệm-khi-self-host","Trách nhiệm khi self-host",[437,2697,2698],{},"Những việc bạn phải tự làm khi self-host:",[586,2700,2701,2704,2707,2710,2713,2716,2719,2722],{},[589,2702,2703],{},"Provisioning và bảo trì server (VPS, dedicated, on-prem)",[589,2705,2706],{},"Security hardening, cập nhật OS và services định kỳ",[589,2708,2709],{},"Cấu hình và quản lý từng service riêng lẻ",[589,2711,2712],{},"Database maintenance (VACUUM, reindex, analyze)",[589,2714,2715],{},"High availability và scalability (replication, load balancing)",[589,2717,2718],{},"Backups và disaster recovery (lên lịch, test restore)",[589,2720,2721],{},"Monitoring và alerting (Prometheus, Grafana, uptime)",[589,2723,2724],{},"Cấu hình S3 storage backend (MinIO hoặc AWS S3)",[432,2726,2728],{"id":2727},"cấu-hình-s3-storage-cho-self-hosted","Cấu hình S3 Storage cho self-hosted",[688,2730,2732],{"className":690,"code":2731,"language":692,"meta":693,"style":693},"# Trong .env của Docker Compose\nSTORAGE_BACKEND=s3\nS3_ENDPOINT=https:\u002F\u002Fs3.amazonaws.com\nS3_REGION=us-east-1\nS3_BUCKET=my-supabase-storage\nS3_ACCESS_KEY=xxx\nS3_SECRET_KEY=xxx\n",[695,2733,2734,2739,2747,2755,2763,2771,2779],{"__ignoreMap":693},[698,2735,2736],{"class":700,"line":701},[698,2737,2738],{"class":704},"# Trong .env của Docker Compose\n",[698,2740,2741,2743,2745],{"class":700,"line":708},[698,2742,2415],{"class":1114},[698,2744,2118],{"class":1186},[698,2746,2420],{"class":715},[698,2748,2749,2751,2753],{"class":700,"line":731},[698,2750,2443],{"class":1114},[698,2752,2118],{"class":1186},[698,2754,2448],{"class":715},[698,2756,2757,2759,2761],{"class":700,"line":738},[698,2758,2454],{"class":1114},[698,2760,2118],{"class":1186},[698,2762,2459],{"class":715},[698,2764,2765,2767,2769],{"class":700,"line":744},[698,2766,2465],{"class":1114},[698,2768,2118],{"class":1186},[698,2770,2470],{"class":715},[698,2772,2773,2775,2777],{"class":700,"line":754},[698,2774,2476],{"class":1114},[698,2776,2118],{"class":1186},[698,2778,2481],{"class":715},[698,2780,2781,2783,2785],{"class":700,"line":1146},[698,2782,2487],{"class":1114},[698,2784,2118],{"class":1186},[698,2786,2481],{"class":715},[432,2788,2790],{"id":2789},"telemetry","Telemetry",[586,2792,2793,2799],{},[589,2794,2795,2796],{},"Self-hosted Supabase (Docker Compose) ",[465,2797,2798],{},"không thu thập telemetry",[589,2800,2801,2802,2805],{},"Supabase CLI thu thập usage telemetry (có thể opt-out bằng ",[695,2803,2804],{},"SUPABASE_CLI_TELEMETRY=0",")",[1706,2807],{},[427,2809,2811],{"id":2810},"migration-guides","Migration Guides",[432,2813,2815],{"id":2814},"firebase-auth-supabase-auth","Firebase Auth -> Supabase Auth",[437,2817,2818,2819,2826],{},"Supabase cung cấp công cụ trong repo ",[2820,2821,2825],"a",{"href":2822,"rel":2823},"https:\u002F\u002Fgithub.com\u002Fsupabase-community\u002Ffirebase-to-supabase",[2824],"nofollow","firebase-to-supabase",":",[688,2828,2830],{"className":690,"code":2829,"language":692,"meta":693,"style":693},"# Clone migration tool\ngit clone https:\u002F\u002Fgithub.com\u002Fsupabase-community\u002Ffirebase-to-supabase.git\n\n# Export Firebase users ra JSON\nnode firestoreusers2json.js users.json 100\n\n# Import users vào Supabase Auth\nnode import_users.js users.json 100\n",[695,2831,2832,2837,2846,2850,2855,2869,2873,2878],{"__ignoreMap":693},[698,2833,2834],{"class":700,"line":701},[698,2835,2836],{"class":704},"# Clone migration tool\n",[698,2838,2839,2841,2843],{"class":700,"line":708},[698,2840,1733],{"class":711},[698,2842,1736],{"class":715},[698,2844,2845],{"class":715}," https:\u002F\u002Fgithub.com\u002Fsupabase-community\u002Ffirebase-to-supabase.git\n",[698,2847,2848],{"class":700,"line":731},[698,2849,735],{"emptyLinePlaceholder":734},[698,2851,2852],{"class":700,"line":738},[698,2853,2854],{"class":704},"# Export Firebase users ra JSON\n",[698,2856,2857,2860,2863,2866],{"class":700,"line":744},[698,2858,2859],{"class":711},"node",[698,2861,2862],{"class":715}," firestoreusers2json.js",[698,2864,2865],{"class":715}," users.json",[698,2867,2868],{"class":1742}," 100\n",[698,2870,2871],{"class":700,"line":754},[698,2872,735],{"emptyLinePlaceholder":734},[698,2874,2875],{"class":700,"line":1146},[698,2876,2877],{"class":704},"# Import users vào Supabase Auth\n",[698,2879,2880,2882,2885,2887],{"class":700,"line":1161},[698,2881,2859],{"class":711},[698,2883,2884],{"class":715}," import_users.js",[698,2886,2865],{"class":715},[698,2888,2868],{"class":1742},[437,2890,2891],{},[465,2892,2893],{},"Quy trình chi tiết:",[2895,2896,2898],"h4",{"id":2897},"bước-1-chuẩn-bị-credentials","Bước 1: Chuẩn bị credentials",[688,2900,2902],{"className":690,"code":2901,"language":692,"meta":693,"style":693},"# Tạo supabase-service.json với connection info\n{\n  \"url\": \"https:\u002F\u002F[PROJECT_REF].supabase.co\",\n  \"service_role_key\": \"eyJhbGciOi...\"\n}\n\n# Tạo firebase-service.json từ Firebase Console\n# Project Settings > Service Accounts > Generate New Private Key\n{\n  \"type\": \"service_account\",\n  \"project_id\": \"my-firebase-project\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----...\",\n  \"client_email\": \"firebase-adminsdk@...\"\n}\n",[695,2903,2904,2909,2914,2933,2948,2953,2957,2962,2967,2971,2987,3003,3019,3033],{"__ignoreMap":693},[698,2905,2906],{"class":700,"line":701},[698,2907,2908],{"class":704},"# Tạo supabase-service.json với connection info\n",[698,2910,2911],{"class":700,"line":708},[698,2912,2913],{"class":1186},"{\n",[698,2915,2916,2919,2921,2924,2927,2930],{"class":700,"line":731},[698,2917,2918],{"class":711},"  \"url\"",[698,2920,2826],{"class":1760},[698,2922,2923],{"class":1186}," \"",[698,2925,2926],{"class":715},"https:\u002F\u002F[PROJECT_REF].supabase.co",[698,2928,2929],{"class":1186},"\"",[698,2931,2932],{"class":715},",\n",[698,2934,2935,2938,2940,2942,2945],{"class":700,"line":738},[698,2936,2937],{"class":711},"  \"service_role_key\"",[698,2939,2826],{"class":1760},[698,2941,2923],{"class":1186},[698,2943,2944],{"class":715},"eyJhbGciOi...",[698,2946,2947],{"class":1186},"\"\n",[698,2949,2950],{"class":700,"line":744},[698,2951,2952],{"class":1186},"}\n",[698,2954,2955],{"class":700,"line":754},[698,2956,735],{"emptyLinePlaceholder":734},[698,2958,2959],{"class":700,"line":1146},[698,2960,2961],{"class":704},"# Tạo firebase-service.json từ Firebase Console\n",[698,2963,2964],{"class":700,"line":1161},[698,2965,2966],{"class":704},"# Project Settings > Service Accounts > Generate New Private Key\n",[698,2968,2969],{"class":700,"line":1166},[698,2970,2913],{"class":1186},[698,2972,2973,2976,2978,2980,2983,2985],{"class":700,"line":1172},[698,2974,2975],{"class":711},"  \"type\"",[698,2977,2826],{"class":1760},[698,2979,2923],{"class":1186},[698,2981,2982],{"class":715},"service_account",[698,2984,2929],{"class":1186},[698,2986,2932],{"class":715},[698,2988,2989,2992,2994,2996,2999,3001],{"class":700,"line":1796},[698,2990,2991],{"class":711},"  \"project_id\"",[698,2993,2826],{"class":1760},[698,2995,2923],{"class":1186},[698,2997,2998],{"class":715},"my-firebase-project",[698,3000,2929],{"class":1186},[698,3002,2932],{"class":715},[698,3004,3005,3008,3010,3012,3015,3017],{"class":700,"line":2177},[698,3006,3007],{"class":711},"  \"private_key\"",[698,3009,2826],{"class":1760},[698,3011,2923],{"class":1186},[698,3013,3014],{"class":715},"-----BEGIN PRIVATE KEY-----...",[698,3016,2929],{"class":1186},[698,3018,2932],{"class":715},[698,3020,3021,3024,3026,3028,3031],{"class":700,"line":2183},[698,3022,3023],{"class":711},"  \"client_email\"",[698,3025,2826],{"class":1760},[698,3027,2923],{"class":1186},[698,3029,3030],{"class":715},"firebase-adminsdk@...",[698,3032,2947],{"class":1186},[698,3034,3035],{"class":700,"line":2188},[698,3036,2952],{"class":1186},[2895,3038,3040],{"id":3039},"bước-2-lấy-firebase-password-hash-parameters","Bước 2: Lấy Firebase password hash parameters",[437,3042,3043],{},"Từ Firebase Console > Authentication > Settings > Password hash parameters:",[586,3045,3046,3065],{},[589,3047,3048,3051,3052,2507,3055,2507,3058,2507,3061,3064],{},[695,3049,3050],{},"hash_config",": Chứa ",[695,3053,3054],{},"mem_cost",[695,3056,3057],{},"rounds",[695,3059,3060],{},"salt_separator",[695,3062,3063],{},"signer_key"," (scrypt parameters)",[589,3066,3067],{},"Các tham số này cần thiết để Supabase có thể verify password hash khi user login",[2895,3069,3071],{"id":3070},"bước-3-export-users-từ-firebase","Bước 3: Export users từ Firebase",[688,3073,3075],{"className":690,"code":3074,"language":692,"meta":693,"style":693},"node firestoreusers2json.js users.json 100\n",[695,3076,3077],{"__ignoreMap":693},[698,3078,3079,3081,3083,3085],{"class":700,"line":701},[698,3080,2859],{"class":711},[698,3082,2862],{"class":715},[698,3084,2865],{"class":715},[698,3086,2868],{"class":1742},[437,3088,3089],{},"Script sẽ export tất cả users từ Firebase Auth (email, password hash, provider data, metadata) ra file JSON.",[2895,3091,3093],{"id":3092},"bước-4-import-users-vào-supabase-auth","Bước 4: Import users vào Supabase Auth",[688,3095,3097],{"className":690,"code":3096,"language":692,"meta":693,"style":693},"node import_users.js users.json 100\n",[695,3098,3099],{"__ignoreMap":693},[698,3100,3101,3103,3105,3107],{"class":700,"line":701},[698,3102,2859],{"class":711},[698,3104,2884],{"class":715},[698,3106,2865],{"class":715},[698,3108,2868],{"class":1742},[437,3110,3111],{},"Script sẽ:",[586,3113,3114,3121,3128,3138],{},[589,3115,3116,3117,3120],{},"Tạo user trong ",[695,3118,3119],{},"auth.users"," với password hash giữ nguyên",[589,3122,3123,3124,3127],{},"Tạo records trong ",[695,3125,3126],{},"auth.identities"," cho mỗi provider",[589,3129,3130,3131,2507,3134,3137],{},"Set ",[695,3132,3133],{},"app_metadata",[695,3135,3136],{},"user_metadata"," từ Firebase user data",[589,3139,3140,3141,3143],{},"Cấu hình scrypt parameters trong ",[695,3142,3126],{}," data",[784,3145,3146,3149,3150,2507,3152,2507,3154,2507,3156,3158,3159,3161],{"color":786,"icon":787},[465,3147,3148],{},"Key insight — Password hash:"," Firebase dùng scrypt để hash password. Supabase cũng hỗ trợ scrypt — nhưng cần cấu hình đúng tham số (",[695,3151,3054],{},[695,3153,3057],{},[695,3155,3060],{},[695,3157,3063],{},"). Nếu sai, user sẽ không thể login với mật khẩu cũ. Đây là lý do phải lấy chính xác ",[695,3160,3050],{}," từ Firebase Console.",[432,3163,3165],{"id":3164},"firebase-firestore-supabase-database","Firebase Firestore -> Supabase Database",[688,3167,3169],{"className":690,"code":3168,"language":692,"meta":693,"style":693},"# Liệt kê tất cả collections\nnode collections.js\n\n# Export collection ra JSON\nnode firestore2json.js users 1000\n\n# Import vào Supabase Postgres\nnode json2supabase.js .\u002Fusers.json bigserial id\n",[695,3170,3171,3176,3183,3187,3192,3205,3209,3214],{"__ignoreMap":693},[698,3172,3173],{"class":700,"line":701},[698,3174,3175],{"class":704},"# Liệt kê tất cả collections\n",[698,3177,3178,3180],{"class":700,"line":708},[698,3179,2859],{"class":711},[698,3181,3182],{"class":715}," collections.js\n",[698,3184,3185],{"class":700,"line":731},[698,3186,735],{"emptyLinePlaceholder":734},[698,3188,3189],{"class":700,"line":738},[698,3190,3191],{"class":704},"# Export collection ra JSON\n",[698,3193,3194,3196,3199,3202],{"class":700,"line":744},[698,3195,2859],{"class":711},[698,3197,3198],{"class":715}," firestore2json.js",[698,3200,3201],{"class":715}," users",[698,3203,3204],{"class":1742}," 1000\n",[698,3206,3207],{"class":700,"line":754},[698,3208,735],{"emptyLinePlaceholder":734},[698,3210,3211],{"class":700,"line":1146},[698,3212,3213],{"class":704},"# Import vào Supabase Postgres\n",[698,3215,3216,3218,3221,3224,3227],{"class":700,"line":1161},[698,3217,2859],{"class":711},[698,3219,3220],{"class":715}," json2supabase.js",[698,3222,3223],{"class":715}," .\u002Fusers.json",[698,3225,3226],{"class":715}," bigserial",[698,3228,3229],{"class":715}," id\n",[437,3231,3232],{},[465,3233,3234],{},"Quy trình transform dữ liệu:",[437,3236,3237],{},"Firestore là NoSQL document-based. Postgres là SQL relational. Việc transform dữ liệu là thách thức lớn nhất:",[688,3239,3243],{"className":3240,"code":3241,"language":3242,"meta":693,"style":693},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F Firestore document (NoSQL — nested)\n{\n  \"id\": \"user-123\",\n  \"name\": \"Vinh\",\n  \"email\": \"vinh@example.com\",\n  \"address\": {                        \u002F\u002F Nested object -> Cần flatten\n    \"street\": \"123 Main St\",\n    \"city\": \"Hanoi\",\n    \"country\": \"Vietnam\"\n  },\n  \"posts\": [                          \u002F\u002F Sub-collection -> Cần tách bảng riêng\n    { \"title\": \"Post 1\", \"content\": \"...\" },\n    { \"title\": \"Post 2\", \"content\": \"...\" }\n  ],\n  \"tags\": [\"react\", \"supabase\"]       \u002F\u002F Array -> Cần bảng junction\n}\n\n\u002F\u002F Postgres schema (SQL — relational)\nCREATE TABLE users (\n  id UUID PRIMARY KEY,\n  name TEXT,\n  email TEXT UNIQUE,\n  address_street TEXT,                \u002F\u002F Flattened\n  address_city TEXT,                  \u002F\u002F Flattened\n  address_country TEXT                \u002F\u002F Flattened\n);\n\nCREATE TABLE posts (\n  id BIGSERIAL PRIMARY KEY,\n  user_id UUID REFERENCES users(id),\n  title TEXT,\n  content TEXT\n);\n\nCREATE TABLE user_tags (\n  user_id UUID REFERENCES users(id),\n  tag TEXT,\n  PRIMARY KEY (user_id, tag)\n);\n","javascript",[695,3244,3245,3250,3254,3277,3297,3317,3334,3355,3375,3393,3398,3413,3456,3494,3501,3534,3538,3542,3547,3558,3565,3572,3579,3589,3599,3607,3614,3618,3626,3633,3645,3652,3657,3663,3667,3676,3686,3693,3709],{"__ignoreMap":693},[698,3246,3247],{"class":700,"line":701},[698,3248,3249],{"class":704},"\u002F\u002F Firestore document (NoSQL — nested)\n",[698,3251,3252],{"class":700,"line":708},[698,3253,2913],{"class":1186},[698,3255,3256,3259,3262,3264,3268,3270,3273,3275],{"class":700,"line":731},[698,3257,3258],{"class":1186},"  \"",[698,3260,3261],{"class":715},"id",[698,3263,2929],{"class":1186},[698,3265,3267],{"class":3266},"swJcz",": ",[698,3269,2929],{"class":1186},[698,3271,3272],{"class":715},"user-123",[698,3274,2929],{"class":1186},[698,3276,2932],{"class":1186},[698,3278,3279,3281,3284,3286,3288,3290,3293,3295],{"class":700,"line":738},[698,3280,3258],{"class":1186},[698,3282,3283],{"class":715},"name",[698,3285,2929],{"class":1186},[698,3287,3267],{"class":3266},[698,3289,2929],{"class":1186},[698,3291,3292],{"class":715},"Vinh",[698,3294,2929],{"class":1186},[698,3296,2932],{"class":1186},[698,3298,3299,3301,3304,3306,3308,3310,3313,3315],{"class":700,"line":744},[698,3300,3258],{"class":1186},[698,3302,3303],{"class":715},"email",[698,3305,2929],{"class":1186},[698,3307,3267],{"class":3266},[698,3309,2929],{"class":1186},[698,3311,3312],{"class":715},"vinh@example.com",[698,3314,2929],{"class":1186},[698,3316,2932],{"class":1186},[698,3318,3319,3321,3324,3326,3328,3331],{"class":700,"line":754},[698,3320,3258],{"class":1186},[698,3322,3323],{"class":715},"address",[698,3325,2929],{"class":1186},[698,3327,3267],{"class":3266},[698,3329,3330],{"class":1186},"{",[698,3332,3333],{"class":704},"                        \u002F\u002F Nested object -> Cần flatten\n",[698,3335,3336,3339,3342,3344,3346,3348,3351,3353],{"class":700,"line":1146},[698,3337,3338],{"class":1186},"    \"",[698,3340,3341],{"class":3266},"street",[698,3343,2929],{"class":1186},[698,3345,2826],{"class":1186},[698,3347,2923],{"class":1186},[698,3349,3350],{"class":715},"123 Main St",[698,3352,2929],{"class":1186},[698,3354,2932],{"class":1186},[698,3356,3357,3359,3362,3364,3366,3368,3371,3373],{"class":700,"line":1161},[698,3358,3338],{"class":1186},[698,3360,3361],{"class":3266},"city",[698,3363,2929],{"class":1186},[698,3365,2826],{"class":1186},[698,3367,2923],{"class":1186},[698,3369,3370],{"class":715},"Hanoi",[698,3372,2929],{"class":1186},[698,3374,2932],{"class":1186},[698,3376,3377,3379,3382,3384,3386,3388,3391],{"class":700,"line":1166},[698,3378,3338],{"class":1186},[698,3380,3381],{"class":3266},"country",[698,3383,2929],{"class":1186},[698,3385,2826],{"class":1186},[698,3387,2923],{"class":1186},[698,3389,3390],{"class":715},"Vietnam",[698,3392,2947],{"class":1186},[698,3394,3395],{"class":700,"line":1172},[698,3396,3397],{"class":1186},"  },\n",[698,3399,3400,3402,3405,3407,3410],{"class":700,"line":1796},[698,3401,3258],{"class":1186},[698,3403,3404],{"class":715},"posts",[698,3406,2929],{"class":1186},[698,3408,3409],{"class":3266},": [                          ",[698,3411,3412],{"class":704},"\u002F\u002F Sub-collection -> Cần tách bảng riêng\n",[698,3414,3415,3418,3420,3423,3425,3427,3429,3432,3434,3437,3439,3442,3444,3446,3448,3451,3453],{"class":700,"line":2177},[698,3416,3417],{"class":1186},"    {",[698,3419,2923],{"class":1186},[698,3421,3422],{"class":3266},"title",[698,3424,2929],{"class":1186},[698,3426,2826],{"class":1186},[698,3428,2923],{"class":1186},[698,3430,3431],{"class":715},"Post 1",[698,3433,2929],{"class":1186},[698,3435,3436],{"class":1186},",",[698,3438,2923],{"class":1186},[698,3440,3441],{"class":3266},"content",[698,3443,2929],{"class":1186},[698,3445,2826],{"class":1186},[698,3447,2923],{"class":1186},[698,3449,3450],{"class":715},"...",[698,3452,2929],{"class":1186},[698,3454,3455],{"class":1186}," },\n",[698,3457,3458,3460,3462,3464,3466,3468,3470,3473,3475,3477,3479,3481,3483,3485,3487,3489,3491],{"class":700,"line":2183},[698,3459,3417],{"class":1186},[698,3461,2923],{"class":1186},[698,3463,3422],{"class":3266},[698,3465,2929],{"class":1186},[698,3467,2826],{"class":1186},[698,3469,2923],{"class":1186},[698,3471,3472],{"class":715},"Post 2",[698,3474,2929],{"class":1186},[698,3476,3436],{"class":1186},[698,3478,2923],{"class":1186},[698,3480,3441],{"class":3266},[698,3482,2929],{"class":1186},[698,3484,2826],{"class":1186},[698,3486,2923],{"class":1186},[698,3488,3450],{"class":715},[698,3490,2929],{"class":1186},[698,3492,3493],{"class":1186}," }\n",[698,3495,3496,3499],{"class":700,"line":2188},[698,3497,3498],{"class":3266},"  ]",[698,3500,2932],{"class":1186},[698,3502,3503,3505,3508,3510,3513,3515,3518,3520,3522,3524,3526,3528,3531],{"class":700,"line":2199},[698,3504,3258],{"class":1186},[698,3506,3507],{"class":715},"tags",[698,3509,2929],{"class":1186},[698,3511,3512],{"class":3266},": [",[698,3514,2929],{"class":1186},[698,3516,3517],{"class":715},"react",[698,3519,2929],{"class":1186},[698,3521,3436],{"class":1186},[698,3523,2923],{"class":1186},[698,3525,712],{"class":715},[698,3527,2929],{"class":1186},[698,3529,3530],{"class":3266},"]       ",[698,3532,3533],{"class":704},"\u002F\u002F Array -> Cần bảng junction\n",[698,3535,3536],{"class":700,"line":2205},[698,3537,2952],{"class":1186},[698,3539,3540],{"class":700,"line":2211},[698,3541,735],{"emptyLinePlaceholder":734},[698,3543,3544],{"class":700,"line":2216},[698,3545,3546],{"class":704},"\u002F\u002F Postgres schema (SQL — relational)\n",[698,3548,3549,3552,3555],{"class":700,"line":2222},[698,3550,3551],{"class":1114},"CREATE TABLE ",[698,3553,3554],{"class":1760},"users",[698,3556,3557],{"class":1114}," (\n",[698,3559,3560,3563],{"class":700,"line":2233},[698,3561,3562],{"class":1114},"  id UUID PRIMARY KEY",[698,3564,2932],{"class":1186},[698,3566,3567,3570],{"class":700,"line":2239},[698,3568,3569],{"class":1114},"  name TEXT",[698,3571,2932],{"class":1186},[698,3573,3574,3577],{"class":700,"line":2244},[698,3575,3576],{"class":1114},"  email TEXT UNIQUE",[698,3578,2932],{"class":1186},[698,3580,3581,3584,3586],{"class":700,"line":2255},[698,3582,3583],{"class":1114},"  address_street TEXT",[698,3585,3436],{"class":1186},[698,3587,3588],{"class":704},"                \u002F\u002F Flattened\n",[698,3590,3591,3594,3596],{"class":700,"line":2261},[698,3592,3593],{"class":1114},"  address_city TEXT",[698,3595,3436],{"class":1186},[698,3597,3598],{"class":704},"                  \u002F\u002F Flattened\n",[698,3600,3601,3604],{"class":700,"line":2266},[698,3602,3603],{"class":1114},"  address_country TEXT                ",[698,3605,3606],{"class":704},"\u002F\u002F Flattened\n",[698,3608,3609,3611],{"class":700,"line":2280},[698,3610,2805],{"class":1114},[698,3612,3613],{"class":1186},";\n",[698,3615,3616],{"class":700,"line":2286},[698,3617,735],{"emptyLinePlaceholder":734},[698,3619,3620,3622,3624],{"class":700,"line":2291},[698,3621,3551],{"class":1114},[698,3623,3404],{"class":1760},[698,3625,3557],{"class":1114},[698,3627,3628,3631],{"class":700,"line":2304},[698,3629,3630],{"class":1114},"  id BIGSERIAL PRIMARY KEY",[698,3632,2932],{"class":1186},[698,3634,3635,3638,3640,3643],{"class":700,"line":2310},[698,3636,3637],{"class":1114},"  user_id UUID REFERENCES ",[698,3639,3554],{"class":1760},[698,3641,3642],{"class":1114},"(id)",[698,3644,2932],{"class":1186},[698,3646,3647,3650],{"class":700,"line":2315},[698,3648,3649],{"class":1114},"  title TEXT",[698,3651,2932],{"class":1186},[698,3653,3654],{"class":700,"line":2321},[698,3655,3656],{"class":1114},"  content TEXT\n",[698,3658,3659,3661],{"class":700,"line":2332},[698,3660,2805],{"class":1114},[698,3662,3613],{"class":1186},[698,3664,3665],{"class":700,"line":2343},[698,3666,735],{"emptyLinePlaceholder":734},[698,3668,3669,3671,3674],{"class":700,"line":2354},[698,3670,3551],{"class":1114},[698,3672,3673],{"class":1760},"user_tags",[698,3675,3557],{"class":1114},[698,3677,3678,3680,3682,3684],{"class":700,"line":2365},[698,3679,3637],{"class":1114},[698,3681,3554],{"class":1760},[698,3683,3642],{"class":1114},[698,3685,2932],{"class":1186},[698,3687,3688,3691],{"class":700,"line":2378},[698,3689,3690],{"class":1114},"  tag TEXT",[698,3692,2932],{"class":1186},[698,3694,3695,3698,3701,3704,3706],{"class":700,"line":2389},[698,3696,3697],{"class":1114},"  PRIMARY ",[698,3699,3700],{"class":1760},"KEY",[698,3702,3703],{"class":1114}," (user_id",[698,3705,3436],{"class":1186},[698,3707,3708],{"class":1114}," tag)\n",[698,3710,3711,3713],{"class":700,"line":2395},[698,3712,2805],{"class":1114},[698,3714,3613],{"class":1186},[437,3716,3717],{},[465,3718,3719],{},"Nguyên tắc transform:",[441,3721,3722,3735],{},[444,3723,3724],{},[447,3725,3726,3729,3732],{},[450,3727,3728],{},"Firestore Pattern",[450,3730,3731],{},"Postgres Equivalent",[450,3733,3734],{},"Cách xử lý",[457,3736,3737,3754,3768,3785,3804,3821],{},[447,3738,3739,3745,3751],{},[462,3740,3741,3742],{},"Nested object ",[695,3743,3744],{},"{address: {city: \"Hanoi\"}}",[462,3746,3747,3748],{},"Separate columns ",[695,3749,3750],{},"address_city TEXT",[462,3752,3753],{},"Flatten nested object thành columns riêng",[447,3755,3756,3759,3765],{},[462,3757,3758],{},"Sub-collection",[462,3760,3761,3762],{},"Separate table + FK ",[695,3763,3764],{},"user_id REFERENCES users(id)",[462,3766,3767],{},"Tạo bảng riêng với foreign key",[447,3769,3770,3776,3782],{},[462,3771,3772,3773],{},"Array field ",[695,3774,3775],{},"[\"react\", \"supabase\"]",[462,3777,3778,3779],{},"Junction table ",[695,3780,3781],{},"user_tags(user_id, tag)",[462,3783,3784],{},"Tạo bảng junction (many-to-many)",[447,3786,3787,3793,3798],{},[462,3788,3789,3790],{},"Timestamp ",[695,3791,3792],{},"Firestore.Timestamp",[462,3794,3795],{},[695,3796,3797],{},"TIMESTAMPTZ",[462,3799,3800,3801],{},"Convert qua ",[695,3802,3803],{},"new Date(ts._seconds * 1000)",[447,3805,3806,3812,3818],{},[462,3807,3808,3809],{},"Reference ",[695,3810,3811],{},"DocumentReference",[462,3813,3814,3815],{},"Foreign Key ",[695,3816,3817],{},"UUID",[462,3819,3820],{},"Extract ID và tạo FK constraint",[447,3822,3823,3826,3832],{},[462,3824,3825],{},"GeoPoint",[462,3827,3828,3831],{},[695,3829,3830],{},"POINT"," type (PostGIS)",[462,3833,3834,3835],{},"Cần extension ",[695,3836,3837],{},"postgis",[784,3839,3841,3844,3845,3848],{"color":2500,"icon":3840},"i-lucide-alert-triangle",[465,3842,3843],{},"Thách thức lớn nhất:"," Firestore là NoSQL (document-based), Postgres là SQL (relational). Dữ liệu nested cần được \"flatten\" -- tách nested data thành các bảng riêng biệt với foreign keys. Dùng ",[695,3846,3847],{},"custom hooks"," để customize quá trình transform JSON.",[432,3850,3852],{"id":3851},"firebase-storage-supabase-storage","Firebase Storage -> Supabase Storage",[437,3854,3855],{},[465,3856,3857],{},"Quy trình migrate files:",[3859,3860,3861],"ol",{},[589,3862,3863,3866],{},[465,3864,3865],{},"Export danh sách files"," từ Firebase Storage (dùng Firebase Admin SDK):",[688,3868,3870],{"className":3240,"code":3869,"language":3242,"meta":693,"style":693},"const admin = require('firebase-admin')\nadmin.initializeApp({ credential: admin.credential.cert(serviceAccount) })\n\nconst bucket = admin.storage().bucket()\nconst [files] = await bucket.getFiles()\n\nfor (const file of files) {\n  console.log(file.name, file.metadata.size)\n  \u002F\u002F Download và upload lên Supabase Storage\n}\n",[695,3871,3872,3900,3941,3945,3971,4001,4005,4026,4062,4067],{"__ignoreMap":693},[698,3873,3874,3878,3881,3883,3886,3889,3892,3895,3897],{"class":700,"line":701},[698,3875,3877],{"class":3876},"spNyl","const",[698,3879,3880],{"class":1114}," admin ",[698,3882,2118],{"class":1186},[698,3884,3885],{"class":1760}," require",[698,3887,3888],{"class":1114},"(",[698,3890,3891],{"class":1186},"'",[698,3893,3894],{"class":715},"firebase-admin",[698,3896,3891],{"class":1186},[698,3898,3899],{"class":1114},")\n",[698,3901,3902,3905,3908,3911,3913,3915,3918,3920,3923,3925,3928,3930,3933,3936,3939],{"class":700,"line":708},[698,3903,3904],{"class":1114},"admin",[698,3906,3907],{"class":1186},".",[698,3909,3910],{"class":1760},"initializeApp",[698,3912,3888],{"class":1114},[698,3914,3330],{"class":1186},[698,3916,3917],{"class":3266}," credential",[698,3919,2826],{"class":1186},[698,3921,3922],{"class":1114}," admin",[698,3924,3907],{"class":1186},[698,3926,3927],{"class":1114},"credential",[698,3929,3907],{"class":1186},[698,3931,3932],{"class":1760},"cert",[698,3934,3935],{"class":1114},"(serviceAccount) ",[698,3937,3938],{"class":1186},"}",[698,3940,3899],{"class":1114},[698,3942,3943],{"class":700,"line":731},[698,3944,735],{"emptyLinePlaceholder":734},[698,3946,3947,3949,3952,3954,3956,3958,3960,3963,3965,3968],{"class":700,"line":738},[698,3948,3877],{"class":3876},[698,3950,3951],{"class":1114}," bucket ",[698,3953,2118],{"class":1186},[698,3955,3922],{"class":1114},[698,3957,3907],{"class":1186},[698,3959,1960],{"class":1760},[698,3961,3962],{"class":1114},"()",[698,3964,3907],{"class":1186},[698,3966,3967],{"class":1760},"bucket",[698,3969,3970],{"class":1114},"()\n",[698,3972,3973,3975,3978,3981,3984,3987,3991,3994,3996,3999],{"class":700,"line":744},[698,3974,3877],{"class":3876},[698,3976,3977],{"class":1186}," [",[698,3979,3980],{"class":1114},"files",[698,3982,3983],{"class":1186},"]",[698,3985,3986],{"class":1186}," =",[698,3988,3990],{"class":3989},"s7zQu"," await",[698,3992,3993],{"class":1114}," bucket",[698,3995,3907],{"class":1186},[698,3997,3998],{"class":1760},"getFiles",[698,4000,3970],{"class":1114},[698,4002,4003],{"class":700,"line":754},[698,4004,735],{"emptyLinePlaceholder":734},[698,4006,4007,4010,4013,4015,4018,4021,4024],{"class":700,"line":1146},[698,4008,4009],{"class":3989},"for",[698,4011,4012],{"class":1114}," (",[698,4014,3877],{"class":3876},[698,4016,4017],{"class":1114}," file ",[698,4019,4020],{"class":1186},"of",[698,4022,4023],{"class":1114}," files) ",[698,4025,2913],{"class":1186},[698,4027,4028,4031,4033,4036,4038,4041,4043,4045,4047,4050,4052,4055,4057,4060],{"class":700,"line":1161},[698,4029,4030],{"class":1114},"  console",[698,4032,3907],{"class":1186},[698,4034,4035],{"class":1760},"log",[698,4037,3888],{"class":3266},[698,4039,4040],{"class":1114},"file",[698,4042,3907],{"class":1186},[698,4044,3283],{"class":1114},[698,4046,3436],{"class":1186},[698,4048,4049],{"class":1114}," file",[698,4051,3907],{"class":1186},[698,4053,4054],{"class":1114},"metadata",[698,4056,3907],{"class":1186},[698,4058,4059],{"class":1114},"size",[698,4061,3899],{"class":3266},[698,4063,4064],{"class":700,"line":1166},[698,4065,4066],{"class":704},"  \u002F\u002F Download và upload lên Supabase Storage\n",[698,4068,4069],{"class":700,"line":1172},[698,4070,2952],{"class":1186},[3859,4072,4073],{"start":708},[589,4074,4075],{},[465,4076,4077],{},"Download files từ Firebase Storage:",[688,4079,4081],{"className":3240,"code":4080,"language":3242,"meta":693,"style":693},"const tempPath = `\u002Ftmp\u002F${file.name}`\nawait file.download({ destination: tempPath })\n",[695,4082,4083,4110],{"__ignoreMap":693},[698,4084,4085,4087,4090,4092,4095,4098,4101,4103,4105,4107],{"class":700,"line":701},[698,4086,3877],{"class":3876},[698,4088,4089],{"class":1114}," tempPath ",[698,4091,2118],{"class":1186},[698,4093,4094],{"class":1186}," `",[698,4096,4097],{"class":715},"\u002Ftmp\u002F",[698,4099,4100],{"class":1186},"${",[698,4102,4040],{"class":1114},[698,4104,3907],{"class":1186},[698,4106,3283],{"class":1114},[698,4108,4109],{"class":1186},"}`\n",[698,4111,4112,4115,4117,4119,4122,4124,4126,4129,4131,4133,4135],{"class":700,"line":708},[698,4113,4114],{"class":3989},"await",[698,4116,4049],{"class":1114},[698,4118,3907],{"class":1186},[698,4120,4121],{"class":1760},"download",[698,4123,3888],{"class":1114},[698,4125,3330],{"class":1186},[698,4127,4128],{"class":3266}," destination",[698,4130,2826],{"class":1186},[698,4132,4089],{"class":1114},[698,4134,3938],{"class":1186},[698,4136,3899],{"class":1114},[3859,4138,4139],{"start":731},[589,4140,4141],{},[465,4142,4143],{},"Upload lên Supabase Storage:",[688,4145,4147],{"className":3240,"code":4146,"language":3242,"meta":693,"style":693},"const fileBuffer = fs.readFileSync(tempPath)\nconst { data, error } = await supabase.storage\n  .from('migrated-files')\n  .upload(file.name, fileBuffer, {\n    contentType: file.metadata.contentType || 'application\u002Foctet-stream',\n    cacheControl: file.metadata.cacheControl || '3600'\n  })\n",[695,4148,4149,4169,4196,4215,4239,4270,4298],{"__ignoreMap":693},[698,4150,4151,4153,4156,4158,4161,4163,4166],{"class":700,"line":701},[698,4152,3877],{"class":3876},[698,4154,4155],{"class":1114}," fileBuffer ",[698,4157,2118],{"class":1186},[698,4159,4160],{"class":1114}," fs",[698,4162,3907],{"class":1186},[698,4164,4165],{"class":1760},"readFileSync",[698,4167,4168],{"class":1114},"(tempPath)\n",[698,4170,4171,4173,4176,4178,4180,4183,4185,4187,4189,4191,4193],{"class":700,"line":708},[698,4172,3877],{"class":3876},[698,4174,4175],{"class":1186}," {",[698,4177,3143],{"class":1114},[698,4179,3436],{"class":1186},[698,4181,4182],{"class":1114}," error ",[698,4184,3938],{"class":1186},[698,4186,3986],{"class":1186},[698,4188,3990],{"class":3989},[698,4190,2538],{"class":1114},[698,4192,3907],{"class":1186},[698,4194,4195],{"class":1114},"storage\n",[698,4197,4198,4201,4204,4206,4208,4211,4213],{"class":700,"line":731},[698,4199,4200],{"class":1186},"  .",[698,4202,4203],{"class":1760},"from",[698,4205,3888],{"class":1114},[698,4207,3891],{"class":1186},[698,4209,4210],{"class":715},"migrated-files",[698,4212,3891],{"class":1186},[698,4214,3899],{"class":1114},[698,4216,4217,4219,4222,4225,4227,4229,4231,4234,4236],{"class":700,"line":738},[698,4218,4200],{"class":1186},[698,4220,4221],{"class":1760},"upload",[698,4223,4224],{"class":1114},"(file",[698,4226,3907],{"class":1186},[698,4228,3283],{"class":1114},[698,4230,3436],{"class":1186},[698,4232,4233],{"class":1114}," fileBuffer",[698,4235,3436],{"class":1186},[698,4237,4238],{"class":1186}," {\n",[698,4240,4241,4244,4246,4248,4250,4252,4254,4257,4260,4263,4266,4268],{"class":700,"line":744},[698,4242,4243],{"class":3266},"    contentType",[698,4245,2826],{"class":1186},[698,4247,4049],{"class":1114},[698,4249,3907],{"class":1186},[698,4251,4054],{"class":1114},[698,4253,3907],{"class":1186},[698,4255,4256],{"class":1114},"contentType ",[698,4258,4259],{"class":1186},"||",[698,4261,4262],{"class":1186}," '",[698,4264,4265],{"class":715},"application\u002Foctet-stream",[698,4267,3891],{"class":1186},[698,4269,2932],{"class":1186},[698,4271,4272,4275,4277,4279,4281,4283,4285,4288,4290,4292,4295],{"class":700,"line":754},[698,4273,4274],{"class":3266},"    cacheControl",[698,4276,2826],{"class":1186},[698,4278,4049],{"class":1114},[698,4280,3907],{"class":1186},[698,4282,4054],{"class":1114},[698,4284,3907],{"class":1186},[698,4286,4287],{"class":1114},"cacheControl ",[698,4289,4259],{"class":1186},[698,4291,4262],{"class":1186},[698,4293,4294],{"class":715},"3600",[698,4296,4297],{"class":1186},"'\n",[698,4299,4300,4303],{"class":700,"line":1146},[698,4301,4302],{"class":1186},"  }",[698,4304,3899],{"class":1114},[3859,4306,4307],{"start":738},[589,4308,4309,4312],{},[465,4310,4311],{},"Map Firebase Storage paths sang Supabase Storage paths:",[586,4313,4314,4320],{},[589,4315,4316,4317],{},"Firebase: ",[695,4318,4319],{},"users\u002Fuser-123\u002Favatar.jpg",[589,4321,4322],{},"Supabase: giữ nguyên cấu trúc hoặc tổ chức lại theo bucket",[437,4324,4325,4326,4329],{},"File metadata có thể được map sang ",[695,4327,4328],{},"storage.objects"," metadata columns.",[432,4331,4333],{"id":4332},"firebase-security-rules-postgres-rls-policy","Firebase Security Rules -> Postgres RLS Policy",[437,4335,4336],{},"Đây là bước quan trọng khi migrate từ Firebase sang Supabase:",[688,4338,4340],{"className":3240,"code":4339,"language":3242,"meta":693,"style":693},"\u002F\u002F Firebase Security Rules (Firestore)\nrules_version = '2';\nservice cloud.firestore {\n  match \u002Fdatabases\u002F{database}\u002Fdocuments {\n    \u002F\u002F Users can read their own data\n    match \u002Fusers\u002F{userId} {\n      allow read: if request.auth.uid == userId;\n      allow write: if request.auth.uid == userId;\n    }\n    \u002F\u002F Anyone can read published posts\n    match \u002Fposts\u002F{postId} {\n      allow read: if resource.data.published == true;\n      allow write: if request.auth.uid == resource.data.author_id;\n    }\n  }\n}\n",[695,4341,4342,4347,4362,4374,4399,4404,4422,4455,4482,4487,4492,4509,4540,4575,4579,4584],{"__ignoreMap":693},[698,4343,4344],{"class":700,"line":701},[698,4345,4346],{"class":704},"\u002F\u002F Firebase Security Rules (Firestore)\n",[698,4348,4349,4352,4354,4356,4358,4360],{"class":700,"line":708},[698,4350,4351],{"class":1114},"rules_version ",[698,4353,2118],{"class":1186},[698,4355,4262],{"class":1186},[698,4357,870],{"class":715},[698,4359,3891],{"class":1186},[698,4361,3613],{"class":1186},[698,4363,4364,4367,4369,4372],{"class":700,"line":731},[698,4365,4366],{"class":1114},"service cloud",[698,4368,3907],{"class":1186},[698,4370,4371],{"class":1114},"firestore ",[698,4373,2913],{"class":1186},[698,4375,4376,4379,4382,4385,4388,4391,4394,4397],{"class":700,"line":738},[698,4377,4378],{"class":1114},"  match",[698,4380,4381],{"class":1186}," \u002F",[698,4383,4384],{"class":1114},"databases",[698,4386,4387],{"class":1186},"\u002F{",[698,4389,4390],{"class":1114},"database",[698,4392,4393],{"class":1186},"}\u002F",[698,4395,4396],{"class":1114},"documents",[698,4398,4238],{"class":1186},[698,4400,4401],{"class":700,"line":744},[698,4402,4403],{"class":704},"    \u002F\u002F Users can read their own data\n",[698,4405,4406,4409,4411,4413,4415,4418,4420],{"class":700,"line":754},[698,4407,4408],{"class":1114},"    match",[698,4410,4381],{"class":1186},[698,4412,3554],{"class":1114},[698,4414,4387],{"class":1186},[698,4416,4417],{"class":1114},"userId",[698,4419,3938],{"class":1186},[698,4421,4238],{"class":1186},[698,4423,4424,4427,4430,4432,4435,4438,4440,4442,4444,4447,4450,4453],{"class":700,"line":1146},[698,4425,4426],{"class":1114},"      allow",[698,4428,4429],{"class":711}," read",[698,4431,2826],{"class":1186},[698,4433,4434],{"class":3989}," if",[698,4436,4437],{"class":1114}," request",[698,4439,3907],{"class":1186},[698,4441,1898],{"class":1114},[698,4443,3907],{"class":1186},[698,4445,4446],{"class":1114},"uid",[698,4448,4449],{"class":1186}," ==",[698,4451,4452],{"class":1114}," userId",[698,4454,3613],{"class":1186},[698,4456,4457,4459,4462,4464,4466,4468,4470,4472,4474,4476,4478,4480],{"class":700,"line":1161},[698,4458,4426],{"class":1114},[698,4460,4461],{"class":711}," write",[698,4463,2826],{"class":1186},[698,4465,4434],{"class":3989},[698,4467,4437],{"class":1114},[698,4469,3907],{"class":1186},[698,4471,1898],{"class":1114},[698,4473,3907],{"class":1186},[698,4475,4446],{"class":1114},[698,4477,4449],{"class":1186},[698,4479,4452],{"class":1114},[698,4481,3613],{"class":1186},[698,4483,4484],{"class":700,"line":1166},[698,4485,4486],{"class":1186},"    }\n",[698,4488,4489],{"class":700,"line":1172},[698,4490,4491],{"class":704},"    \u002F\u002F Anyone can read published posts\n",[698,4493,4494,4496,4498,4500,4502,4505,4507],{"class":700,"line":1796},[698,4495,4408],{"class":1114},[698,4497,4381],{"class":1186},[698,4499,3404],{"class":1114},[698,4501,4387],{"class":1186},[698,4503,4504],{"class":1114},"postId",[698,4506,3938],{"class":1186},[698,4508,4238],{"class":1186},[698,4510,4511,4513,4515,4517,4519,4522,4524,4527,4529,4532,4534,4538],{"class":700,"line":2177},[698,4512,4426],{"class":1114},[698,4514,4429],{"class":711},[698,4516,2826],{"class":1186},[698,4518,4434],{"class":3989},[698,4520,4521],{"class":1114}," resource",[698,4523,3907],{"class":1186},[698,4525,4526],{"class":1114},"data",[698,4528,3907],{"class":1186},[698,4530,4531],{"class":1114},"published",[698,4533,4449],{"class":1186},[698,4535,4537],{"class":4536},"sfNiH"," true",[698,4539,3613],{"class":1186},[698,4541,4542,4544,4546,4548,4550,4552,4554,4556,4558,4560,4562,4564,4566,4568,4570,4573],{"class":700,"line":2183},[698,4543,4426],{"class":1114},[698,4545,4461],{"class":711},[698,4547,2826],{"class":1186},[698,4549,4434],{"class":3989},[698,4551,4437],{"class":1114},[698,4553,3907],{"class":1186},[698,4555,1898],{"class":1114},[698,4557,3907],{"class":1186},[698,4559,4446],{"class":1114},[698,4561,4449],{"class":1186},[698,4563,4521],{"class":1114},[698,4565,3907],{"class":1186},[698,4567,4526],{"class":1114},[698,4569,3907],{"class":1186},[698,4571,4572],{"class":1114},"author_id",[698,4574,3613],{"class":1186},[698,4576,4577],{"class":700,"line":2188},[698,4578,4486],{"class":1186},[698,4580,4581],{"class":700,"line":2199},[698,4582,4583],{"class":1186},"  }\n",[698,4585,4586],{"class":700,"line":2205},[698,4587,2952],{"class":1186},[437,4589,4590],{},"Tương đương trong Postgres RLS:",[688,4592,4596],{"className":4593,"code":4594,"language":4595,"meta":693,"style":693},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- Policy: Users can read their own data\nCREATE POLICY \"Users can access own data\"\nON users FOR ALL\nUSING (auth.uid() = id)\nWITH CHECK (auth.uid() = id);\n\n-- Policy: Anyone can read published posts\nCREATE POLICY \"Anyone can read published posts\"\nON posts FOR SELECT\nUSING (published = true);\n\n-- Policy: Authors can edit their posts\nCREATE POLICY \"Authors can edit own posts\"\nON posts FOR INSERT\nWITH CHECK (auth.uid() = author_id);\n\nCREATE POLICY \"Authors can update own posts\"\nON posts FOR UPDATE\nUSING (auth.uid() = author_id)\nWITH CHECK (auth.uid() = author_id);\n","sql",[695,4597,4598,4603,4608,4613,4618,4623,4627,4632,4637,4642,4647,4651,4656,4661,4666,4671,4675,4680,4685,4690],{"__ignoreMap":693},[698,4599,4600],{"class":700,"line":701},[698,4601,4602],{},"-- Policy: Users can read their own data\n",[698,4604,4605],{"class":700,"line":708},[698,4606,4607],{},"CREATE POLICY \"Users can access own data\"\n",[698,4609,4610],{"class":700,"line":731},[698,4611,4612],{},"ON users FOR ALL\n",[698,4614,4615],{"class":700,"line":738},[698,4616,4617],{},"USING (auth.uid() = id)\n",[698,4619,4620],{"class":700,"line":744},[698,4621,4622],{},"WITH CHECK (auth.uid() = id);\n",[698,4624,4625],{"class":700,"line":754},[698,4626,735],{"emptyLinePlaceholder":734},[698,4628,4629],{"class":700,"line":1146},[698,4630,4631],{},"-- Policy: Anyone can read published posts\n",[698,4633,4634],{"class":700,"line":1161},[698,4635,4636],{},"CREATE POLICY \"Anyone can read published posts\"\n",[698,4638,4639],{"class":700,"line":1166},[698,4640,4641],{},"ON posts FOR SELECT\n",[698,4643,4644],{"class":700,"line":1172},[698,4645,4646],{},"USING (published = true);\n",[698,4648,4649],{"class":700,"line":1796},[698,4650,735],{"emptyLinePlaceholder":734},[698,4652,4653],{"class":700,"line":2177},[698,4654,4655],{},"-- Policy: Authors can edit their posts\n",[698,4657,4658],{"class":700,"line":2183},[698,4659,4660],{},"CREATE POLICY \"Authors can edit own posts\"\n",[698,4662,4663],{"class":700,"line":2188},[698,4664,4665],{},"ON posts FOR INSERT\n",[698,4667,4668],{"class":700,"line":2199},[698,4669,4670],{},"WITH CHECK (auth.uid() = author_id);\n",[698,4672,4673],{"class":700,"line":2205},[698,4674,735],{"emptyLinePlaceholder":734},[698,4676,4677],{"class":700,"line":2211},[698,4678,4679],{},"CREATE POLICY \"Authors can update own posts\"\n",[698,4681,4682],{"class":700,"line":2216},[698,4683,4684],{},"ON posts FOR UPDATE\n",[698,4686,4687],{"class":700,"line":2222},[698,4688,4689],{},"USING (auth.uid() = author_id)\n",[698,4691,4692],{"class":700,"line":2233},[698,4693,4670],{},[437,4695,4696],{},[465,4697,4698],{},"Mapping table:",[441,4700,4701,4711],{},[444,4702,4703],{},[447,4704,4705,4708],{},[450,4706,4707],{},"Firebase Rules Pattern",[450,4709,4710],{},"Postgres RLS Equivalent",[457,4712,4713,4725,4737,4749,4761,4773,4785],{},[447,4714,4715,4720],{},[462,4716,4717],{},[695,4718,4719],{},"request.auth != null",[462,4721,4722],{},[695,4723,4724],{},"auth.role() = 'authenticated'",[447,4726,4727,4732],{},[462,4728,4729],{},[695,4730,4731],{},"request.auth.uid == userId",[462,4733,4734],{},[695,4735,4736],{},"auth.uid() = user_id",[447,4738,4739,4744],{},[462,4740,4741],{},[695,4742,4743],{},"request.auth.uid == resource.data.owner",[462,4745,4746],{},[695,4747,4748],{},"auth.uid() = owner_id",[447,4750,4751,4756],{},[462,4752,4753],{},[695,4754,4755],{},"resource.data.published == true",[462,4757,4758],{},[695,4759,4760],{},"published = true",[447,4762,4763,4768],{},[462,4764,4765],{},[695,4766,4767],{},"request.auth.uid in resource.data.members",[462,4769,4770],{},[695,4771,4772],{},"auth.uid() = ANY(members)",[447,4774,4775,4780],{},[462,4776,4777],{},[695,4778,4779],{},"request.auth.token.role == 'admin'",[462,4781,4782],{},[695,4783,4784],{},"auth.jwt()->>'role' = 'admin'",[447,4786,4787,4792],{},[462,4788,4789],{},[695,4790,4791],{},"allow read: if true",[462,4793,4794],{},[695,4795,4796],{},"CREATE POLICY ... FOR SELECT USING (true)",[432,4798,4800],{"id":4799},"amazon-rds-postgres-supabase","Amazon RDS Postgres -> Supabase",[688,4802,4804],{"className":690,"code":4803,"language":692,"meta":693,"style":693},"# Export từ RDS\npg_dump -h [RDS_HOST] -U [USER] -d [DB_NAME] \\\n  --no-owner --no-acl -f dump.sql\n\n# Import vào Supabase\npsql -h [SUPABASE_HOST] -U postgres -d postgres -f dump.sql\n",[695,4805,4806,4811,4841,4854,4858,4863],{"__ignoreMap":693},[698,4807,4808],{"class":700,"line":701},[698,4809,4810],{"class":704},"# Export từ RDS\n",[698,4812,4813,4816,4819,4822,4824,4827,4829,4832,4834,4837,4839],{"class":700,"line":708},[698,4814,4815],{"class":711},"pg_dump",[698,4817,4818],{"class":715}," -h",[698,4820,4821],{"class":1114}," [RDS_HOST] -U ",[698,4823,1187],{"class":1186},[698,4825,4826],{"class":1114},"USER",[698,4828,3983],{"class":1186},[698,4830,4831],{"class":1114}," -d ",[698,4833,1187],{"class":1186},[698,4835,4836],{"class":1114},"DB_NAME",[698,4838,3983],{"class":1186},[698,4840,1126],{"class":1114},[698,4842,4843,4846,4849,4851],{"class":700,"line":731},[698,4844,4845],{"class":711},"  --no-owner",[698,4847,4848],{"class":715}," --no-acl",[698,4850,1805],{"class":715},[698,4852,4853],{"class":715}," dump.sql\n",[698,4855,4856],{"class":700,"line":738},[698,4857,735],{"emptyLinePlaceholder":734},[698,4859,4860],{"class":700,"line":744},[698,4861,4862],{"class":704},"# Import vào Supabase\n",[698,4864,4865,4868,4870],{"class":700,"line":754},[698,4866,4867],{"class":711},"psql",[698,4869,4818],{"class":715},[698,4871,4872],{"class":1114}," [SUPABASE_HOST] -U postgres -d postgres -f dump.sql\n",[432,4874,4876],{"id":4875},"heroku-postgres-supabase","Heroku Postgres -> Supabase",[688,4878,4880],{"className":690,"code":4879,"language":692,"meta":693,"style":693},"# Từ Heroku\nheroku pg:backups:capture --app your-app\nheroku pg:backups:download --app your-app\n\n# Import vào Supabase\npg_restore -h [SUPABASE_HOST] -U postgres -d postgres latest.dump\n",[695,4881,4882,4887,4901,4912,4916,4920],{"__ignoreMap":693},[698,4883,4884],{"class":700,"line":701},[698,4885,4886],{"class":704},"# Từ Heroku\n",[698,4888,4889,4892,4895,4898],{"class":700,"line":708},[698,4890,4891],{"class":711},"heroku",[698,4893,4894],{"class":715}," pg:backups:capture",[698,4896,4897],{"class":715}," --app",[698,4899,4900],{"class":715}," your-app\n",[698,4902,4903,4905,4908,4910],{"class":700,"line":731},[698,4904,4891],{"class":711},[698,4906,4907],{"class":715}," pg:backups:download",[698,4909,4897],{"class":715},[698,4911,4900],{"class":715},[698,4913,4914],{"class":700,"line":738},[698,4915,735],{"emptyLinePlaceholder":734},[698,4917,4918],{"class":700,"line":744},[698,4919,4862],{"class":704},[698,4921,4922,4925,4927],{"class":700,"line":754},[698,4923,4924],{"class":711},"pg_restore",[698,4926,4818],{"class":715},[698,4928,4929],{"class":1114}," [SUPABASE_HOST] -U postgres -d postgres latest.dump\n",[432,4931,4933],{"id":4932},"mysql-supabase","MySQL -> Supabase",[437,4935,4936],{},"Do MySQL và PostgreSQL khác nhau về syntax và data types:",[3859,4938,4939,4945,4956,5005,5008],{},[589,4940,4941,4942],{},"Export MySQL schema và data qua ",[695,4943,4944],{},"mysqldump",[589,4946,4947,4948,4951,4952,4955],{},"Dùng ",[465,4949,4950],{},"pgloader"," hoặc ",[695,4953,4954],{},"mysql-to-postgres"," converter",[589,4957,4958,4959],{},"Điều chỉnh data types:\n",[586,4960,4961,4974,4982,4989,4997],{},[589,4962,4963,4966,4967,4970,4971],{},[695,4964,4965],{},"AUTO_INCREMENT"," -> ",[695,4968,4969],{},"SERIAL"," \u002F ",[695,4972,4973],{},"BIGSERIAL",[589,4975,4976,4966,4979],{},[695,4977,4978],{},"TINYINT",[695,4980,4981],{},"SMALLINT",[589,4983,4984,4966,4987],{},[695,4985,4986],{},"DATETIME",[695,4988,3797],{},[589,4990,4991,4966,4994,4996],{},[695,4992,4993],{},"TEXT",[695,4995,4993],{}," (giữ nguyên)",[589,4998,4999,4966,5002],{},[695,5000,5001],{},"ENUM",[695,5003,5004],{},"CREATE TYPE ... AS ENUM",[589,5006,5007],{},"Convert stored procedures từ MySQL procedural SQL sang PL\u002FpgSQL",[589,5009,5010],{},"Import vào Supabase",[432,5012,5014],{"id":5013},"vercel-postgres-neon-supabase","Vercel Postgres \u002F Neon -> Supabase",[437,5016,5017],{},"Cả hai đều là PostgreSQL -- đơn giản nhất:",[688,5019,5021],{"className":690,"code":5020,"language":692,"meta":693,"style":693},"# Pull schema từ Vercel\u002FNeon\npg_dump -h [HOST] -U [USER] -d [DB] --schema-only -f schema.sql\n\n# Push vào Supabase qua migration\nsupabase db push\n",[695,5022,5023,5028,5055,5059,5064],{"__ignoreMap":693},[698,5024,5025],{"class":700,"line":701},[698,5026,5027],{"class":704},"# Pull schema từ Vercel\u002FNeon\n",[698,5029,5030,5032,5034,5037,5039,5041,5043,5045,5047,5050,5052],{"class":700,"line":708},[698,5031,4815],{"class":711},[698,5033,4818],{"class":715},[698,5035,5036],{"class":1114}," [HOST] -U ",[698,5038,1187],{"class":1186},[698,5040,4826],{"class":1114},[698,5042,3983],{"class":1186},[698,5044,4831],{"class":1114},[698,5046,1187],{"class":1186},[698,5048,5049],{"class":1114},"DB",[698,5051,3983],{"class":1186},[698,5053,5054],{"class":1114}," --schema-only -f schema.sql\n",[698,5056,5057],{"class":700,"line":731},[698,5058,735],{"emptyLinePlaceholder":734},[698,5060,5061],{"class":700,"line":738},[698,5062,5063],{"class":704},"# Push vào Supabase qua migration\n",[698,5065,5066,5068,5071],{"class":700,"line":744},[698,5067,712],{"class":711},[698,5069,5070],{"class":715}," db",[698,5072,5073],{"class":715}," push\n",[432,5075,5077],{"id":5076},"mssql-sql-server-supabase","MSSQL \u002F SQL Server -> Supabase",[437,5079,5080],{},"Cần convert từ T-SQL sang PL\u002FpgSQL:",[3859,5082,5083,5086,5104,5107,5116],{},[589,5084,5085],{},"Export schema definition từ SQL Server",[589,5087,5088,5089,4966,5092,2507,5094,4966,5096,2507,5098,4966,5101],{},"Convert data types: ",[695,5090,5091],{},"NVARCHAR",[695,5093,4993],{},[695,5095,4986],{},[695,5097,3797],{},[695,5099,5100],{},"BIT",[695,5102,5103],{},"BOOLEAN",[589,5105,5106],{},"Convert stored procedures từ T-SQL sang PL\u002FpgSQL",[589,5108,5109,5110,4966,5113,5115],{},"Xử lý differences: ",[695,5111,5112],{},"IDENTITY",[695,5114,4969],{},", computed columns -> generated columns",[589,5117,5118],{},"Dùng migration tool để import vào Supabase",[784,5120,5121,5124],{"color":786,"icon":787},[465,5122,5123],{},"Key insight:"," Khi migrate từ NoSQL (Firestore) sang SQL (Postgres), việc thiết kế lại schema là không thể tránh khỏi. Password hash cần được bảo toàn -- Firebase dùng scrypt, Supabase cũng hỗ trợ scrypt qua auth hooks.",[784,5126,5127,5130],{"color":786,"icon":787},[465,5128,5129],{},"Interview point — Firebase to Supabase Migration:"," Ba phần khó nhất: (1) Password hash preservation — cần giữ nguyên scrypt parameters, (2) Schema redesign — NoSQL nested data phải được normalize thành relational tables, (3) Security Rules to RLS mapping — Firebase rules syntax khác hoàn toàn với SQL RLS policies.",[1706,5132],{},[427,5134,5136],{"id":5135},"công-cụ-sản-phẩm-bổ-sung","Công cụ & Sản phẩm bổ sung",[432,5138,5140],{"id":5139},"cron-pg_cron","Cron (pg_cron)",[437,5142,5143],{},"Lập lịch jobs định kỳ với cron syntax ngay trong Postgres:",[688,5145,5147],{"className":4593,"code":5146,"language":4595,"meta":693,"style":693},"-- Bật extension\nCREATE EXTENSION IF NOT EXISTS pg_cron;\n\n-- Job chạy SQL mỗi phút -- dọn dữ liệu tạm cũ\nSELECT cron.schedule(\n  'cleanup-temp',\n  '* * * * *',\n  'DELETE FROM temp_data WHERE created_at \u003C now() - interval ''1 hour'''\n);\n\n-- Job gọi Edge Function mỗi giờ\nSELECT cron.schedule(\n  'hourly-sync',\n  '0 * * * *',\n  $$\n  SELECT net.http_post(\n    url := 'https:\u002F\u002F[REF].supabase.co\u002Ffunctions\u002Fv1\u002Fsync-data',\n    headers := '{\"Authorization\": \"Bearer [SERVICE_ROLE_KEY]\"}'::jsonb\n  )\n  $$\n);\n\n-- Quản lý jobs\nSELECT * FROM cron.job;                              -- Danh sách jobs\nSELECT * FROM cron.job_run_details;                  -- Lịch sử chạy\nSELECT cron.unschedule('cleanup-temp');               -- Hủy job\n",[695,5148,5149,5154,5159,5163,5168,5173,5178,5183,5188,5193,5197,5202,5206,5211,5216,5221,5226,5231,5236,5241,5245,5249,5253,5258,5263,5268],{"__ignoreMap":693},[698,5150,5151],{"class":700,"line":701},[698,5152,5153],{},"-- Bật extension\n",[698,5155,5156],{"class":700,"line":708},[698,5157,5158],{},"CREATE EXTENSION IF NOT EXISTS pg_cron;\n",[698,5160,5161],{"class":700,"line":731},[698,5162,735],{"emptyLinePlaceholder":734},[698,5164,5165],{"class":700,"line":738},[698,5166,5167],{},"-- Job chạy SQL mỗi phút -- dọn dữ liệu tạm cũ\n",[698,5169,5170],{"class":700,"line":744},[698,5171,5172],{},"SELECT cron.schedule(\n",[698,5174,5175],{"class":700,"line":754},[698,5176,5177],{},"  'cleanup-temp',\n",[698,5179,5180],{"class":700,"line":1146},[698,5181,5182],{},"  '* * * * *',\n",[698,5184,5185],{"class":700,"line":1161},[698,5186,5187],{},"  'DELETE FROM temp_data WHERE created_at \u003C now() - interval ''1 hour'''\n",[698,5189,5190],{"class":700,"line":1166},[698,5191,5192],{},");\n",[698,5194,5195],{"class":700,"line":1172},[698,5196,735],{"emptyLinePlaceholder":734},[698,5198,5199],{"class":700,"line":1796},[698,5200,5201],{},"-- Job gọi Edge Function mỗi giờ\n",[698,5203,5204],{"class":700,"line":2177},[698,5205,5172],{},[698,5207,5208],{"class":700,"line":2183},[698,5209,5210],{},"  'hourly-sync',\n",[698,5212,5213],{"class":700,"line":2188},[698,5214,5215],{},"  '0 * * * *',\n",[698,5217,5218],{"class":700,"line":2199},[698,5219,5220],{},"  $$\n",[698,5222,5223],{"class":700,"line":2205},[698,5224,5225],{},"  SELECT net.http_post(\n",[698,5227,5228],{"class":700,"line":2211},[698,5229,5230],{},"    url := 'https:\u002F\u002F[REF].supabase.co\u002Ffunctions\u002Fv1\u002Fsync-data',\n",[698,5232,5233],{"class":700,"line":2216},[698,5234,5235],{},"    headers := '{\"Authorization\": \"Bearer [SERVICE_ROLE_KEY]\"}'::jsonb\n",[698,5237,5238],{"class":700,"line":2222},[698,5239,5240],{},"  )\n",[698,5242,5243],{"class":700,"line":2233},[698,5244,5220],{},[698,5246,5247],{"class":700,"line":2239},[698,5248,5192],{},[698,5250,5251],{"class":700,"line":2244},[698,5252,735],{"emptyLinePlaceholder":734},[698,5254,5255],{"class":700,"line":2255},[698,5256,5257],{},"-- Quản lý jobs\n",[698,5259,5260],{"class":700,"line":2261},[698,5261,5262],{},"SELECT * FROM cron.job;                              -- Danh sách jobs\n",[698,5264,5265],{"class":700,"line":2266},[698,5266,5267],{},"SELECT * FROM cron.job_run_details;                  -- Lịch sử chạy\n",[698,5269,5270],{"class":700,"line":2280},[698,5271,5272],{},"SELECT cron.unschedule('cleanup-temp');               -- Hủy job\n",[437,5274,5275,5278],{},[465,5276,5277],{},"Limits:"," Khuyến nghị không quá 8 jobs chạy đồng thời, mỗi job không quá 10 phút.",[432,5280,5282],{"id":5281},"queues-pgmq","Queues (pgmq)",[437,5284,5285],{},"Message queue bền vững trong Postgres -- không cần Redis hay RabbitMQ riêng:",[688,5287,5289],{"className":4593,"code":5288,"language":4595,"meta":693,"style":693},"-- Bật extension + tạo queue\nCREATE EXTENSION IF NOT EXISTS pgmq;\nSELECT pgmq.create('task_queue');\n\n-- Gửi message\nSELECT pgmq.send('task_queue', '{\"task\": \"send_email\", \"to\": \"user@example.com\", \"template\": \"welcome\"}');\n\n-- Consumer: đọc message (visibility timeout 60 giây)\nSELECT * FROM pgmq.read('task_queue', 10, 60);\n\n-- Xác nhận đã xử lý\nSELECT pgmq.delete('task_queue', msg_id);\n\n-- Archive message đã xử lý (giữ lại để audit)\nSELECT pgmq.archive('task_queue', msg_id);\n\n-- Pop (đọc + xóa atomic)\nSELECT * FROM pgmq.pop('task_queue');\n",[695,5290,5291,5296,5301,5306,5310,5315,5320,5324,5329,5334,5338,5343,5348,5352,5357,5362,5366,5371],{"__ignoreMap":693},[698,5292,5293],{"class":700,"line":701},[698,5294,5295],{},"-- Bật extension + tạo queue\n",[698,5297,5298],{"class":700,"line":708},[698,5299,5300],{},"CREATE EXTENSION IF NOT EXISTS pgmq;\n",[698,5302,5303],{"class":700,"line":731},[698,5304,5305],{},"SELECT pgmq.create('task_queue');\n",[698,5307,5308],{"class":700,"line":738},[698,5309,735],{"emptyLinePlaceholder":734},[698,5311,5312],{"class":700,"line":744},[698,5313,5314],{},"-- Gửi message\n",[698,5316,5317],{"class":700,"line":754},[698,5318,5319],{},"SELECT pgmq.send('task_queue', '{\"task\": \"send_email\", \"to\": \"user@example.com\", \"template\": \"welcome\"}');\n",[698,5321,5322],{"class":700,"line":1146},[698,5323,735],{"emptyLinePlaceholder":734},[698,5325,5326],{"class":700,"line":1161},[698,5327,5328],{},"-- Consumer: đọc message (visibility timeout 60 giây)\n",[698,5330,5331],{"class":700,"line":1166},[698,5332,5333],{},"SELECT * FROM pgmq.read('task_queue', 10, 60);\n",[698,5335,5336],{"class":700,"line":1172},[698,5337,735],{"emptyLinePlaceholder":734},[698,5339,5340],{"class":700,"line":1796},[698,5341,5342],{},"-- Xác nhận đã xử lý\n",[698,5344,5345],{"class":700,"line":2177},[698,5346,5347],{},"SELECT pgmq.delete('task_queue', msg_id);\n",[698,5349,5350],{"class":700,"line":2183},[698,5351,735],{"emptyLinePlaceholder":734},[698,5353,5354],{"class":700,"line":2188},[698,5355,5356],{},"-- Archive message đã xử lý (giữ lại để audit)\n",[698,5358,5359],{"class":700,"line":2199},[698,5360,5361],{},"SELECT pgmq.archive('task_queue', msg_id);\n",[698,5363,5364],{"class":700,"line":2205},[698,5365,735],{"emptyLinePlaceholder":734},[698,5367,5368],{"class":700,"line":2211},[698,5369,5370],{},"-- Pop (đọc + xóa atomic)\n",[698,5372,5373],{"class":700,"line":2216},[698,5374,5375],{},"SELECT * FROM pgmq.pop('task_queue');\n",[437,5377,5378],{},[465,5379,5380],{},"Đặc điểm:",[586,5382,5383,5389,5395,5401],{},[589,5384,5385,5388],{},[465,5386,5387],{},"Guaranteed delivery",": Message được đảm bảo gửi đến consumer",[589,5390,5391,5394],{},[465,5392,5393],{},"Exactly-once delivery",": Trong visibility window",[589,5396,5397,5400],{},[465,5398,5399],{},"RLS integration",": Phân quyền truy cập queue bằng RLS policies",[589,5402,5403,5406],{},[465,5404,5405],{},"Dashboard management",": Quản lý queues và messages trong Dashboard",[432,5408,5410],{"id":5409},"analytics-buckets","Analytics Buckets",[437,5412,5413,5414,5417],{},"Lưu dữ liệu trong ",[465,5415,5416],{},"Apache Iceberg"," format, query từ Postgres qua foreign tables. Tối ưu cho data lakes, logs, và Pipelines.",[432,5419,5421],{"id":5420},"log-drains-pro-tier","Log Drains (Pro tier)",[437,5423,5424,5425,3907],{},"Export logs ra external services: ",[465,5426,5427],{},"Datadog, Grafana Loki, Sentry, S3, Better Stack, Logtail, Axiom",[432,5429,5431],{"id":5430},"supabase-ui","Supabase UI",[437,5433,5434],{},"Pre-built React components cho Supabase:",[688,5436,5438],{"className":690,"code":5437,"language":692,"meta":693,"style":693},"npm install @supabase\u002Fauth-ui-react @supabase\u002Fauth-ui-shared\n",[695,5439,5440],{"__ignoreMap":693},[698,5441,5442,5445,5447,5450],{"class":700,"line":701},[698,5443,5444],{"class":711},"npm",[698,5446,2548],{"class":715},[698,5448,5449],{"class":715}," @supabase\u002Fauth-ui-react",[698,5451,5452],{"class":715}," @supabase\u002Fauth-ui-shared\n",[432,5454,5456],{"id":5455},"mcp-server","MCP Server",[437,5458,5459],{},"Cho phép AI agents (Claude Code, Cursor, Windsurf) tương tác với Supabase:",[688,5461,5463],{"className":690,"code":5462,"language":692,"meta":693,"style":693},"npx skills add supabase\u002Fagent-skills\n",[695,5464,5465],{"__ignoreMap":693},[698,5466,5467,5470,5473,5475],{"class":700,"line":701},[698,5468,5469],{"class":711},"npx",[698,5471,5472],{"class":715}," skills",[698,5474,2535],{"class":715},[698,5476,5477],{"class":715}," supabase\u002Fagent-skills\n",[432,5479,5481],{"id":5480},"partner-gallery-integrations","Partner Gallery -- Integrations",[586,5483,5484,5490,5496,5502,5507,5513],{},[589,5485,5486,5489],{},[465,5487,5488],{},"Prisma, Drizzle",": ORM cho database",[589,5491,5492,5495],{},[465,5493,5494],{},"Resend, SendGrid",": Email service",[589,5497,5498,5501],{},[465,5499,5500],{},"Stripe",": Payment processing",[589,5503,5504,5506],{},[465,5505,1044],{},": Error monitoring",[589,5508,5509,5512],{},[465,5510,5511],{},"Vercel, Netlify",": Hosting",[589,5514,5515,5518],{},[465,5516,5517],{},"Auth0, Clerk, AWS Cognito",": Third-party auth providers",[1706,5520],{},[427,5522,5524],{"id":5523},"bài-tập-thực-hành","Bài tập thực hành",[5526,5527,5528,5672],"field-group",{},[5529,5530,5532,5535,5595,5598,5621,5627],"field",{"name":5531},"Bài tập 1: Setup local dev environment với Supabase CLI",[437,5533,5534],{},"Xây dựng môi trường phát triển local đầy đủ:",[3859,5536,5537,5543,5551,5559,5565,5574,5580,5586],{},[589,5538,5539,5542],{},[465,5540,5541],{},"Cài đặt Supabase CLI"," và Docker Desktop",[589,5544,5545,5550],{},[465,5546,5547],{},[695,5548,5549],{},"supabase init"," để khởi tạo project",[589,5552,5553,5558],{},[465,5554,5555],{},[695,5556,5557],{},"supabase start"," để khởi động toàn bộ stack local",[589,5560,5561,5564],{},[465,5562,5563],{},"Tạo migration"," cho schema blog (bảng posts, authors, comments)",[589,5566,5567,5570,5571],{},[465,5568,5569],{},"Thêm seed data"," với file ",[695,5572,5573],{},"supabase\u002Fseed.sql",[589,5575,5576,5579],{},[465,5577,5578],{},"Generate TypeScript types"," từ schema local",[589,5581,5582,5585],{},[465,5583,5584],{},"Tạo Edge Function"," test thử trên local",[589,5587,5588,5591,5592],{},[465,5589,5590],{},"Snapshot"," môi trường local với ",[695,5593,5594],{},"supabase db dump",[437,5596,5597],{},"Verify bằng cách:",[586,5599,5600,5606,5609,5615],{},[589,5601,5602,5603],{},"Truy cập Studio Dashboard tại ",[695,5604,5605],{},"http:\u002F\u002Flocalhost:54323",[589,5607,5608],{},"Chạy SQL query trong SQL Editor local",[589,5610,5611,5612],{},"Test API endpoint tại ",[695,5613,5614],{},"http:\u002F\u002Flocalhost:54321\u002Frest\u002Fv1\u002F",[589,5616,5617,5618],{},"Gọi Edge Function local qua ",[695,5619,5620],{},"http:\u002F\u002Flocalhost:54321\u002Ffunctions\u002Fv1\u002F",[437,5622,5623,5624,2826],{},"Gợi ý ",[695,5625,5626],{},"supabase\u002Fconfig.toml",[688,5628,5632],{"className":5629,"code":5630,"language":5631,"meta":693,"style":693},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[db]\nport = 54322\n\n[studio]\nport = 54323\n\n[api]\nport = 54321\n","toml",[695,5633,5634,5639,5644,5648,5653,5658,5662,5667],{"__ignoreMap":693},[698,5635,5636],{"class":700,"line":701},[698,5637,5638],{},"[db]\n",[698,5640,5641],{"class":700,"line":708},[698,5642,5643],{},"port = 54322\n",[698,5645,5646],{"class":700,"line":731},[698,5647,735],{"emptyLinePlaceholder":734},[698,5649,5650],{"class":700,"line":738},[698,5651,5652],{},"[studio]\n",[698,5654,5655],{"class":700,"line":744},[698,5656,5657],{},"port = 54323\n",[698,5659,5660],{"class":700,"line":754},[698,5661,735],{"emptyLinePlaceholder":734},[698,5663,5664],{"class":700,"line":1146},[698,5665,5666],{},"[api]\n",[698,5668,5669],{"class":700,"line":1161},[698,5670,5671],{},"port = 54321\n",[5529,5673,5675,5678,5683,5708,5713,5727,5732,5749],{"name":5674},"Bài tập 2: Migration plan từ Firebase sang Supabase",[437,5676,5677],{},"Thiết kế kế hoạch migration cho một mobile app React Native đang dùng Firebase:",[437,5679,5680],{},[465,5681,5682],{},"Hiện trạng Firebase:",[586,5684,5685,5702,5705],{},[589,5686,5687,5688,2507,5690,2507,5692,2507,5695,5698,5699],{},"Firestore: ",[695,5689,3554],{},[695,5691,3404],{},[695,5693,5694],{},"comments",[695,5696,5697],{},"likes"," (nested trong posts), ",[695,5700,5701],{},"notifications",[589,5703,5704],{},"Firebase Auth: Email\u002FPassword + Google OAuth",[589,5706,5707],{},"Firebase Storage: Avatar users, post images",[437,5709,5710],{},[465,5711,5712],{},"Yêu cầu sau migration:",[586,5714,5715,5718,5721,5724],{},[589,5716,5717],{},"Tất cả dữ liệu phải được migrate không mất mát",[589,5719,5720],{},"Người dùng không cần đặt lại mật khẩu",[589,5722,5723],{},"App hoạt động với cả Firebase và Supabase trong giai đoạn chuyển tiếp",[589,5725,5726],{},"Downtime tối đa 30 phút",[437,5728,5729],{},[465,5730,5731],{},"Lập kế hoạch chi tiết gồm:",[586,5733,5734,5737,5740,5743,5746],{},[589,5735,5736],{},"Thiết kế schema mới (ERD) cho Postgres",[589,5738,5739],{},"Chiến lược migrate auth users (giữ password hash)",[589,5741,5742],{},"Script transform dữ liệu Firestore sang SQL",[589,5744,5745],{},"Kế hoạch dual-write trong giai đoạn chuyển tiếp",[589,5747,5748],{},"Timeline và rollback plan",[437,5750,5751,5752,5755],{},"Gợi ý: Vẽ bản đồ ",[695,5753,5754],{},"Firestore collection -> Postgres table",", xác định nested data cần flatten, và thiết kế foreign key relationships.",[1706,5757],{},[5759,5760],"navigation",{":next":5761,":prev":5761,"nextIcon":196,"nextTitle":284,"nextTo":5762,"prevIcon":181,"prevTitle":275,"prevTo":5763},"true",".\u002F7.interview",".\u002F5.api-sdk",[5765,5766,5767],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":693,"searchDepth":708,"depth":708,"links":5769},[5770,5782,5793,5804,5813],{"id":429,"depth":708,"text":430,"children":5771},[5772,5773,5774,5775,5776,5777,5778,5779,5780,5781],{"id":434,"depth":731,"text":435},{"id":583,"depth":731,"text":584},{"id":635,"depth":731,"text":636},{"id":678,"depth":731,"text":679},{"id":798,"depth":731,"text":799},{"id":1208,"depth":731,"text":1209},{"id":1446,"depth":731,"text":1447},{"id":1491,"depth":731,"text":1492},{"id":1552,"depth":731,"text":1553},{"id":1692,"depth":731,"text":1693},{"id":1710,"depth":708,"text":1711,"children":5783},[5784,5785,5786,5787,5788,5789,5790,5791,5792],{"id":1714,"depth":731,"text":1715},{"id":1817,"depth":731,"text":1818},{"id":2092,"depth":731,"text":2093},{"id":2515,"depth":731,"text":2516},{"id":2556,"depth":731,"text":2557},{"id":2563,"depth":731,"text":2564},{"id":2694,"depth":731,"text":2695},{"id":2727,"depth":731,"text":2728},{"id":2789,"depth":731,"text":2790},{"id":2810,"depth":708,"text":2811,"children":5794},[5795,5796,5797,5798,5799,5800,5801,5802,5803],{"id":2814,"depth":731,"text":2815},{"id":3164,"depth":731,"text":3165},{"id":3851,"depth":731,"text":3852},{"id":4332,"depth":731,"text":4333},{"id":4799,"depth":731,"text":4800},{"id":4875,"depth":731,"text":4876},{"id":4932,"depth":731,"text":4933},{"id":5013,"depth":731,"text":5014},{"id":5076,"depth":731,"text":5077},{"id":5135,"depth":708,"text":5136,"children":5805},[5806,5807,5808,5809,5810,5811,5812],{"id":5139,"depth":731,"text":5140},{"id":5281,"depth":731,"text":5282},{"id":5409,"depth":731,"text":5410},{"id":5420,"depth":731,"text":5421},{"id":5430,"depth":731,"text":5431},{"id":5455,"depth":731,"text":5456},{"id":5480,"depth":731,"text":5481},{"id":5523,"depth":708,"text":5524},"Supabase Platform — Studio, organizations, branching, logs, self-hosting Docker\u002FK8s. Migration từ Firebase, Heroku, RDS, MySQL.","md",null,{},{"title":279,"icon":282},{"title":422,"description":5814},"yVH4CYsHFqWqzSpKw22asAb8eMlgJhmvD7yBZV4J0nY",[5822,5824],{"title":275,"path":276,"stem":277,"description":5823,"icon":181,"children":-1},"Supabase API reference — REST API (PostgREST), GraphQL (pg_graphql), Client SDKs (JS, Flutter, Python, C#, Swift, Kotlin), CLI.",{"title":284,"path":285,"stem":286,"description":5825,"icon":196,"children":-1},"So sánh Supabase vs Firebase vs Neon vs PlanetScale. Best practices, tips phỏng vấn, 15+ câu hỏi phỏng vấn Supabase.",1783490369159]