import nextCallout from "/src/fragments/lib/ssr/js/next-js-callout.mdx"; To work well with server-rendered pages, Amplify JS requires slight modifications from how you would use it in a client-only environment. ## Amplify ### Enabling SSR When using the Amplify CLI, the __aws-exports.js__ file gets created and updated automatically for you based upon the resources you have added and configured. For client-only apps, `Amplify.configure(awsExports)` is all you need. To enable SSR support, also provide `ssr: true`: ```js import { Amplify } from "aws-amplify"; import awsExports from "../src/aws-exports"; Amplify.configure({ ...awsExports, ssr: true }); ``` By providing `ssr: true`, Amplify persists credentials on the client in cookies so that subsequent requests to the server have access to them. > **Note**: Once [vercel/next.js#16977](https://github.com/vercel/next.js/issues/16977) is resolved, you can hoist `Amplify.configure` into **pages/_app.js**. Until then, be sure that all **pages/*** run `Amplify.configure({ ...awsExports, ssr: true })`. ### withSSRContext Once an application has been configured with `ssr: true`, client-side credentials are passed to the server via cookies. The `withSSRContext` utility creates an instance of `Amplify` scoped to a single request (`req`) using those cookie credentials. For client-side code rendered in the browser, your page should continue using top-level imports as usual: ```js import { Amplify, API } from "aws-amplify"; import awsExports from "../src/aws-exports"; Amplify.configure({ ...awsExports, ssr: true }); export default function HomePage({ posts = [] }) { const [posts, setPosts] = useState(posts); useEffect(() => { // Notice how the client correctly uses the top-level `API` import API.graphql({ query: listPosts }).then(({ data }) => setPosts(data.listPosts.items)); }, []) return ( ... ); } ``` When on the server, use `withSSRContext({ req?: ServerRequest })`: ```js import { Amplify, API, withSSRContext } from "aws-amplify"; export async function getServerSideProps({ req }) { // Notice how the server uses `API` from `withSSRContext`, instead of the top-level `API`. const SSR = withSSRContext({ req }) const { data } = await SSR.API.graphql({ query: listPosts }); return { props: { posts: data.listPosts.items } } } ``` Server-side functions that don't have a req object (e.g. Next.js' getStaticProps & getStaticPaths) should still use withSSRContext(). Please note that it's not possible to perform authenticated requests with Amplify when using these functions. ## Use Amplify with Next.js App Router ("app/" directory) Amplify JavaScript can be used with the Next.js App Router (Next.js v13.4+) by applying the following changes: ### 1. Run `Amplify.configure({ ...awsExports, ssr: true })` in **both** the client-side and server-side code To use Amplify with Next.js App Router, you **must** run `Amplify.configure()` in both Client and Server Components. The `ssr` option **must** be enabled. Example Next.js App Router Client Component containing your login flow (e.g. `app/page.tsx`): ```js 'use client'; import { Amplify } from 'aws-amplify'; import awsExports from '@/aws-exports'; Amplify.configure({ ...awsExports, ssr: true }); export default function Home() { return ( {/* Render a login form for your users */} ) } ``` Example Next.js App Router Server Component (e.g. `app/posts/page.tsx`): ```js import { Amplify } from 'aws-amplify'; import awsExports from '@/aws-exports'; Amplify.configure({ ...awsExports, ssr: true }); export default async function Page() { // ... } ``` ### 2. Prepare a request object for `withSSRContext` to perform server-side operations that require authentication Example Next.js App Router Server Component that queries data from GraphQL (e.g. `app/posts/page.tsx`): ```js import { Amplify, withSSRContext } from 'aws-amplify'; import { headers } from 'next/headers'; import { listPosts } from '@/graphql/queries'; import awsExports from '@/aws-exports'; Amplify.configure({ ...awsExports, ssr: true }); export default async function Page() { // Construct a req object & prepare an SSR enabled version of Amplify const req = { headers: { cookie: headers().get('cookie'), }, }; const SSR = withSSRContext({ req }) const { data } = await SSR.API.graphql({ query: listPosts }); // ... } ``` ## DataStore ### Serializing For Next.js, returned `props` from the server have to be valid JSON. Because `DataStore.query(Model)` returns _instances_ of `Model`, you need the `serializeModel` helper to convert it to JSON instead: ```js import { serializeModel } from '@aws-amplify/datastore/ssr'; import { Amplify, withSSRContext } from "aws-amplify"; ... export async function getServerSideProps({ req }) { const SSR = withSSRContext({ req }); const posts = await SSR.DataStore.query(Post); return { props: { // This converts Post instances into serialized JSON for the client posts: serializeModel(posts), }, }; } ``` ### Deserializing If your client-side code only reads from the server-side props and doesn't perform any updates to these models, then your client-side code won't need any changes. However, if you receive models from the server and need to `DataStore.delete(model)` or `DataStore.save(...)` changes to them, you'll need the `deserializeModel` utility to convert them from server-friendly JSON back into model _instances_: ```js import { deserializeModel } from '@aws-amplify/datastore/ssr'; import { Amplify, withSSRContext } from "aws-amplify"; import { Post } from "../src/models"; export default function HomePage(initialData) { // This converts the serialized JSON back into Post instances const [posts, setPosts] = useState(deserializeModel(Post, initialData.posts)); ... } ``` ### Example: Using the following schema: ```graphql type Post @model @auth(rules: [{ allow: owner }, { allow: public, operations: [read] }]) { id: ID! title: String! content: String! } ``` Open **pages/index.js** and replace it with the following code: ```jsx // pages/index.js import Head from "next/head"; import styles from "../styles/Home.module.css"; import { Amplify, Auth, AuthModeStrategyType, DataStore, withSSRContext, } from "aws-amplify"; import { Authenticator } from "@aws-amplify/ui-react"; import { Post } from "../src/models"; import { serializeModel } from "@aws-amplify/datastore/ssr"; import awsExports from "../src/aws-exports"; Amplify.configure({ ...awsExports, DataStore: { authModeStrategyType: AuthModeStrategyType.MULTI_AUTH, }, ssr: true, }); export async function getServerSideProps({ req }) { const SSR = withSSRContext({ req }); const posts = await SSR.DataStore.query(Post); return { props: { // This converts Post instances into serialized JSON for the client posts: serializeModel(posts), }, }; } async function handleCreatePost(event) { event.preventDefault(); const form = new FormData(event.target); try { const post = await DataStore.save( new Post({ title: form.get("title"), content: form.get("content"), }) ); window.location.href = `/posts/${post.id}`; } catch (error) { console.log(error); } } export default function Home({ posts = [] }) { return (
Amplify DataStore + Next.js

Amplify DataStore + Next.js

{posts.length} posts

{posts.map((post) => (

{post.title}

{post.content}

))}

New Post

Title
Content