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-nextjs
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.
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: false
when querying data -
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.
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 types
Setup 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_AUTH
auth mode strategy in DataStore, you may need to send an authMode in yourAPI.graphql
call.
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_AUTH
auth mode strategy in DataStore, you may need to send an authMode in yourAPI.graphql
call.
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 } };};