diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 0fad3322748..2a8039c3796 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -1934,7 +1934,10 @@ export const directory = { path: 'src/pages/gen2/start/quickstart/nextjs-pages-router/index.mdx' }, { - path: 'src/pages/gen2/start/quickstart/nextjs-app-router/index.mdx' + path: 'src/pages/gen2/start/quickstart/nextjs-app-router-client-components/index.mdx' + }, + { + path: 'src/pages/gen2/start/quickstart/nextjs-app-router-server-components/index.mdx' } ] }, diff --git a/src/pages/gen2/start/quickstart/nextjs-app-router/index.mdx b/src/pages/gen2/start/quickstart/nextjs-app-router-client-components/index.mdx similarity index 93% rename from src/pages/gen2/start/quickstart/nextjs-app-router/index.mdx rename to src/pages/gen2/start/quickstart/nextjs-app-router-client-components/index.mdx index 77814e1e06e..4fb5c8494ca 100644 --- a/src/pages/gen2/start/quickstart/nextjs-app-router/index.mdx +++ b/src/pages/gen2/start/quickstart/nextjs-app-router-client-components/index.mdx @@ -1,5 +1,5 @@ export const meta = { - title: 'Next.js App Router', + title: 'Next.js App Router (Client Components)', description: 'Get started with AWS Amplify (Gen 2) using the Next.js App Router.' }; @@ -11,7 +11,7 @@ export function getStaticProps(context) { }; } -This Quickstart guide will walk you through how to build a task list application with TypeScript, Next.js **App Router**, and React. If you are new to these technologies, we recommend you go through the official [React](https://react.dev/learn/tutorial-tic-tac-toe), [Next.js](https://nextjs.org/docs/getting-started/installation), and [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html) tutorials first. +This Quickstart guide will walk you through how to build a task list application with TypeScript, Next.js **App Router with Client Components**, and React. If you are new to these technologies, we recommend you go through the official [React](https://react.dev/learn/tutorial-tic-tac-toe), [Next.js](https://nextjs.org/docs/getting-started/installation), and [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html) tutorials first. import prerequisites from 'src/fragments/gen2/quickstart/prerequisites.mdx'; diff --git a/src/pages/gen2/start/quickstart/nextjs-app-router-server-components/index.mdx b/src/pages/gen2/start/quickstart/nextjs-app-router-server-components/index.mdx new file mode 100644 index 00000000000..47a1fec7db3 --- /dev/null +++ b/src/pages/gen2/start/quickstart/nextjs-app-router-server-components/index.mdx @@ -0,0 +1,351 @@ +export const meta = { + title: 'Next.js App Router (Server Components)', + description: 'Get started with AWS Amplify (Gen 2) using the Next.js App Router using Server Components.' +}; + +export function getStaticProps(context) { + return { + props: { + meta + } + }; +} + +This Quickstart guide will walk you through how to build a task list application with TypeScript, Next.js **App Router with Server Components**, and React. If you are new to these technologies, we recommend you go through the official [React](https://react.dev/learn/tutorial-tic-tac-toe), [Next.js](https://nextjs.org/docs/getting-started/installation), and [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html) tutorials first. + +import prerequisites from 'src/fragments/gen2/quickstart/prerequisites.mdx'; + + + +import createProject from 'src/fragments/gen2/quickstart/create-nextjs-app-router-project.mdx'; + + + +import buildABackend from 'src/fragments/gen2/quickstart/build-a-backend.mdx'; + + + + +## Build UI + +Let's add UI that connects to the backend data and auth resources. + + +### Configure Amplify Client Side + +First, install the Amplify UI component library: + +```bash +npm install @aws-amplify/ui-react +``` + +Next, create a `components` folder in the root of your project and the contents below to a file called `ConfigureAmplify.tsx`. + +```ts title="components/ConfigureAmplify.tsx" +// components/ConfigureAmplify.tsx +"use client"; + +import { Amplify } from "aws-amplify"; + +import config from "@/amplifyconfiguration.json"; + +Amplify.configure(config, { ssr: true }); + +export default function ConfigureAmplifyClientSide() { + return null; +} +``` + +Update `app/layout.tsx` to import and render ``. This client component will configure Amplify for client pages in our application. + +```ts title="app/layout.tsx" +// app/layout.tsx +import "@aws-amplify/ui-react/styles.css"; +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +import ConfigureAmplifyClientSide from "@/components/ConfigureAmplify"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + {children} + + + ); +} +``` + +### Add a login page + +First, create a client side Login component in the `components` folder that will be wrapped in `withAuthenticator`. If the user is logged in, they will be redirected to the index route, otherwise the [Amplify UI Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator) will be rendered. + +```ts title="components/Login.tsx" +// components/Login.tsx +"use client"; + +import { withAuthenticator } from "@aws-amplify/ui-react"; +import { AuthUser } from "aws-amplify/auth"; +import { redirect } from "next/navigation"; +import { useEffect } from "react"; + +function Login({ user }: { user?: AuthUser }) { + useEffect(() => { + if (user) { + redirect("/"); + } + }, [user]); + return null; +} + +export default withAuthenticator(Login); +``` + +Next, create a new route under `app/login/page.tsx` to render the `Login` component. + +```ts title="app/login/page.tsx" +// app/login/page.tsx + +import Login from "@/components/Login"; + +export default function LoginPage() { + return ; +} +``` + + + +Some applications require more customization for the `` component. The following example shows how to add a custom Header to the ``. For this use, you will not need the `Login` file in the `components` folder but can do everything through the `page` file in the `app/login/` folder. For more customization option, see [Authenticator Customization](https://ui.docs.amplify.aws/react/connected-components/authenticator/customization). + +```ts title="app/login/page.tsx" +// app/login/page.tsx - Custom + +"use client"; + +import { + Authenticator, + Text, + View, + useAuthenticator, +} from "@aws-amplify/ui-react"; +import { redirect } from "next/navigation"; +import { useEffect } from "react"; + +const components = { + Header() { + return ( + + Authenticator Header + + ); + }, +}; + +function CustomAuthenticator() { + const { user } = useAuthenticator((context) => [context.user]); + + useEffect(() => { + if (user) { + redirect("/"); + } + }, [user]); + + return ; +} + +export default function Login() { + return ( + + + + ); +} + +``` + + + + +### Configure Amplify Server Side + +First, install the Amplify Next.js Adapter: + +```bash +npm install @aws-amplify/adapter-nextjs +``` + +Next, create a `utils/amplify-utils.ts` file from the root of the project and paste the code below. `runWithAmplifyServerContext` and `cookiesClient` are declared here and will be used to gain access to Amplify assets from the server. + + +```ts title="utils/amplify-utils.ts" +// utils/amplify-utils.ts +import { cookies } from "next/headers"; + +import { createServerRunner } from "@aws-amplify/adapter-nextjs"; +import { generateServerClientUsingCookies } from "@aws-amplify/adapter-nextjs/api"; + +import { type Schema } from "@/amplify/data/resource"; +import config from "@/amplifyconfiguration.json"; + +export const { runWithAmplifyServerContext } = createServerRunner({ + config, +}); + +export const cookiesClient = generateServerClientUsingCookies({ + config, + cookies, +}); +``` + +### Add middleware for server-side redirect + +Create `middleware.ts` in the root of the project with the contents below. + +This middleware runs `fetchAuthSession` wrapped in `runWithAmplifyServerContext` and will redirect to `/login` when a user is not logged in. + + +```ts title="middleware.ts" +// middleware.ts +import { NextRequest, NextResponse } from "next/server"; + +import { fetchAuthSession } from "aws-amplify/auth/server"; + +import { runWithAmplifyServerContext } from "@/utils/amplify-utils"; + +export async function middleware(request: NextRequest) { + const response = NextResponse.next(); + + const authenticated = await runWithAmplifyServerContext({ + nextServerContext: { request, response }, + operation: async (contextSpec) => { + try { + const session = await fetchAuthSession(contextSpec, {}); + return session.tokens !== undefined; + } catch (error) { + console.log(error); + return false; + } + }, + }); + + if (authenticated) { + return response; + } + + return NextResponse.redirect(new URL("/login", request.url)); +} + +export const config = { + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - api (API routes) + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico (favicon file) + * - login + */ + "/((?!api|_next/static|_next/image|favicon.ico|login).*)", + ], +}; +``` + +Run your application with `npm run dev` and navigate to `http://localhost:3000`. You should now see the authenticator, which is already configured and ready for your first sign-up! Create a new user account, confirm the account through email, and then sign in. + +### View list of to-do items + +Now, let's display data on our app's frontend. + +The code below uses the `cookiesClient` to provide access to the `Todo` model defined in the backend. + +Modify your app's home page file, `app/page.tsx`, with the following code: + +```ts title="app/page.tsx" +// app/page.tsx + +import { cookiesClient } from "@/utils/amplify-utils"; + +async function App() { + const { data: todos } = await cookiesClient.models.Todo.list(); + + return ( + <> +

Hello, Amplify 👋

+
    + {todos && todos.map((todo) =>
  • {todo.content}
  • )} +
+ + ); +} + +export default App; +``` + +Once you save the file and navigate back to `http://localhost:3000`, you should see "Hello, Amplify" with a blank page for now because you have only an empty list of to-dos. + +### Create a new to-do item + +Let's update the component to have a form for prompting the user for the title for creating a new to-do list item and run the `addTodo` method on form submission. In a production app, the additional fields of the `Todo` model would be added to the form. + +After creating a todo, `revalidatePath` is run to clear the Next.js cache for this route to instantly update the results from the server without a full page reload. + +```ts title="app/page.tsx" +// app/page.tsx + +import { revalidatePath } from "next/cache"; + +import { cookiesClient } from "@/utils/amplify-utils"; + +async function App() { + const { data: todos } = await cookiesClient.models.Todo.list(); + + async function addTodo(data: FormData) { + "use server"; + const title = data.get("title") as string; + await cookiesClient.models.Todo.create({ + content: title, + done: false, + priority: "medium", + }); + revalidatePath("/"); + } + + return ( + <> +

Hello, Amplify 👋

+
+ + +
+ +
    + {todos && todos.map((todo) =>
  • {todo.content}
  • )} +
+ + ); +} + +export default App; +``` + +### Terminate dev server + +Go to `localhost` in the browser to make sure you can now log in and create and list to-dos. You can end your development session by shutting down the frontend dev server and cloud sandbox. The sandbox prompts you to delete your backend resources. While you can retain your backend, we recommend deleting all resources so you can start clean again next time. + +import deployAndHost from 'src/fragments/gen2/quickstart/deploy-and-host.mdx'; + +