Migrate from v5 to v6
This guide will help you migrate v5 SSR implementations that use withSSRContext to the new Next.js adapter in v6.
Step 1: Install Next.js Adapter
npm install @aws-amplify/adapter-nextjsStep 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});Step 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 for additional details.
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 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
Using the API category in v6
Amplify v6 offers two specialized GraphQL API clients 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 or NextRequest and NextResponse. 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:
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 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, you will need to be aware of how conflict resolution works. 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:
-
You will need to ignore deleted records by filtering on
_deleted: falsewhen querying data -
You will need to ensure that
_versionis 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.
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 for more information)
amplify codegen statementsamplify codegen typesSetup API Client
Follow the instructions for Using the API category in v6 to set up the request/response-based server client.
Replace DataStore.query with API.graphql
Note: if you were using the
MULTI_AUTHauth mode strategy in DataStore, you may need to send an authMode in yourAPI.graphqlcall.
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-Sideconst 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, authMode: 'apiKey' // May be required when using multi-auth }); } });
return { props: { todos: data.listTodos.items } };};Replace DataStore.save with API.graphql
Note: if you were using the
MULTI_AUTHauth mode strategy in DataStore, you may need to send an authMode in yourAPI.graphqlcall.
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';import { GRAPHQL_AUTH_MODE } from
// Server-Sideconst getServerSideProps = async ({ req, res }) => { const data = await runWithAmplifyServerContext({ nextServerContext: { request: req, response: res }, operation: async (contextSpec) => { const todoDetails = { id: '123456', lastViewed: new Date() }; return reqResBasedClient.graphql(contextSpec, { query: updateTodo, variables: { input: todoDetails }, authMode: 'userPool' // May be required when using multi-auth }); } });
return { props: { todo: data.updateTodo } };};