From d61e076e582053e73ebc109bc51dbf36bfcbd971 Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Wed, 6 Dec 2023 16:02:02 -0700 Subject: [PATCH 1/9] feat: add ssr migration guide --- src/directory/directory.mjs | 14 +- .../server-side-rendering/index.mdx | 355 +--------------- .../nextjs-migration-guide/index.mdx | 349 ++++++++++++++++ .../use-amplify-in-nextjs/index.mdx | 383 ++++++++++++++++++ 4 files changed, 744 insertions(+), 357 deletions(-) create mode 100644 src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx create mode 100644 src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 2065cf9b3e4..57b82577d0d 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -442,11 +442,19 @@ export const directory = { }, { path: 'src/pages/[platform]/build-a-backend/push-notifications/push-notifications-migration-guide/index.mdx' - }, + } ] }, { - path: 'src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx' + path: 'src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx', + children: [ + { + path: 'src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx' + }, + { + path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx' + } + ] }, { path: 'src/pages/[platform]/build-a-backend/utilities/index.mdx', @@ -664,7 +672,7 @@ export const directory = { }, { path: 'src/pages/[platform]/build-a-backend/more-features/in-app-messaging/in-app-messaging-migration-guide/index.mdx' - }, + } ] }, { diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx index 72f668df028..bf0bc376bb6 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx @@ -30,357 +30,4 @@ export function getStaticProps(context) { }; } -This guide walks through how to use Amplify Auth, GraphQL API, REST API, and Storage category APIs from Next.js server-side runtimes. - -import versionRangeCallout from '/src/fragments/lib/ssr/nextjs/version-range-callout.mdx'; - - - -Before you begin, you will need: - -- [A Next.js application created](/[platform]/start/project-setup/create-application/) -- [Installed Amplify libraries for Next.js](/[platform]/start/project-setup/create-application/#install-amplify-libraries) - -## Configure Amplify Library for server-side usage - -To use Amplify APIs on the server-side of your Next.js app, you will need to create a `runWithAmplifyServerContextRunner` function. - -You can create an `amplifyServerUtils.ts` file under a `utils` folder in your codebase. In this file, you will import the Amplify configuration object from the `amplifyconfiguration.json` file that is generated by the Amplify CLI, and use the `createServerRunner` function to create the `runWithAmplifyServerContextRunner` function. - -For example, the `utils/amplifyServerUtils.ts` file may contain the following content: - -```typescript -import { createServerRunner } from '@aws-amplify/adapter-nextjs'; -import config from '@/amplifyconfiguration.json'; - -export const { runWithAmplifyServerContext } = createServerRunner({ - config -}); -``` - -You can use the exported `runWithAmplifyServerContext` function to call Amplify APIs with in isolated request contexts. Usage examples see [here](#calling-amplify-category-apis-on-the-server-side). - -**TIP:** You only need to call the `createServerRunner` function once and reuse the `runWithAmplifyServerContext` function throughout. - -## Configure Amplify library for client-side usage - -When you use the Amplify library on the client-side of your Next.js app, you will need to configure Amplify by calling the Amplify.configure as you would to use Amplify in a single-page application. - - - -**NOTE:** To use the Amplify library on the client side in a Next.js app, you will need to set `ssr` to `true` when calling `Amplify.configure`. This instructs the Amplify library to store tokens in the cookie store of a browser. Cookies will be sent along with requests to your Next.js server for authentication. - - - -```typescript -'use client'; - -import config from '@/amplifyconfiguration.json'; -import { Amplify } from 'aws-amplify'; - -Amplify.configure(config, { - ssr: true // required when using Amplify with Next.js -}); - -export default function RootLayoutThatConfiguresAmplifyOnTheClient({ - children -}: { - children: React.ReactNode; -}) { - return children; -} -``` - - - -To avoid repetitive calls to `Amplify.configure`, you can call it once in a top-level client-side rendered layout component. - - - - - -If you're using the Next.js App Router, you can create a client component to configure Amplify and import it into your root layout. - -`ConfigureAmplifyClientSide.ts`: - -```typescript -'use client'; - -import { Amplify } from 'aws-amplify'; -import config from '../amplifyconfiguration.json'; - -Amplify.configure(config, { ssr: true }); - -export default function ConfigureAmplifyClientSide() { - return null; -} -``` - -`layout.tsx`: - -```jsx -import ConfigureAmplifyClientSide from '@/components/ConfigureAmplifyClientSide'; -import './globals.css'; - -import type { Metadata } from 'next'; - -export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', -}; - -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - - - <> - - {children} - - - - ); -} -``` - - - -## Authentication with Next.js server-side runtime - -You can use the Amplify Auth category APIs to sign up and sign in your end users on the client side. With setting `ssr: true` when calling `Amplify.configure`, the Amplify library uses cookies to store tokens, which will be sent along with HTTP requests to your Next.js app server. - -### Manage Auth session with the Next.js Middleware - -You can use the `fetchAuthSession` API to check the auth sessions that are attached to the incoming requests in the middleware of your Next.js app to protect your routes. For example: - -```typescript -import { fetchAuthSession } from 'aws-amplify/auth/server'; -import { NextRequest, NextResponse } from 'next/server'; -import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; - -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('/sign-in', 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) - */ - '/((?!api|_next/static|_next/image|favicon.ico|sign-in).*)' - ] -}; -``` - -In this example, if the incoming request is not associated with a valid user session the request will be redirected to the `/sign-in` route. - - - -**NOTE:** When calling `fetchAuthSession` with a `response` context, it will send the refreshed tokens (if any) back to the client via the `Set-Cookie` header in the response. - - - -## Calling Amplify category APIs on the server side - -For the **Auth**, **REST APIs**, and **Storage** categories to use Amplify APIs on the server in your Next.js app, you will need to: - -1. Import the API from the `/server` sub path. -2. Use the `runWithAmplifyServerContext` helper function created by calling the `createServerRunner` function exported from `@aws-amplify/adapter-nextjs` to call the Amplify API in an isolated server context. - -For the **GraphQL API** category, review [Connect to GraphQL API from server-side runtimes](/[platform]/build-a-backend/graphqlapi/connect-from-server-runtime/). - - - -**NOTE:** A subset of Amplify APIs can now be called on the server side of a Next.js app. These APIs are exported from the `/server` sub paths. See [the full list](#supported-apis-for-nextjs-server-side-usage) of supported APIs. - - - -### With Next.js App Router - -#### In React Server Component - -##### Dynamic Rendering - -Dynamic rendering is based on a user session extracted from an incoming request. - -```jsx -import { cookies } from 'next/headers'; -import { getCurrentUser } from '@aws-amplify/auth/server'; -import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; - -// This page always dynamically renders per request -export const dynamic = 'force-dynamic'; - -export default async function AuthGetCurrentUserServer() { - try { - const currentUser = await runWithAmplifyServerContext({ - nextServerContext: { cookies }, - operation: (contextSpec) => getCurrentUser(contextSpec) - }); - - return ( - - ); - } catch (error) { - console.error(error); - return

Something went wrong...

; - } -} -``` - -##### Static Rendering - -Static rendering doesn’t require a user session, so you can specify the `nextServerContext` parameter as `null`. This is useful for some use cases, for example, when you are using the Storage API with guest access (if you have enabled it in your backend). - -```jsx -import { getUrl } from 'aws-amplify/storage/server'; -import Image from 'next/image'; -import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; - -// Re-render this page every 60 minutes -export const revalidate = 60 * 60; // in seconds - -export default async function StaticallyRenderedPage() { - try { - const splashUrl = await runWithAmplifyServerContext({ - nextServerContext: null, - operation: (contextSpec) => - getUrl(contextSpec, { - key: 'splash.png' - }) - }); - - return ( - Splash Image - ); - } catch (error) { - console.error(error); - return

Something went wrong...

; - } -} -``` - - - -**NOTE:** The URL returned by the `getUrl` API expires in the above example. You may want to specify the `revalidate` parameter to rerender the page as required to ensure the URL gets regenerated. - - - -#### In Route Handlers - -Take implementing an API route that enables `GET /apis/get-current-user`. - -```typescript -import { getCurrentUser } from 'aws-amplify/auth/server'; -import { cookies } from 'next/headers'; -import { NextResponse } from 'next/server'; -import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; - -export async function GET() { - const user = await runWithAmplifyServerContext({ - nextServerContext: { cookies }, - operation: (contextSpec) => getCurrentUser(contextSpec) - }); - - return NextResponse.json({ user }); -} -``` - -When you call `fetch('/apis/get-current-user')` it return a payload that contains the `user` data for the currently signed-in user. - -### With Next.js Pages Router - -#### In `getServerSideProps` - -The following example extracts current user data from the request and provides them to a page react component via its props. - -```typescript -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const currentUser = await runWithAmplifyServerContext({ - nextServerContext: { request: req, response: res }, - operation: (contextSpec) => getCurrentUser(contextSpec) - }); - - return { props: { currentUser } }; -}; -``` - -#### In `getStaticProps` - -Similar to static rendering with the App Router, you can pass `null` as the value of the `nextServerContext` parameter to use the Amplify Storage API with guest access. - -```typescript -export async function getStaticProps() { - const splashUrl = await runWithAmplifyServerContext({ - nextServerContext: null, - operation: (contextSpec) => getUrl(contextSpec, { key: 'splash.png' }) - }); - - return { - props: { imageUrl: splashUrl.url.toString() }, - revalidate: (splashUrl.expiresAt.getTime() - Date.now()) / 1000 // in seconds - }; -} -``` - -## Supported APIs for Next.js server-side usage - -All APIs that support use on the server are exported from the `aws-amplify//server` sub paths. You **must** use these APIs for any server side use cases. - -| Category | APIs | Server (Node.js) Amplify Hosting/Vercel | Vercel Edge Runtime (middleware) | -| --- | --- | --- | --- | -| Auth | `fetchAuthSession` | ✅ | ✅ | -| Auth | `fetchUserAttributes` | ✅ | ✅ | -| Auth | `getCurrentUser` | ✅ | ✅ | -| API (GraphQL) | `generateServerClientUsingCookies` | ✅ | | -| API (GraphQL) | `generateServerClientUsingReqRes` | ✅ | | -| API (REST) | `GET` | ✅ | | -| API (REST) | `POST` | ✅ | | -| API (REST) | `PUT` | ✅ | | -| API (REST) | `DEL` | ✅ | | -| API (REST) | `HEAD` | ✅ | | -| API (REST) | `PATCH` | ✅ | | -| Storage | `getUrl` | ✅ | | -| Storage | `getProperties` | ✅ | | -| Storage | `list` | ✅ | | -| Storage | `remove` | ✅ | | -| Storage | `copy` | ✅ | | - -## Migrate from Amplify JavaScript v5 - -The Amplify JS v5 `withSSRContext` utility is no longer available with Amplify JS v6. You will need to use the `createServerRunner` function exported from `@aws-amplify/adapter-nextjs` to create a `runWithAmplifyServerContext` function, and use this function to make Amplify API calls on the server side of your Next.js app. For usage examples, see [here](#calling-amplify-category-apis-on-the-server-side). + diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx new file mode 100644 index 00000000000..248bb9d0fb8 --- /dev/null +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx @@ -0,0 +1,349 @@ +import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; + +export const meta = { + title: 'Migrate from v5 to v6', + description: 'Learn more about the migration steps to upgrade Server-Side APIs for Amplify JavaScript v5 to v6', + platforms: ['nextjs'], +}; + +export const getStaticPaths = async () => { + return getCustomStaticPath(meta.platforms); +}; + +export function getStaticProps(context) { + return { + props: { + platform: context.params.platform, + meta + } + }; +} + +This migration guide is to help customers who are currently using `withSSRContext` in v5 move to using the new NextJS adapter in v6. + +## 1. Install NextJS Adapter + +You will need to upgrade your existing [environment](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#1-upgrade-your-dev-environment), [dependencies](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#2-update-your-amplify-project-dependencies) and [amplify cli](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#3-upgrade-amplify-cli-version-and-configuration-file) to `aws-amplify@6`-compatible versions + +```sh +npm install @aws-amplify/adapter-nextjs +``` + +## 2. Server-Side Configuration + +The Amplify JS v5 `withSSRContext` utility is no longer available with Amplify JS v6. You will need to use the `createServerRunner` function exported from `@aws-amplify/adapter-nextjs` to create a `runWithAmplifyServerContext` function, and use this function to make Amplify API calls on the server side of your Next.js app. + +You can create an `amplifyServerUtils.ts` file under a `utils` folder in your codebase. In this file, you will import the Amplify configuration object from the `amplifyconfiguration.json` file that is generated by the Amplify CLI, and use the `createServerRunner` function to create the `runWithAmplifyServerContextRunner` function. + +For example, the `utils/amplifyServerUtils.ts` file may contain the following content: + +``` +import { createServerRunner } from '@aws-amplify/adapter-nextjs'; +import config from '@/amplifyconfiguration.json'; + +export const { runWithAmplifyServerContext } = createServerRunner({ + config +}); +``` + +## 3. Client-Side Configuration + +`Amplify.configure` now has two params: `resourcesConfig` and `libraryOptions`. `ssr.true` should now be sent in with `libraryOptions`. + + +
+ **V5** + + ``` + import { Amplify } from 'aws-amplify'; + import awsExports from '@/aws-exports'; + + Amplify.configure({ + ...awsExports, + ssr: true + }); + ``` + +
+
+ **V6** + + ``` + import { Amplify } from 'aws-amplify'; + import amplifyConfig from '@/amplifyconfiguration.json'; + + Amplify.configure( + amplifyConfig, + { ssr: true } + ); + ``` +
+
+ +> To avoid repetitive calls to `Amplify.configure`, you can call it once in a top-level client-side rendered layout component. See [Configure Amplify library for client-side usage](/[platform]/build-a-backend/server-side-rendering/#configure-amplify-library-for-client-side-usage) for additional details. + +## 4. Replace `withSSRContext` with `runWithAmplifyServerContext` + +Make sure you are importing API’s from the `/server` subpath when in a server context. See [the full list](/[platform]/build-a-backend/server-side-rendering/#supported-apis-for-nextjs-server-side-usage) of supported APIs on the server side. + + +
+ **V5** + + ``` + import { withSSRContext } from 'aws-amplify'; + import { listTodos } from './graphql/queries'; + + const getServerSideProps = async ({ req }) => { + const SSR = withSSRContext({ req }); + const user = await SSR.Auth.currentAuthenticatedUser(); + + return { props: { user } }; + }; + ``` + +
+
+ **V6** + + ``` + import { getCurrentUser } from 'aws-amplify/auth/server'; + import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; + + const getServerSideProps = async ({ req, res }) => { + const user = await runWithAmplifyServerContext({ + nextServerContext: { request: req, response: res }, + operation: (contextSpec) => getCurrentUser(contextSpec) + }); + + return { props: { user } }; + } + ``` + +
+
+ +> Note: Next.js Middleware is now supported in v6. You can find instructions for implementation here: [Manage Auth session with Next.js Middleware](/[platform]/build-a-backend/server-side-rendering/#manage-auth-session-with-the-nextjs-middleware) + +## Using the API category in v6 + +Amplify v6 offers [two specialized GraphQL API clients](/[platform]/build-a-backend/graphqlapi/connect-from-server-runtime/#step-1---choose-the-correct-graphql-api-client-for-nextjs-server-runtimes) for Next.js server runtimes (imported from `@aws-amplify/adapter-nextjs/api`) that you should use depending whether you retrieve the user tokens using [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies) or [`NextRequest` and `NextResponse`](https://nextjs.org/docs/app/api-reference/functions/next-request). We will demonstrate the migration using the request/response-based server client to reflect what was previously available in v5. + +First add server-side client generation to [your `amplifyServerUtils` file:](/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/#2-server-side-configuration) + +``` +import { createServerRunner } from '@aws-amplify/adapter-nextjs'; +import config from '@/amplifyconfiguration.json'; + +export const { runWithAmplifyServerContext } = createServerRunner({ + config +}); + +export const reqResBasedClient = generateServerClientUsingReqRes({ + config +}); +``` + +Now update the implementation in your server-side-rendered component: + + +
+ **V5** + + ``` + import { withSSRContext } from 'aws-amplify'; + import { listTodos } from './graphql/queries'; + + const getServerSideProps = async ({ req }) => { + const SSR = withSSRContext({ req }); + const { data } = await SSR.API.graphql({ query: listTodos }); + + return { + props: { + posts: data.listTodos.items + } + }; + }; + ``` + +
+
+ **V6** + + ``` + import { + reqResBasedClient, + runWithAmplifyServerContext + } from '@/utils/amplifyServerUtils'; + import { listTodos } from './graphql/queries'; + + const getServerSideProps = async ({ req, res }) => { + const data = await runWithAmplifyServerContext({ + nextServerContext: { request: req, response: res }, + operation: async (contextSpec) => { + return reqResBasedClient.graphql(contextSpec, { + query: listTodos + }); + } + }); + + return { + props: { + todos: data.listTodos.items + } + }; + }; + ``` + +
+
+ +## Migrating from DataStore to API in a server context + +DataStore is no longer supported within a server context in v6. You will need to use the GraphQL API client instead. + +For a successful transition to using the API category in the server context, you will need to be aware of [how conflict resolution works](https://docs.aws.amazon.com/appsync/latest/devguide/conflict-detection-and-sync.html#conflict-detection-and-resolution), paying particular attention to `auto-merge`. This will require the following adjustments to standard GraphQL operations: + +1. You will need to ignore deleted records by filtering on `_deleted: false` when querying data + +2. You will need to provide `_version` for update and delete mutations. + +When updating operations from DataStore to GraphQL, you should also take into account that the return structure of a query is different between DataStore and GraphQL, so where DataStore would return `Todo[]`, a GraphQL query returns `{ data: { listTodos: { items: Todo[] } } }` + +If your project does not contain GraphQL mutations, queries, and subscriptions, you can use the amplify cli script `amplify codegen statements` to generate those files (See [the API (GraphQL) documentation](/[platform]/build-a-backend/graphqlapi/client-code-generation/#shared-schema-modified-elsewhere-eg-console-or-team-workflows) for more information) + + Do not try to perform DataStore mutations directly on items returned by a graphql operation. The returned types are not compatible and should not be mixed and matched. If you need to update an item using DataStore, you should first query that item using DataStore, then perform the update. + + +First, follow the instructions for [Using the API category in v6](/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/#using-the-api-category-in-v6) to set up the request/response-based server client, then update your server-side and client-side code to use the API category + +### DataStore.query + + +
+ **V5** + + ``` + import { serializeModel } from '@aws-amplify/datastore/ssr'; + import { Todo } from '@/src/models'; + import { withSSRContext } from 'aws-amplify'; + + export async function getServerSideProps({ req }) { + const SSR = withSSRContext({ req }); + const todos = await SSR.DataStore.query(Todo); + + return { + props: { + todos: serializeModel(todos), + }, + }; + } + ``` +
+
+ **V6** + + ``` + import { + reqResBasedClient, + runWithAmplifyServerContext + } from '@/utils/amplifyServerUtils'; + import { listTodos } from '@/graphql/queries'; + + // Server-Side + const getServerSideProps = async ({ req, res }) => { + const data = await runWithAmplifyServerContext({ + nextServerContext: { request: req, response: res }, + operation: async (contextSpec) => { + const variables = { + filter: { + _deleted: { eq: false } + } + }; + + return reqResBasedClient.graphql(contextSpec, { + query: listTodos, + variables + }); + } + }); + + return { + props: { + todos: data.listTodos.items + } + }; + }; + ``` +
+ +
+ +### DataStore.save + + +
+ **V5** + + ``` + import { serializeModel } from '@aws-amplify/datastore/ssr'; + import { Todo } from '@/src/models'; + import { withSSRContext } from 'aws-amplify'; + + export async function getServerSideProps({ req }) { + const SSR = withSSRContext({ req }); + + `const`` original ``=`` ``await`` SSR``.``DataStore``.``query``(``Todo``,`` ``'123456'``);` + const updatedTodo = await SSR.DataStore.save( + Todo.copyOf(original, updated => { + updated.lastViewed = new Date() + }) + ); + + return { + props: { + todo: serializeModel(updatedTodo), + }, + }; + } + ``` + +
+
+ **V6** + + ``` + import { + reqResBasedClient, + runWithAmplifyServerContext + } from '@/utils/amplifyServerUtils'; + import { updateTodo } from '@/graphql/queries'; + + // Server-Side + const getServerSideProps = async ({ req, res }) => { + const data = await runWithAmplifyServerContext({ + nextServerContext: { request: req, response: res }, + operation: async (contextSpec) => { + const todoDetails = { + id: '123456', + _version: 2, + lastViewed: new Date() + }; + + return reqResBasedClient.graphql(contextSpec, { + query: updateTodo, + variables: { input: todoDetails } + }); + } + }); + + return { + props: { + todo: data.updateTodo + } + }; + }; + ``` +
+ +
diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx new file mode 100644 index 00000000000..13e2287774c --- /dev/null +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx @@ -0,0 +1,383 @@ +import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; +import { getChildPageNodes } from '@/utils/getChildPageNodes'; + +export const meta = { + title: 'Use Amplify in Next.js', + description: 'Use Amplify categories APIs from Next.js server-side runtimes.', + platforms: [ + 'javascript', + 'react-native', + 'react', + 'nextjs', + 'angular', + 'vue' + ] +}; + +export const getStaticPaths = async () => { + return getCustomStaticPath(meta.platforms); +}; + +export function getStaticProps(context) { + return { + props: { + platform: context.params.platform, + meta + } + }; +} + +This guide walks through how to use Amplify Auth, GraphQL API, REST API, and Storage category APIs from Next.js server-side runtimes. + +import versionRangeCallout from '/src/fragments/lib/ssr/nextjs/version-range-callout.mdx'; + + + +Before you begin, you will need: + +- [A Next.js application created](/[platform]/start/project-setup/create-application/) +- [Installed Amplify libraries for Next.js](/[platform]/start/project-setup/create-application/#install-amplify-libraries) + +## Configure Amplify Library for server-side usage + +To use Amplify APIs on the server-side of your Next.js app, you will need to create a `runWithAmplifyServerContextRunner` function. + +You can create an `amplifyServerUtils.ts` file under a `utils` folder in your codebase. In this file, you will import the Amplify configuration object from the `amplifyconfiguration.json` file that is generated by the Amplify CLI, and use the `createServerRunner` function to create the `runWithAmplifyServerContextRunner` function. + +For example, the `utils/amplifyServerUtils.ts` file may contain the following content: + +```typescript +import { createServerRunner } from '@aws-amplify/adapter-nextjs'; +import config from '@/amplifyconfiguration.json'; + +export const { runWithAmplifyServerContext } = createServerRunner({ + config +}); +``` + +You can use the exported `runWithAmplifyServerContext` function to call Amplify APIs with in isolated request contexts. Usage examples see [here](#calling-amplify-category-apis-on-the-server-side). + +**TIP:** You only need to call the `createServerRunner` function once and reuse the `runWithAmplifyServerContext` function throughout. + +## Configure Amplify library for client-side usage + +When you use the Amplify library on the client-side of your Next.js app, you will need to configure Amplify by calling the Amplify.configure as you would to use Amplify in a single-page application. + + + +**NOTE:** To use the Amplify library on the client side in a Next.js app, you will need to set `ssr` to `true` when calling `Amplify.configure`. This instructs the Amplify library to store tokens in the cookie store of a browser. Cookies will be sent along with requests to your Next.js server for authentication. + + + +```typescript +'use client'; + +import config from '@/amplifyconfiguration.json'; +import { Amplify } from 'aws-amplify'; + +Amplify.configure(config, { + ssr: true // required when using Amplify with Next.js +}); + +export default function RootLayoutThatConfiguresAmplifyOnTheClient({ + children +}: { + children: React.ReactNode; +}) { + return children; +} +``` + + + +To avoid repetitive calls to `Amplify.configure`, you can call it once in a top-level client-side rendered layout component. + + + + + +If you're using the Next.js App Router, you can create a client component to configure Amplify and import it into your root layout. + +`ConfigureAmplifyClientSide.ts`: + +```typescript +'use client'; + +import { Amplify } from 'aws-amplify'; +import config from '../amplifyconfiguration.json'; + +Amplify.configure(config, { ssr: true }); + +export default function ConfigureAmplifyClientSide() { + return null; +} +``` + +`layout.tsx`: + +```jsx +import ConfigureAmplifyClientSide from '@/components/ConfigureAmplifyClientSide'; +import './globals.css'; + +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + <> + + {children} + + + + ); +} +``` + + + +## Authentication with Next.js server-side runtime + +You can use the Amplify Auth category APIs to sign up and sign in your end users on the client side. With setting `ssr: true` when calling `Amplify.configure`, the Amplify library uses cookies to store tokens, which will be sent along with HTTP requests to your Next.js app server. + +### Manage Auth session with the Next.js Middleware + +You can use the `fetchAuthSession` API to check the auth sessions that are attached to the incoming requests in the middleware of your Next.js app to protect your routes. For example: + +```typescript +import { fetchAuthSession } from 'aws-amplify/auth/server'; +import { NextRequest, NextResponse } from 'next/server'; +import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; + +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('/sign-in', 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) + */ + '/((?!api|_next/static|_next/image|favicon.ico|sign-in).*)' + ] +}; +``` + +In this example, if the incoming request is not associated with a valid user session the request will be redirected to the `/sign-in` route. + + + +**NOTE:** When calling `fetchAuthSession` with a `response` context, it will send the refreshed tokens (if any) back to the client via the `Set-Cookie` header in the response. + + + +## Calling Amplify category APIs on the server side + +For the **Auth**, **REST APIs**, and **Storage** categories to use Amplify APIs on the server in your Next.js app, you will need to: + +1. Import the API from the `/server` sub path. +2. Use the `runWithAmplifyServerContext` helper function created by calling the `createServerRunner` function exported from `@aws-amplify/adapter-nextjs` to call the Amplify API in an isolated server context. + +For the **GraphQL API** category, review [Connect to GraphQL API from server-side runtimes](/[platform]/build-a-backend/graphqlapi/connect-from-server-runtime/). + + + +**NOTE:** A subset of Amplify APIs can now be called on the server side of a Next.js app. These APIs are exported from the `/server` sub paths. See [the full list](#supported-apis-for-nextjs-server-side-usage) of supported APIs. + + + +### With Next.js App Router + +#### In React Server Component + +##### Dynamic Rendering + +Dynamic rendering is based on a user session extracted from an incoming request. + +```jsx +import { cookies } from 'next/headers'; +import { getCurrentUser } from '@aws-amplify/auth/server'; +import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; + +// This page always dynamically renders per request +export const dynamic = 'force-dynamic'; + +export default async function AuthGetCurrentUserServer() { + try { + const currentUser = await runWithAmplifyServerContext({ + nextServerContext: { cookies }, + operation: (contextSpec) => getCurrentUser(contextSpec) + }); + + return ( + + ); + } catch (error) { + console.error(error); + return

Something went wrong...

; + } +} +``` + +##### Static Rendering + +Static rendering doesn’t require a user session, so you can specify the `nextServerContext` parameter as `null`. This is useful for some use cases, for example, when you are using the Storage API with guest access (if you have enabled it in your backend). + +```jsx +import { getUrl } from 'aws-amplify/storage/server'; +import Image from 'next/image'; +import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; + +// Re-render this page every 60 minutes +export const revalidate = 60 * 60; // in seconds + +export default async function StaticallyRenderedPage() { + try { + const splashUrl = await runWithAmplifyServerContext({ + nextServerContext: null, + operation: (contextSpec) => + getUrl(contextSpec, { + key: 'splash.png' + }) + }); + + return ( + Splash Image + ); + } catch (error) { + console.error(error); + return

Something went wrong...

; + } +} +``` + + + +**NOTE:** The URL returned by the `getUrl` API expires in the above example. You may want to specify the `revalidate` parameter to rerender the page as required to ensure the URL gets regenerated. + + + +#### In Route Handlers + +Take implementing an API route that enables `GET /apis/get-current-user`. + +```typescript +import { getCurrentUser } from 'aws-amplify/auth/server'; +import { cookies } from 'next/headers'; +import { NextResponse } from 'next/server'; +import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; + +export async function GET() { + const user = await runWithAmplifyServerContext({ + nextServerContext: { cookies }, + operation: (contextSpec) => getCurrentUser(contextSpec) + }); + + return NextResponse.json({ user }); +} +``` + +When you call `fetch('/apis/get-current-user')` it return a payload that contains the `user` data for the currently signed-in user. + +### With Next.js Pages Router + +#### In `getServerSideProps` + +The following example extracts current user data from the request and provides them to a page react component via its props. + +```typescript +export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { + const currentUser = await runWithAmplifyServerContext({ + nextServerContext: { request: req, response: res }, + operation: (contextSpec) => getCurrentUser(contextSpec) + }); + + return { props: { currentUser } }; +}; +``` + +#### In `getStaticProps` + +Similar to static rendering with the App Router, you can pass `null` as the value of the `nextServerContext` parameter to use the Amplify Storage API with guest access. + +```typescript +export async function getStaticProps() { + const splashUrl = await runWithAmplifyServerContext({ + nextServerContext: null, + operation: (contextSpec) => getUrl(contextSpec, { key: 'splash.png' }) + }); + + return { + props: { imageUrl: splashUrl.url.toString() }, + revalidate: (splashUrl.expiresAt.getTime() - Date.now()) / 1000 // in seconds + }; +} +``` + +## Supported APIs for Next.js server-side usage + +All APIs that support use on the server are exported from the `aws-amplify//server` sub paths. You **must** use these APIs for any server side use cases. + +| Category | APIs | Server (Node.js) Amplify Hosting/Vercel | Vercel Edge Runtime (middleware) | +| --- | --- | --- | --- | +| Auth | `fetchAuthSession` | ✅ | ✅ | +| Auth | `fetchUserAttributes` | ✅ | ✅ | +| Auth | `getCurrentUser` | ✅ | ✅ | +| API (GraphQL) | `generateServerClientUsingCookies` | ✅ | | +| API (GraphQL) | `generateServerClientUsingReqRes` | ✅ | | +| API (REST) | `GET` | ✅ | | +| API (REST) | `POST` | ✅ | | +| API (REST) | `PUT` | ✅ | | +| API (REST) | `DEL` | ✅ | | +| API (REST) | `HEAD` | ✅ | | +| API (REST) | `PATCH` | ✅ | | +| Storage | `getUrl` | ✅ | | +| Storage | `getProperties` | ✅ | | +| Storage | `list` | ✅ | | +| Storage | `remove` | ✅ | | +| Storage | `copy` | ✅ | | + +## Migrate from Amplify JavaScript v5 + +The Amplify JS v5 `withSSRContext` utility is no longer available with Amplify JS v6. You will need to use the `createServerRunner` function exported from `@aws-amplify/adapter-nextjs` to create a `runWithAmplifyServerContext` function, and use this function to make Amplify API calls on the server side of your Next.js app. For usage examples, see [here](#calling-amplify-category-apis-on-the-server-side). From 3bd205a736d3acef3641eae454e60d68ab0ecf08 Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Thu, 7 Dec 2023 10:06:38 -0700 Subject: [PATCH 2/9] chore: address pr feedback on verbiage and syntax highlighting --- .../nextjs-migration-guide/index.mdx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx index 248bb9d0fb8..9a9cea54541 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx @@ -19,9 +19,9 @@ export function getStaticProps(context) { }; } -This migration guide is to help customers who are currently using `withSSRContext` in v5 move to using the new NextJS adapter in v6. +This guide will help you migrate v5 SSR implementations that use `withSSRContext` to the new `Next.js` adapter in v6. -## 1. Install NextJS Adapter +## 1. Install `Next.js` Adapter You will need to upgrade your existing [environment](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#1-upgrade-your-dev-environment), [dependencies](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#2-update-your-amplify-project-dependencies) and [amplify cli](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#3-upgrade-amplify-cli-version-and-configuration-file) to `aws-amplify@6`-compatible versions @@ -37,7 +37,7 @@ You can create an `amplifyServerUtils.ts` file under a `utils` folder in your co For example, the `utils/amplifyServerUtils.ts` file may contain the following content: -``` +```ts import { createServerRunner } from '@aws-amplify/adapter-nextjs'; import config from '@/amplifyconfiguration.json'; @@ -54,7 +54,7 @@ export const { runWithAmplifyServerContext } = createServerRunner({
**V5** - ``` + ```ts import { Amplify } from 'aws-amplify'; import awsExports from '@/aws-exports'; @@ -68,7 +68,7 @@ export const { runWithAmplifyServerContext } = createServerRunner({
**V6** - ``` + ```ts import { Amplify } from 'aws-amplify'; import amplifyConfig from '@/amplifyconfiguration.json'; @@ -90,7 +90,7 @@ Make sure you are importing API’s from the `/server` subpath when in a server
**V5** - ``` + ```ts import { withSSRContext } from 'aws-amplify'; import { listTodos } from './graphql/queries'; @@ -106,7 +106,7 @@ Make sure you are importing API’s from the `/server` subpath when in a server
**V6** - ``` + ```ts import { getCurrentUser } from 'aws-amplify/auth/server'; import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; @@ -131,7 +131,7 @@ Amplify v6 offers [two specialized GraphQL API clients](/[platform]/build-a-back First add server-side client generation to [your `amplifyServerUtils` file:](/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/#2-server-side-configuration) -``` +```ts import { createServerRunner } from '@aws-amplify/adapter-nextjs'; import config from '@/amplifyconfiguration.json'; @@ -150,7 +150,7 @@ Now update the implementation in your server-side-rendered component:
**V5** - ``` + ```ts import { withSSRContext } from 'aws-amplify'; import { listTodos } from './graphql/queries'; @@ -170,7 +170,7 @@ Now update the implementation in your server-side-rendered component:
**V6** - ``` + ```ts import { reqResBasedClient, runWithAmplifyServerContext @@ -223,7 +223,7 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu
**V5** - ``` + ```ts import { serializeModel } from '@aws-amplify/datastore/ssr'; import { Todo } from '@/src/models'; import { withSSRContext } from 'aws-amplify'; @@ -243,7 +243,7 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu
**V6** - ``` + ```ts import { reqResBasedClient, runWithAmplifyServerContext @@ -285,7 +285,7 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu
**V5** - ``` + ```ts import { serializeModel } from '@aws-amplify/datastore/ssr'; import { Todo } from '@/src/models'; import { withSSRContext } from 'aws-amplify'; @@ -293,7 +293,7 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu export async function getServerSideProps({ req }) { const SSR = withSSRContext({ req }); - `const`` original ``=`` ``await`` SSR``.``DataStore``.``query``(``Todo``,`` ``'123456'``);` + const original = await SSR.DataStore.query(Todo, '123456'); const updatedTodo = await SSR.DataStore.save( Todo.copyOf(original, updated => { updated.lastViewed = new Date() @@ -312,7 +312,7 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu
**V6** - ``` + ```ts import { reqResBasedClient, runWithAmplifyServerContext From 675d664c0638a4b38b92103f28e63e0abc02fe1b Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Thu, 7 Dec 2023 10:38:08 -0700 Subject: [PATCH 3/9] chore: simplify path for nextjs guide --- src/directory/directory.mjs | 2 +- .../{use-amplify-in-nextjs => nextjs}/index.mdx | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/pages/[platform]/build-a-backend/server-side-rendering/{use-amplify-in-nextjs => nextjs}/index.mdx (100%) diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 57b82577d0d..d8846d62b89 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -449,7 +449,7 @@ export const directory = { path: 'src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx', children: [ { - path: 'src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx' + path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx' }, { path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx' diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx similarity index 100% rename from src/pages/[platform]/build-a-backend/server-side-rendering/use-amplify-in-nextjs/index.mdx rename to src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx From d876e68353e16a9d8032b4d247dff285bd3f449d Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Fri, 8 Dec 2023 14:58:44 -0700 Subject: [PATCH 4/9] fix: update ds to api examples based on tests --- .../nextjs-migration-guide/index.mdx | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx index 9a9cea54541..5d780405e3a 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx @@ -21,7 +21,7 @@ export function getStaticProps(context) { This guide will help you migrate v5 SSR implementations that use `withSSRContext` to the new `Next.js` adapter in v6. -## 1. Install `Next.js` Adapter +## Step 1: Install `Next.js` Adapter You will need to upgrade your existing [environment](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#1-upgrade-your-dev-environment), [dependencies](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#2-update-your-amplify-project-dependencies) and [amplify cli](/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/#3-upgrade-amplify-cli-version-and-configuration-file) to `aws-amplify@6`-compatible versions @@ -29,7 +29,7 @@ This guide will help you migrate v5 SSR implementations that use `withSSRContext npm install @aws-amplify/adapter-nextjs ``` -## 2. Server-Side Configuration +## Step 2: Server-Side Configuration The Amplify JS v5 `withSSRContext` utility is no longer available with Amplify JS v6. You will need to use the `createServerRunner` function exported from `@aws-amplify/adapter-nextjs` to create a `runWithAmplifyServerContext` function, and use this function to make Amplify API calls on the server side of your Next.js app. @@ -46,7 +46,7 @@ export const { runWithAmplifyServerContext } = createServerRunner({ }); ``` -## 3. Client-Side Configuration +## Step 3: Client-Side Configuration `Amplify.configure` now has two params: `resourcesConfig` and `libraryOptions`. `ssr.true` should now be sent in with `libraryOptions`. @@ -82,7 +82,7 @@ export const { runWithAmplifyServerContext } = createServerRunner({ > To avoid repetitive calls to `Amplify.configure`, you can call it once in a top-level client-side rendered layout component. See [Configure Amplify library for client-side usage](/[platform]/build-a-backend/server-side-rendering/#configure-amplify-library-for-client-side-usage) for additional details. -## 4. Replace `withSSRContext` with `runWithAmplifyServerContext` +## Step 4: Replace `withSSRContext` with `runWithAmplifyServerContext` Make sure you are importing API’s from the `/server` subpath when in a server context. See [the full list](/[platform]/build-a-backend/server-side-rendering/#supported-apis-for-nextjs-server-side-usage) of supported APIs on the server side. @@ -206,18 +206,26 @@ For a successful transition to using the API category in the server context, you 1. You will need to ignore deleted records by filtering on `_deleted: false` when querying data -2. You will need to provide `_version` for update and delete mutations. +2. You will need to ensure that `_version` is included in your graphql queries, mutations, and subscriptions (included by default in amplify generated statements). -When updating operations from DataStore to GraphQL, you should also take into account that the return structure of a query is different between DataStore and GraphQL, so where DataStore would return `Todo[]`, a GraphQL query returns `{ data: { listTodos: { items: Todo[] } } }` +When updating operations from DataStore to GraphQL, you should also take into account that the return structure of a query is different between DataStore and GraphQL, so where `DataStore.query` would return `Todo[]`, a GraphQL query returns `{ data: { listTodos: { items: Todo[] } } }`, and where `DataStore.save` returns a Todo, a graphql create/update will return `{ data.createTodo }` -If your project does not contain GraphQL mutations, queries, and subscriptions, you can use the amplify cli script `amplify codegen statements` to generate those files (See [the API (GraphQL) documentation](/[platform]/build-a-backend/graphqlapi/client-code-generation/#shared-schema-modified-elsewhere-eg-console-or-team-workflows) for more information) +If your project does not contain GraphQL mutations, queries, subscriptions, and types, you can use the amplify cli scripts below to generate those files (See [the API (GraphQL) documentation](/[platform]/build-a-backend/graphqlapi/client-code-generation/#shared-schema-modified-elsewhere-eg-console-or-team-workflows) for more information) - Do not try to perform DataStore mutations directly on items returned by a graphql operation. The returned types are not compatible and should not be mixed and matched. If you need to update an item using DataStore, you should first query that item using DataStore, then perform the update. +```sh +amplify codegen statements +amplify codegen types +``` + + Do not try to perform DataStore mutations directly on items returned by graphql operations. The returned types are not compatible and should not be mixed. If you need to update an item using DataStore, you should first query that item using DataStore, then perform the update. +### Setup API Client -First, follow the instructions for [Using the API category in v6](/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/#using-the-api-category-in-v6) to set up the request/response-based server client, then update your server-side and client-side code to use the API category +Follow the instructions for [Using the API category in v6](/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/#using-the-api-category-in-v6) to set up the request/response-based server client. -### DataStore.query +### Replace `DataStore.query` with `API.graphql` + +> Note: if you were using the `MULTI_AUTH` auth mode strategy in DataStore, you may need to send an authMode in your `API.graphql` call.
@@ -263,7 +271,8 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu return reqResBasedClient.graphql(contextSpec, { query: listTodos, - variables + variables, + authMode: 'apiKey' }); } }); @@ -279,7 +288,9 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu -### DataStore.save +### Replace `DataStore.save` with `API.graphql` + +> Note: if you were using the `MULTI_AUTH` auth mode strategy in DataStore, you may need to send an authMode in your `API.graphql` call.
@@ -318,6 +329,7 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu runWithAmplifyServerContext } from '@/utils/amplifyServerUtils'; import { updateTodo } from '@/graphql/queries'; + import { GRAPHQL_AUTH_MODE } from // Server-Side const getServerSideProps = async ({ req, res }) => { @@ -326,13 +338,13 @@ First, follow the instructions for [Using the API category in v6](/[platform]/bu operation: async (contextSpec) => { const todoDetails = { id: '123456', - _version: 2, lastViewed: new Date() }; return reqResBasedClient.graphql(contextSpec, { query: updateTodo, - variables: { input: todoDetails } + variables: { input: todoDetails }, + authMode: 'userPool' }); } }); From 22851b78d5313609b8ff056ec656af78823df5cf Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Fri, 8 Dec 2023 15:39:19 -0700 Subject: [PATCH 5/9] chore: add v5-to-v6 to migration guide path: --- src/directory/directory.mjs | 2 +- .../index.mdx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/pages/[platform]/build-a-backend/server-side-rendering/{nextjs-migration-guide => nextjs-v5-to-v6-migration-guide}/index.mdx (98%) diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index d8846d62b89..88b9b679eac 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -452,7 +452,7 @@ export const directory = { path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx' }, { - path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx' + path: 'src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx' } ] }, diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx similarity index 98% rename from src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx rename to src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx index 5d780405e3a..1d7c96a30f5 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx @@ -129,7 +129,7 @@ Make sure you are importing API’s from the `/server` subpath when in a server Amplify v6 offers [two specialized GraphQL API clients](/[platform]/build-a-backend/graphqlapi/connect-from-server-runtime/#step-1---choose-the-correct-graphql-api-client-for-nextjs-server-runtimes) for Next.js server runtimes (imported from `@aws-amplify/adapter-nextjs/api`) that you should use depending whether you retrieve the user tokens using [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies) or [`NextRequest` and `NextResponse`](https://nextjs.org/docs/app/api-reference/functions/next-request). We will demonstrate the migration using the request/response-based server client to reflect what was previously available in v5. -First add server-side client generation to [your `amplifyServerUtils` file:](/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/#2-server-side-configuration) +First add server-side client generation to [your `amplifyServerUtils` file:](/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/#2-server-side-configuration) ```ts import { createServerRunner } from '@aws-amplify/adapter-nextjs'; @@ -221,7 +221,7 @@ amplify codegen types ### Setup API Client -Follow the instructions for [Using the API category in v6](/[platform]/build-a-backend/server-side-rendering/nextjs-migration-guide/#using-the-api-category-in-v6) to set up the request/response-based server client. +Follow the instructions for [Using the API category in v6](/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/#using-the-api-category-in-v6) to set up the request/response-based server client. ### Replace `DataStore.query` with `API.graphql` From a3115f372522cf9f57085c4fe822ebab648d3150 Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Fri, 8 Dec 2023 15:45:47 -0700 Subject: [PATCH 6/9] chore: fix inconsistent wording in return types --- .../nextjs-v5-to-v6-migration-guide/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx index 1d7c96a30f5..5c1b8a18ce7 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx @@ -208,7 +208,7 @@ For a successful transition to using the API category in the server context, you 2. You will need to ensure that `_version` is included in your graphql queries, mutations, and subscriptions (included by default in amplify generated statements). -When updating operations from DataStore to GraphQL, you should also take into account that the return structure of a query is different between DataStore and GraphQL, so where `DataStore.query` would return `Todo[]`, a GraphQL query returns `{ data: { listTodos: { items: Todo[] } } }`, and where `DataStore.save` returns a Todo, a graphql create/update will return `{ data.createTodo }` +When migrating operations from DataStore to GraphQL, you should also take into account that the return structures are different: where `DataStore.query` returns `Todo[]`, an `API.graphql` list operation will return `{ data: { listTodos: { items: Todo[] } } }`, and where `DataStore.save` returns `Todo`, an `API.graphql` create/update operation will return `{ data: { createTodo: Todo } }` and `{ data: { updateTodo: Todo } }` respectively If your project does not contain GraphQL mutations, queries, subscriptions, and types, you can use the amplify cli scripts below to generate those files (See [the API (GraphQL) documentation](/[platform]/build-a-backend/graphqlapi/client-code-generation/#shared-schema-modified-elsewhere-eg-console-or-team-workflows) for more information) From 35a150758d8ab00c00afc9e1f2b3e3750a95c5de Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Fri, 8 Dec 2023 15:57:39 -0700 Subject: [PATCH 7/9] chore: add links to ssr migration guide to overview --- src/fragments/lib/troubleshooting/common/upgrading.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fragments/lib/troubleshooting/common/upgrading.mdx b/src/fragments/lib/troubleshooting/common/upgrading.mdx index a1b6383f7df..b8119bc055c 100644 --- a/src/fragments/lib/troubleshooting/common/upgrading.mdx +++ b/src/fragments/lib/troubleshooting/common/upgrading.mdx @@ -927,13 +927,13 @@ For a deeper look at v6 Utilities, check out our [Utilities documentation](/[pla ### Server-side Rendering - +Find a comprehensive summary of changes to Server-Side Rendering in the [NextJS migration guide](/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/) -DataStore is no longer supported in an SSR context: if you are using DataStore within your SSR project, you will need to migrate to the API category. +The Amplify JS v5 `withSSRContext` utility is no longer available with Amplify JS v6. You will need to use the `runWithAmplifyServerContext` function exported from `@aws-amplify/adapter-nextjs` to use Amplify categories on the server side of your Next.js app. - +> Note: DataStore is no longer supported in an SSR context: if you are using DataStore within your SSR project, you will need to migrate to the API category. For details on how to accomplish this, see the [NextJS migration guide: Migrating from DataStore to API in a server context](/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/#migrating-from-datastore-to-api-in-a-server-context) -The Amplify JS v5 withSSRContext utility is no longer available with Amplify JS v6. You will need to use the runWithAmplifyServerContext function exported from @aws-amplify/adapter-nextjs to make Amplify API calls on the server side of your Next.js app. Use the switcher below to see the differences between v5 and v6: +Use the switcher below to see the differences between v5 and v6: From d7572ca177f3383d4c8a2f6e80588b8b49555b96 Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Tue, 12 Dec 2023 11:48:34 -0700 Subject: [PATCH 8/9] chore: implement feedback, minor nit fixes --- .../nextjs-v5-to-v6-migration-guide/index.mdx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx index 5c1b8a18ce7..edacc1950a7 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/index.mdx @@ -123,7 +123,7 @@ Make sure you are importing API’s from the `/server` subpath when in a server
-> Note: Next.js Middleware is now supported in v6. You can find instructions for implementation here: [Manage Auth session with Next.js Middleware](/[platform]/build-a-backend/server-side-rendering/#manage-auth-session-with-the-nextjs-middleware) +> **Note:** Next.js Middleware is now supported in v6. You can find instructions for implementation here: [Manage Auth session with Next.js Middleware](/[platform]/build-a-backend/server-side-rendering/#manage-auth-session-with-the-nextjs-middleware) ## Using the API category in v6 @@ -198,17 +198,17 @@ Now update the implementation in your server-side-rendered component:
-## Migrating from DataStore to API in a server context +## Migrating from DataStore to GraphQL API client in a server context DataStore is no longer supported within a server context in v6. You will need to use the GraphQL API client instead. -For a successful transition to using the API category in the server context, you will need to be aware of [how conflict resolution works](https://docs.aws.amazon.com/appsync/latest/devguide/conflict-detection-and-sync.html#conflict-detection-and-resolution), paying particular attention to `auto-merge`. This will require the following adjustments to standard GraphQL operations: +For a successful transition to using the API category, you will need to be aware of [how conflict resolution works](https://docs.aws.amazon.com/appsync/latest/devguide/conflict-detection-and-sync.html#conflict-detection-and-resolution). DataStore API's use the `auto-merge` strategy as the default, which is assumed in the examples below. The `auth-merge` strategy requires the following adjustments to standard GraphQL operations: 1. You will need to ignore deleted records by filtering on `_deleted: false` when querying data -2. You will need to ensure that `_version` is included in your graphql queries, mutations, and subscriptions (included by default in amplify generated statements). +2. You will need to ensure that `_version` is included in the selection sets of GraphQL queries, mutations, and subscriptions (included by default in amplify generated statements). -When migrating operations from DataStore to GraphQL, you should also take into account that the return structures are different: where `DataStore.query` returns `Todo[]`, an `API.graphql` list operation will return `{ data: { listTodos: { items: Todo[] } } }`, and where `DataStore.save` returns `Todo`, an `API.graphql` create/update operation will return `{ data: { createTodo: Todo } }` and `{ data: { updateTodo: Todo } }` respectively +When migrating operations from DataStore to GraphQL, you should also take into account that the return structures are different: where `DataStore.query` returns `Todo[]`, an `API.graphql` list operation will return `{ data: { listTodos: { items: Todo[] } } }`, and where `DataStore.save` returns `Todo`, an `API.graphql` create/update operation will return `{ data: { createTodo: Todo } }` and `{ data: { updateTodo: Todo } }` respectively. If your project does not contain GraphQL mutations, queries, subscriptions, and types, you can use the amplify cli scripts below to generate those files (See [the API (GraphQL) documentation](/[platform]/build-a-backend/graphqlapi/client-code-generation/#shared-schema-modified-elsewhere-eg-console-or-team-workflows) for more information) @@ -217,7 +217,7 @@ amplify codegen statements amplify codegen types ``` - Do not try to perform DataStore mutations directly on items returned by graphql operations. The returned types are not compatible and should not be mixed. If you need to update an item using DataStore, you should first query that item using DataStore, then perform the update. + Do not try to perform DataStore mutations directly on items returned by GraphQL operations. The returned types are not compatible and should not be mixed. If you need to update an item using DataStore client-side, you should first query that item using DataStore, then perform the update. ### Setup API Client @@ -225,7 +225,7 @@ Follow the instructions for [Using the API category in v6](/[platform]/build-a-b ### Replace `DataStore.query` with `API.graphql` -> Note: if you were using the `MULTI_AUTH` auth mode strategy in DataStore, you may need to send an authMode in your `API.graphql` call. +> **Note:** if you were using the `MULTI_AUTH` auth mode strategy in DataStore, you may need to send an authMode in your `API.graphql` call.
@@ -272,7 +272,7 @@ Follow the instructions for [Using the API category in v6](/[platform]/build-a-b return reqResBasedClient.graphql(contextSpec, { query: listTodos, variables, - authMode: 'apiKey' + authMode: 'apiKey' // May be required when using multi-auth }); } }); @@ -290,7 +290,7 @@ Follow the instructions for [Using the API category in v6](/[platform]/build-a-b ### Replace `DataStore.save` with `API.graphql` -> Note: if you were using the `MULTI_AUTH` auth mode strategy in DataStore, you may need to send an authMode in your `API.graphql` call. +> **Note:** if you were using the `MULTI_AUTH` auth mode strategy in DataStore, you may need to send an authMode in your `API.graphql` call.
@@ -344,7 +344,7 @@ Follow the instructions for [Using the API category in v6](/[platform]/build-a-b return reqResBasedClient.graphql(contextSpec, { query: updateTodo, variables: { input: todoDetails }, - authMode: 'userPool' + authMode: 'userPool' // May be required when using multi-auth }); } }); From d1e3a4b48699e284dd448d702b2e8b0259209fb9 Mon Sep 17 00:00:00 2001 From: Erin Beal Date: Tue, 12 Dec 2023 11:55:30 -0700 Subject: [PATCH 9/9] fix: update outdated link --- src/fragments/lib/troubleshooting/common/upgrading.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fragments/lib/troubleshooting/common/upgrading.mdx b/src/fragments/lib/troubleshooting/common/upgrading.mdx index b8119bc055c..00d770b4e85 100644 --- a/src/fragments/lib/troubleshooting/common/upgrading.mdx +++ b/src/fragments/lib/troubleshooting/common/upgrading.mdx @@ -931,7 +931,7 @@ For a deeper look at v6 Utilities, check out our [Utilities documentation](/[pla The Amplify JS v5 `withSSRContext` utility is no longer available with Amplify JS v6. You will need to use the `runWithAmplifyServerContext` function exported from `@aws-amplify/adapter-nextjs` to use Amplify categories on the server side of your Next.js app. -> Note: DataStore is no longer supported in an SSR context: if you are using DataStore within your SSR project, you will need to migrate to the API category. For details on how to accomplish this, see the [NextJS migration guide: Migrating from DataStore to API in a server context](/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/#migrating-from-datastore-to-api-in-a-server-context) +> Note: DataStore is no longer supported in an SSR context: if you are using DataStore within your SSR project, you will need to migrate to the API category. For details on how to accomplish this, see the [NextJS migration guide: Migrating from DataStore to API in a server context](/[platform]/build-a-backend/server-side-rendering/nextjs-v5-to-v6-migration-guide/#migrating-from-datastore-to-graphql-api-client-in-a-server-context) Use the switcher below to see the differences between v5 and v6: