AWS Amplify Hosting ã«ããããAmplify ã¢ããªã§ã«ã¹ã¿ã ãã¡ã€ã³ã䜿çšããéã®ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã®äžè¬æäŸãçºè¡šããããšãã§ãå¬ããæããŸããããã¯ãSoftware as a Service (SaaS) ããã«ãããã³ããã©ãããã©ãŒã ã§ããŠãŒã¶ãŒã«ã«ã¹ã¿ãã€ãºãããäœéšãæäŸããéçºè
ã«ãšã£ãŠéèŠã§ãã ãã®æ°æ©èœã¯ãéçã¢ããªãã·ã³ã°ã«ããŒãžã¢ããªã±ãŒã·ã§ã³ (SPA)ãNext.js ã䜿çšãããã«ã¹ã¿ãã¯ãµãŒããŒãµã€ãã¬ã³ããªã³ã°ã¢ããªãªã©ãã«ã¹ã¿ã ãã¡ã€ã³ã䜿çšã㊠Amplify Hosting ã«ãããã€ãããä»»æã®ã¢ããªã§å©çšå¯èœã§ãããã®æ©èœã«ãããåçãªã客æ§åºæã®ãµããã¡ã€ã³ãäœæããããã»ã¹ãç°¡çŽ åãããã ãã§ãªããã¢ããªã®ã«ã¹ã¿ãã€ãºã®å¯èœæ§ãåºãããŸããã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã§ã¯ãâ*â ã¯ã€ã«ãã«ãŒãã䜿çšããŠããã©ãã£ãã¯ã Amplify ã¢ããªã®ãã©ã³ãã«ã«ãŒãã£ã³ã°ãã âcatch-allâ ãµããã¡ã€ã³ãäœæã§ããŸããããã¯ãç¬èªã®ãŠããŒã¯ãªãµããã¡ã€ã³èå¥åãå¿
èŠãšãã SaaS ã¢ããªã§äžè¬çãªãã¿ãŒã³ã§ãããã客æ§ãã¢ã«ãŠã³ãã®ãªã³ããŒãã£ã³ã°ïŒããã³ãªãããŒãã£ã³ã°ïŒæã«ã¢ããªãæè»ã«å¯Ÿå¿ã§ããããã«ããŸãã ãœãªã¥ãŒã·ã§ã³ã®æŠèŠ ãã®èšäºã§ã¯ãã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã掻çšãã Next.js ãµãŒããŒãµã€ãã¬ã³ããªã³ã° (SSR) ã¢ããªã Amplify Hosting ã§æ§ç¯ããæ¹æ³ã玹ä»ããŸããNext.js ããã«ãŠã§ã¢ã§ãµããã¡ã€ã³èå¥åãååŸããããã»ã¹ãšããã®æ©èœãã¢ããªã®ã¢ãŒããã¯ãã£ã«çµ±åããæ¹æ³ãèŠãŠãããŸãã å
·äœçãªäŸãšããŠãæå°éã®æ©èœãæã€ âLink in Bioâ ã¿ã€ãã®ã¢ããªãæ§ç¯ããŸãããã®ã¢ããªã¯ Amplify ã®èªèšŒãš GraphQL API ã䜿ã£ãŠãåå¥ã®ãµããã¡ã€ã³ã«ãŒããšããŠå³åº§ã«ã¢ã¯ã»ã¹å¯èœãªãŠãŒã¶ãŒã¢ã«ãŠã³ããäœæããŸãããã®ã¢ãŒããã¯ãã£ãã¿ãŒã³ã¯ãããã°ãe ã³ããŒã¹ãã«ã¹ã¿ã ãŠã§ããµã€ããã©ãããã©ãŒã ãªã©ãåäžã®ã³ãŒãããŒã¹ã§ç°ãªããµããã¡ã€ã³ã«ããã£ãŠè€æ°ã®é¡§å®¢ã«ãµãŒãã¹ãæäŸããæ§ã
ãªãã«ãããã³ãã¢ããªã«æ¡åŒµããããšãã§ããŸãã ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã䜿ã£ã Next.js ã¢ããªã®ãããã€ åææ¡ä»¶ ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ãå©çšããããã«å¿
èŠãªãã®ã¯ä»¥äžã®éãã§ãã ã¢ããªã«äœ¿çšããã«ã¹ã¿ã ãã¡ã€ã³ ãã®ãã¡ã€ã³ã® DNS èšå®ãžã®ã¢ã¯ã»ã¹ ãŸããã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ãå©çšãã Next.js SSR ã¢ããªã AWS Amplify Hosting ã«ãããã€ããŸãããã®ã»ããã¢ããã«ãããã¢ããªã¯åçã« *.example.com ã®ãªã¯ãšã¹ããåŠçã§ããããã«ãªããŸããã¢ã¹ã¿ãªã¹ã¯ (â*â) ã¯ããªã¯ãšã¹ãã«å«ãŸããæå¹ãªå€ã®ã©ãã«ããããããŸãã ãããã€ã«ã¯ GitHub ã䜿ãã®ã§ãã³ãŒãã®å€æŽã GitHub ãªããžããªã«ããã·ã¥ããAmplify Hosting CI/CD ã䜿ã£ãŠã¢ããªãäœæãããªããžããªã«æ¥ç¶ããŠã¢ããªããã«ãããŸãã æ°ãã Next.js ã¢ããªã®äœæ ãŸãã create-next-app ã䜿çšããŠããã©ã«ãã® Next.js SSR ã¢ããªãäœæããŸãããã®ã¢ããªã¯ App Router ã䜿çšããŸãã npx create-next-app@latest with-wildcard-subdomains --app åèãŸã§ã«ã以äžã¯ package.json ã§ãããããžã§ã¯ãã®æ§æã¯ SSR çšã«ããå¿
èŠããããŸãã scripts ã»ã¯ã·ã§ã³ã¯ä»¥äžã®ããã«ããŸãã { "name": "with-wildcard-subdomains", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "next": "^14.0.1", "react": "^18", "react-dom": "^18" }, "devDependencies": { "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10", "eslint": "^8", "eslint-config-next": "13.5.6", "postcss": "^8", "tailwindcss": "^3", "typescript": "^5" } } GitHub ã«æ°ãããªããžããªãäœæããæ°ããäœæããããããžã§ã¯ããããã«ããã·ã¥ããŸãã Amplify Hosting ã§æ°ããã¢ããªããããã€ãã Amplify Hosting ã® Host a web app ãããŒã䜿çšããŸããã¢ããªãäœæããéã«ããã€ãèšå®ããŸãã ãŸããGitHub ã¢ã«ãŠã³ãå
ã®ã¢ããªã®ãªããžããªã«æ¥ç¶ããŸããæ¥ç¶ãããšãAmplify Hosting ãããªãã®ã¢ã«ãŠã³ãã®ãªããžããªããã©ãŒã¯ãããããã€ã確èªããããæ±ããŸãã ã¢ããªã ãµãŒããŒãµã€ãã¬ã³ããªã³ã°ãããã€ã¡ã³ã ãšããŠèªèãããŠããããšã確èªããŸãããã«ãã³ãã³ã㯠npm run build ã baseDirectory ã®å€ã¯ .next ã«ãªã£ãŠããå¿
èŠããããŸãã ãµãŒããŒãµã€ãã¬ã³ããªã³ã°ã®ãã°çšã® IAM ããŒã«ãéžæãŸãã¯äœæããŸããããã«ãããAmazon CloudWatch ã«ãµãŒããŒãµã€ãã®ãã°ãåéããããšãã§ããŸãããããã£ãŠãReact Server Components ã API ã«ãŒãå
ã®ä»»æã® console.log ã¯ããªãã®ã¢ã«ãŠã³ãã«ãã°åºåãããŸãã Build image settings ã»ã¯ã·ã§ã³ã§ããããåããŠã®ãããã€ã§ããã°ã«ã¹ã¿ã ãã«ãã€ã¡ãŒãžã®å€ãšã㊠amplify:al2023 ã䜿çšããŸããã¢ããªããã§ã«ãããã€ãããŠããå Žåã¯ãããããããŠã³ãã Amazon Linux 2023 ã€ã¡ãŒãžãéžæããŸãã Next ãã¯ãªãã¯ããŠã¢ããªããããã€ããŸããã¢ããªã¯ãã«ã¹ã¿ãã¯ã® SSR ã¢ããªããã«ãããŠãããã€ããŸãããµãŒããŒãµã€ãã®ã³ã³ãã¥ãŒãã©ã³ã¿ã€ã ã®ã©ã³ã¿ã€ã ç°å¢ã¯ããã«ãæã«äœ¿çšãããã©ã³ã¿ã€ã ãšäžèŽããŸãã CI/CD ãã€ãã©ã€ã³ãå®äºããåŸãã¢ããªã¯ amplifyapp.com ãã¡ã€ã³ã§ãã¹ããããŸããæ¬¡ã«ãã«ã¹ã¿ã ãã¡ã€ã³ã远å ããŸãã ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã®èšå® ã«ã¹ã¿ã ãã¡ã€ã³ã®èšå® ããã²ãŒã·ã§ã³ãã€ã³ã§ã App Settings > Domain management ãéžæããŸããã«ã¹ã¿ã ãã¡ã€ã³ã远å ããèšå®ããŸãã æ³š: äŸãšããŠã example.com ããã¡ã€ã³ãšããŠäœ¿çšããŠããŸããããã§ã¯ããªããææããŠããããããã¯DNS ã¬ã³ãŒãã®æŽæ°ãå¯èœã§ãããã¡ã€ã³ã䜿çšããŠãã ããã ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã远å ããã«ã¯ãAdd ãã¯ãªãã¯ããå€ãšããŠã¢ã¹ã¿ãªã¹ã¯ * ãå
¥åããŸãããã®ã·ããªãªã§ã¯ã www.example.com ãžã®ãªã¯ãšã¹ãã¯ã¡ã€ã³ãã©ã³ãã«ãããã³ã°ãããŸããããã«ãä»ã®ä»»æã®ãµããã¡ã€ã³å€ãã¢ã¹ã¿ãªã¹ã¯ã«ãã£ãŠããããããã¡ã€ã³ãã©ã³ããžã®ãã©ãã£ãã¯ããããã³ã°ããŸãã æææš©ã確èªããããã« DNS ãæŽæ°ããŸããæ°ååŸãAmplify Hosting ã¯ãã¡ã€ã³ã® SSL ãäœæããèšå®ããŸãã ãããå®äºããããèšå®ãããµããã¡ã€ã³çšã«ããã« 2 ã€ã® CNAME ã¬ã³ãŒãã远å ããå¿
èŠããããŸããDNS ã§èšå®ããŠå®äºãããšãã«ã¹ã¿ã ãã¡ã€ã³ã®ã¹ããŒã¿ã¹ã¯ Available ã«åãæ¿ãããŸãã ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã皌åããŸããïŒã¢ããªã¯ã©ã®ãµããã¡ã€ã³å€ã§ãã¢ã¯ã»ã¹å¯èœã§ãããŠãŒã¶ãŒãã¢ããªã«ã¢ã¯ã»ã¹ãããšãã¢ããªã®ããã«ãŠã§ã¢ã¯ã¢ããªã®ã€ã³ããã¯ã¹ããŒãžã«ã«ãŒãã£ã³ã°ããŸãããããã€åŸã www 以å€ã®ä»»æã®ãµããã¡ã€ã³å€ã«ã¢ã¯ã»ã¹ããŠã¿ãŠãã ãããã¡ã€ã³ã«ãŒãã®ã€ã³ããã¯ã¹ããŒãžã«ãªãã€ã¬ã¯ããããŸãã äžèšã®æé ãèžãããšã§ãNext.js ã¢ããªã¯åçãªãµããã¡ã€ã³ãæ±ããããã«ãªããSaaS ã¿ã€ãã®ã¢ããªã«æé©ãªåœ¢ãšãªããŸãã æ¬¡ã®ã¹ããã â Link-in-Bio ã¢ããªã®æ§ç¯ ãã©ã³ãããããã€ããã«ã¹ã¿ã ãã¡ã€ã³ãšã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ãèšå®ããã®ã§ãæå°éã®æ©èœãåãã âLink in Bioâ ã¢ããªãäœæããæºåãã§ããŸããããã®ã¢ããªã¯ããŠãŒã¶ãŒãç¬èªã®ãŠãŒã¶ãŒåã§ãµã€ã³ã¢ãããããµããã¡ã€ã³ïŒäŸ: <username>.<domain>.com ïŒçµç±ã§å人㮠Bio ããŒãžã«ã¢ã¯ã»ã¹ããåçã«ãããã£ãŒã«ã«ãªãã€ã¬ã¯ãã§ããããã«ãããã®ã§ãã ãµããã¡ã€ã³ã®ããŒã¹ãšãªã¯ãšã¹ãã®ãªãã€ã¬ã¯ããåŠçããããã«ãNext.js ã®ããã«ãŠã§ã¢ã䜿çšããŸãããã®æ©èœã«ãããNext.js ã¢ããªå
ã§ãªã¯ãšã¹ããã€ã³ã¿ãŒã»ããããŠå€æŽããããšãã§ããŸããããã«ãAmplify JS ãšããªãã«ãã® Amplify UI Authenticator ã³ã³ããŒãã³ãã䜿çšãããŠãŒã¶ãŒèªèšŒãçµ±åããŸãã ãããå®è¡ããããã«ã以äžã®æé ã«åŸããŸãã Amplify CLI ã䜿çšã㊠Amplify ããã¯ãšã³ããäœæããNext.js ãããžã§ã¯ãã«ãã«ãã Amplify Auth ã远å ãã ããŒã¿ã®æ°žç¶åã®ããã® GraphQL API ã远å ãã ãŠãŒã¶ãŒãã¢ããªã«ãµã€ã³ã¢ãããããã€ã¬ã¯ãããã²ãŒã·ã§ã³çšã®ãŠãŒã¶ãŒåãéžæã§ããããã«ãã ãµããã¡ã€ã³ããã£ããã£ããæ£ããããŒãžã«ãªã¯ãšã¹ããæžãæããããã®ããã«ãŠã§ã¢ã远å ãã ã¢ããªã® Get Started > Backend environments ã¿ããã¯ãªãã¯ããŠãã¢ããªã® Amplify ããã¯ãšã³ããæå¹ã«ããŸãã Local setup instructions ã䜿ã£ãŠãAmplify ããã¯ãšã³ããããŒã«ã«ç°å¢ã«ãã«ããŸãã amplify pull --appId <app-id> --envName staging ããã³ããã衚瀺ãããããæé ã«åŸã£ãŠ Amplify Studio ã«ãã°ã€ã³ããããŒã«ã«ã®ããã³ããšã³ãã¢ããªãæ°ãã Amplify ããã¯ãšã³ãã«ãªã³ã¯ããŸããã³ãã³ãã©ã€ã³ã«æ»ã£ãåŸãããã³ããã®éžæãè¡ããæåŸã«æ¬¡ã®ããã«å
¥åããŸãã Do you plan on modifying this backend? Yes ããã§ãã¢ããªã®æ§ç¯ãç¶ããããšãã§ããŸãã ã¢ããªã®æ§é å¿
èŠãªæ©èœã远å ããããã«ãApp Router ã䜿çšãã Next.js 14 ã¢ããªã®æ§é ã倿ŽããŸããæŽæ°å
容ã¯ä»¥äžã®éãã§ãã Amplify Authenticator ã /login ã«ãŒãã«çµ±åãããŠãŒã¶ãŒç»é²ããã°ã€ã³ããããã£ãŒã«æŽæ°ãå¯èœã«ãã ãŠãŒã¶ãŒã Bio ãããã£ãŒã«æ
å ±ãæŽæ°ã§ãã BioForm ã³ã³ããŒãã³ã /users/[username] ã«ãŒãã¯ãªã¯ãšã¹ããåããã«ãŒãã«ãªããŸãããŠãŒã¶ãŒã®ãµããã¡ã€ã³ã [username] ã«äžèŽããå Žåããªã¯ãšã¹ãã¯ããã«åããããŸãã middleware.ts ã¯ãªã¯ãšã¹ãã®ã€ã³ã¿ãŒã»ãããšæ£ãããã¹ãžã®ãããã³ã°ãåŠçããŸã . +âââ amplify/ âââ app/ +â ââ (auth)/ +â â ââ login/ +â â ââ BioForm.tsx +â â ââ page.tsx +â â +â ââ users/ +â â ââ [username]/ +â â ââ page.tsx â ââ layout.tsx â ââ page.tsx â ââ ... +âââ middleware.ts âââ node_modules/ âââ public/ âââ next.config.js ... ââ README.md Amplify Auth ã®è¿œå Amplify Auth ã远å ããŸãããµã€ã³ã€ã³ã¡ã«ããºã ãšã㊠username ãéžæããŸãã amplify add auth GraphQL API ã®è¿œå ãŠãŒã¶ãŒã®æŠå¿µãã§ããã®ã§ããŠãŒã¶ãŒãèªåã® Bio ããŒãžã«è¡šç€ºããããŒã¿ãæ°žç¶åããããã® API ã远å ããŸããBio ã®èª¬æãš Bio ãªã³ã¯ãä¿æããããã®æå°éã®ããŒã¿ã¢ãã«ãæã€ GraphQL API ã远å ããŸãã amplify add api schema.graphql ã« User ã¹ããŒãã远å ããŸããããã«ããããŠãŒã¶ãŒã¯èªåã®ãããã£ãŒã«ã¬ã³ãŒããäœæãèªã¿åããæŽæ°ããããšãã§ãããšåæã«ããããã£ãŒã«ãžã®å
¬éã¢ã¯ã»ã¹ãå¯èœã«ãªããŸãã type User @model @auth(rules: [{ allow: public, operations: [read] }, { allow: owner }]) { id: ID! username: String! @index(name: "byUsername", queryField: "byUsername") description: String link: String } 倿Žãããã·ã¥ããŸããããã«ããã以åã® Auth ã®å€æŽãããã·ã¥ãããŸãã amplify push UI ã®äœæ 次ã«ãAmplify JS ãš Amplify UI ã©ã€ãã©ãªãçµ±åããŸããããã«ãããã¢ããªã¯å
ã»ã©ããããžã§ãã³ã°ãããã¯ã©ãŠããªãœãŒã¹ã䜿çšã§ããããã«ãªããŸãããŠãŒã¶ãŒç»é²ã«ã¯ <Authenticator> ã³ã³ããŒãã³ãã䜿çšããŸããããã«ãAmplify JS ã©ã€ãã©ãªã¯ãã¥ãŒããŒã·ã§ã³ãšã¯ãšãªãéã㊠GraphQL API ãšé£æºããŸãã UI ã®æ§ç¯ãå§ããããã«ã以äžã®äŸåé¢ä¿ãã€ã³ã¹ããŒã«ããŸãã npm install aws-amplify@6 npm install @aws-amplify/ui-react èªèšŒã®è¿œå ããã©ã«ãã® Authenticator ã¯ã 远å ã®ãã£ãŒã«ãã¬ãã«æ€èšŒ ãšãšãã«äœ¿çšãããŸãããŠãŒã¶ãŒåã¯ãµããã¡ã€ã³åç
§ã«ããªãããã subdomainRegex ã䜿ã£ãŠæ€èšŒãããŸãã "use client"; // ...imports... export default function App() { return ( <div> <Authenticator initialState="signUp" components={{ SignUp: { FormFields() { return ( <> <Authenticator.SignUp.FormFields /> </> ); }, }, }} services={{ async validateCustomSignUp(formData) { // this is important if (!formData.username) { return { acknowledgement: "Username is invalid.", }; } // match subdomain pattern const subdomainRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; // check if subdomain is valid if (!subdomainRegex.test(formData?.username)) { return { acknowledgement: "Username is invalid.", }; } }, }} > {({ signOut, user }) => ( <div> <main> <h1>Hello, {user?.username}.</h1> <div> <button onClick={signOut}>Sign out</button> </div> </main> </div> )} </Authenticator> </div> ); } æ°ããã«ãŒãã§ Authenticator ãèšå®ããããšãã¢ã«ãŠã³ããäœæããåãã¢ããªã§ãµã€ã³ã€ã³ããåã«ã以äžã®ãããªãã¥ãŒã app.<domain>/login ã«ãŒãã«è¡šç€ºãããŸãã ãããŠããŠãŒã¶ãŒåã§ãµã€ã³ã€ã³ããåŸã¯ä»¥äžã®ããã«ãªããŸããïŒäŸ: stephen ïŒ ãããã£ãŒã« Bio ãã©ãŒã ã®è¿œå ãŠãŒã¶ãŒã®Bio ããŒãžã§å
¬éããããã«ããŠãŒã¶ãŒã®èª¬æãšãªã³ã¯ãã£ãŒã«ããååŸããç°¡åãªãã©ãŒã ã远å ããŸãã æåã¯ãŠãŒã¶ãŒã®ãŠãŒã¶ãŒåã®ã¿æã£ãŠããããããã®ãã£ãŒã«ã㯠GraphQL API ã§ç°¡åã«æ€çŽ¢ã§ããããã«ãã©ãŒã ã«ãããããå
¥åãããŠããŸããGraphQL API ã¯ãŠãŒã¶ãŒåã«ã€ã³ããã¯ã¹ãæã€ããšã§ãããå®çŸããŸãã æ°ããã³ã³ããŒãã³ã BioForm.tsx ãäœæããæ¢åã®ãã°ã€ã³ããŒãžã«å«ããŸããæå°éã®ããŒãžã§ã³ã¯ä»¥äžã®ããã«ãªããŸãã // /app/(auth)/login/BioForm.tsx "use client"; import { useEffect, useState } from "react"; import type { FormEvent } from "react"; import { Amplify } from "aws-amplify"; import { createUser, updateUser } from "@/src/graphql/mutations"; import * as queries from "@/src/graphql/queries"; import { getCurrentUser } from "@aws-amplify/auth"; import { generateClient } from "aws-amplify/api"; import awsExports from "@/src/aws-exports"; Amplify.configure(awsExports); const client = generateClient(); export default function BioForm() { const [loggedInUser, setLoggedInUser] = useState<any>(null); const [userProfile, setUserProfile] = useState<any>(null); const [userProfileExists, setUserProfileExists] = useState<boolean>(false); async function onSubmit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); const form = new FormData(event.currentTarget); const description = form.get("description") || userProfile?.description; const link = form.get("link") || userProfile?.link; if (userProfileExists) { await onUpdate({ id: userProfile?.id, username: loggedInUser?.username, description, link, }); } else { await onCreate({ username: loggedInUser?.username, description, link, }); } } // include mutation handlers to update user profile const onCreate = async (formData: { username: string; description: string; link: string; }) => { const { username, description, link } = formData; const input = { username, description, link }; await client.graphql({ query: createUser, variables: { input }, authMode: "userPool", }); }; const onUpdate = async (formData: { id: string; username: string; description: string; link: string; }) => { const { id, username, description, link } = formData; const input = { id, username, description, link }; await client.graphql({ query: updateUser, variables: { input }, authMode: "userPool", }); }; const getUserProfile = async (username: string) => { return await client.graphql({ query: queries.byUsername, variables: { username }, authMode: "userPool", }); }; useEffect(() => { const getUser = async () => { const user = await getCurrentUser(); if (user) { setLoggedInUser(user as any); const { data } = await getUserProfile(user?.username); const profile = data?.byUsername?.items[0]; if (profile) { setUserProfileExists(true); setUserProfile(profile as any); } } }; getUser(); }, []); return ( <form onSubmit={onSubmit}> <div> <input type="text" name="username" id="username" value={loggedInUser?.username} disabled={true} /> </div> <div> <textarea name="description" id="description" /> </div> <div> <label htmlFor="link">Link</label> <input type="url" name="link" id="link" /> </div> <div> <button type="submit"> {userProfileExists ? "Update" : "Create"} </button> </div> </form> ); } çŸåšã®ãã°ã€ã³ããŒãžãæŽæ°ããŠãæ°ããã³ã³ããŒãã³ããå«ããŸããããã§ããŠãŒã¶ãŒããã°ã€ã³ãããšããããã£ãŒã«ãã©ãŒã ã衚瀺ãããŸãã Link-in-Bio ããŒãžã®äœæ ãšã³ããŠãŒã¶ãŒããŠãŒã¶ãŒåãµããã¡ã€ã³ã®ã«ãŒãã«ã¢ã¯ã»ã¹ãããšããªã¯ãšã¹ã㯠/users ã«ãŒãã«æžãæããããŸãããŠãŒã¶ãŒåã¯ããŠãŒã¶ãŒã®ãããã£ãŒã«ãååŸããŠè¡šç€ºããããã®åçãã©ã¡ãŒã¿ãŒãšããŠæ©èœããŸãããã®æ©èœãå®è£
ããããã«ã app/users/[username]/page.tsx ã§ãŠãŒã¶ãŒã® Bio ãããã£ãŒã«æ
å ±ã衚瀺ããå
¬éããŒãžãäœæããŸããNext.js ã®ã«ãŒããã©ã¡ãŒã¿ãŒã䜿çšããŠãAmplify JS ã§ãŠãŒã¶ãŒã®ãããã£ãŒã«ãã¯ãšãªã§ããŸãã以äžã¯äŸã§ãã const { data } = await client.graphql({ query: queries.byUsername, variables: { username }, }) ããã«ãŠã§ã¢ã«ãããã«ãããã³ãã«ãŒãã£ã³ã° ããã¯ããµããã¡ã€ã³ã§ãããã³ã°ãããªã¯ãšã¹ããæ£ããã¢ããªããŒãžã«æžãæããããã® middleware.ts ãã³ãã©ã®äŸã§ãããªã¯ãšã¹ãã¯ããµããã¡ã€ã³å€ãå«ãå Žåã«ã¯é©åãªãŠãŒã¶ãŒãããã£ãŒã«ããŒãžã«ã«ãŒãã£ã³ã°ããããã°ã€ã³ããŒãžã¯ app. ãµããã¡ã€ã³ã§æäŸãããŸãã import { NextRequest, NextResponse } from "next/server"; export const config = { matcher: [ /* * Match all paths except for: * 1. /api routes * 2. /_next (Next.js internals) * 3. /_static (inside /public) * 4. all root files inside /public */ "/((?!api/|_next/|_static/|[\\w-]+\\.\\w+).*)", ], }; export default async function middleware(req: NextRequest) { const url = req.nextUrl; const hostname = req.headers.get("host")!; const path = url.pathname; let subdomain = hostname.split(".")[0]; subdomain = subdomain.replace("localhost:3000", ""); // handle no subdomain or www with base path if ((subdomain === "www" || subdomain === "") && path === "/") { return NextResponse.rewrite(new URL("/", req.url)); } // profile login if (subdomain === "app" && path === "/login") { return NextResponse.rewrite(new URL("/login", req.url)); } // subdomains if (subdomain !== "app") { return NextResponse.rewrite( new URL(`/users/${subdomain}${path === "/" ? "" : path}`, req.url) ); } return NextResponse.next(); } ããã§ãã¢ããªããªã¯ãšã¹ããåãåããšãããã«ãŠã§ã¢ã¯ä»¥äžãè¡ããŸãã ãã¹ã« âappâ ã®ãµããã¡ã€ã³ãš /login ã®ãã¹ããããã©ããã確èªãã âappâ ã«äžèŽããªããµããã¡ã€ã³ããã§ãã¯ãã ãŸãã¯ãå
ã®ãªã¯ãšã¹ãããã®ãŸãŸæž¡ãããã«ãã©ãŒã«ããã¯ãã ããã§ããŠãŒã¶ãŒãäœæãããåŸããŠãŒã¶ãŒãããã£ãŒã«ããŒãžã皌åããŸãïŒäŸãã°ã stephen.localhost:3000 ã«ã¢ã¯ã»ã¹ãããšããŠãŒã¶ãŒãååšããå Žåã«ã¯ãããã£ãŒã«ããŒãžã衚瀺ãããŸãã Hosting ãžã®ããã·ã¥ãšãããã€ æŽæ°ãããã¢ããªããããã€ããããã«ãããã³ããšã³ããšããã¯ãšã³ãã®äž¡æ¹ã®å€æŽãå«ããŠããŸã Git ãªããžããªã«å€æŽãããã·ã¥ããŸããã¢ããªã«èšå®ããã IAM ãµãŒãã¹ããŒã«ãæŽæ°ããŠãAmplify ããã¯ãšã³ãã®ãã«ããèš±å¯ããå¿
èŠããããŸãããã㯠App settings > General ã®èšå®ã§æŽæ°ã§ããŸãã ãããå®äºããããããã³ããšã³ããããã¯ãšã³ãã«ãªã³ã¯ããŠãç¶ç¶çãããã€ã¡ã³ããæå¹ã«ããŸãã App settings > Build image settings ã§ãAmplify CLI ã®ããŒãžã§ã³ã«é©ãã Live Package Updates ãéžæããŸãã ããã§ãã³ããããããã·ã¥ãããšãããã³ããšã³ããšããã¯ãšã³ãããããã€ãããŸãã ã¯ãªãŒã³ã¢ãã æåŸã«ãäžèŠã«ãªã£ãã¢ããªãåé€ããŸãããããè¡ãã«ã¯ããã®ã¢ããªã® Amplify Hosting Console ã® App settings > General ã«ç§»åãã Delete app ãã¯ãªãã¯ããŸãããŸããå¥ã®ã¢ããªã«åå©çšããå Žåã¯ããã¡ã€ã³ãã DNS ã¬ã³ãŒããåé€ããŠãã ããã ãŸãšã ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã䜿çšããããšã¯ãAmplify Hosting äžã§ãã«ãããã³ãã¢ãŒããã¯ãã£ãæ¡åŒµãã广çãªæ¹æ³ã§ããåçãªã¢ããªã SSR ã¢ããªã«ã¯ã€ã«ãã«ãŒããµããã¡ã€ã³ã®èšå®ãçµ±åããããšã§ãSaaS ã¢ããªã®æ¡å€§ã«åãããŠãæå°éã®èšå®ã§ã«ã¹ã¿ãã€ãºãããäœéšãæäŸããããšãã§ããŸãã 詳现ã«ã€ããŠã¯ãAmplify Hosting ã®ããã¥ã¡ã³ãã® Wildcard subdomains ãã芧ãã ããã æ¬èšäºã¯ã Wildcard Subdomains for Multi-tenant Apps on AWS Amplify Hosting ã翻蚳ãããã®ã§ãã翻蚳ã¯ãœãªã¥ãŒã·ã§ã³ã¢ãŒããã¯ãã® éœç¯ ãæ
åœããŸããã