Page updated Dec 12, 2023

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

You will need to upgrade your existing environment, dependencies and amplify cli to aws-amplify@6-compatible versions
1npm 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:

1import { createServerRunner } from '@aws-amplify/adapter-nextjs';
2import config from '@/amplifyconfiguration.json';
3
4export const { runWithAmplifyServerContext } = createServerRunner({
5 config
6});

Step 3: Client-Side Configuration

Amplify.configure now has two params: resourcesConfig and libraryOptions. ssr.true should now be sent in with libraryOptions.

V5

1import { Amplify } from 'aws-amplify';
2import awsExports from '@/aws-exports';
3
4Amplify.configure({
5 ...awsExports,
6 ssr: true
7});

V6

1import { Amplify } from 'aws-amplify';
2import amplifyConfig from '@/amplifyconfiguration.json';
3
4Amplify.configure(
5 amplifyConfig,
6 { ssr: true }
7);

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

1import { withSSRContext } from 'aws-amplify';
2import { listTodos } from './graphql/queries';
3
4const getServerSideProps = async ({ req }) => {
5 const SSR = withSSRContext({ req });
6 const user = await SSR.Auth.currentAuthenticatedUser();
7
8 return { props: { user } };
9};

V6

1import { getCurrentUser } from 'aws-amplify/auth/server';
2import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils';
3
4const getServerSideProps = async ({ req, res }) => {
5 const user = await runWithAmplifyServerContext({
6 nextServerContext: { request: req, response: res },
7 operation: (contextSpec) => getCurrentUser(contextSpec)
8 });
9
10 return { props: { user } };
11}

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:

1import { createServerRunner } from '@aws-amplify/adapter-nextjs';
2import config from '@/amplifyconfiguration.json';
3
4export const { runWithAmplifyServerContext } = createServerRunner({
5 config
6});
7
8export const reqResBasedClient = generateServerClientUsingReqRes({
9 config
10});

Now update the implementation in your server-side-rendered component:

V5

1import { withSSRContext } from 'aws-amplify';
2import { listTodos } from './graphql/queries';
3
4const getServerSideProps = async ({ req }) => {
5 const SSR = withSSRContext({ req });
6 const { data } = await SSR.API.graphql({ query: listTodos });
7
8 return {
9 props: {
10 posts: data.listTodos.items
11 }
12 };
13};

V6

1import {
2 reqResBasedClient,
3 runWithAmplifyServerContext
4} from '@/utils/amplifyServerUtils';
5import { listTodos } from './graphql/queries';
6
7const getServerSideProps = async ({ req, res }) => {
8 const data = await runWithAmplifyServerContext({
9 nextServerContext: { request: req, response: res },
10 operation: async (contextSpec) => {
11 return reqResBasedClient.graphql(contextSpec, {
12 query: listTodos
13 });
14 }
15 });
16
17 return {
18 props: {
19 todos: data.listTodos.items
20 }
21 };
22};

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:

  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 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)

1amplify codegen statements
2amplify 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 client-side, you should first query that item using DataStore, then perform the update.

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 your API.graphql call.

V5

1import { serializeModel } from '@aws-amplify/datastore/ssr';
2import { Todo } from '@/src/models';
3import { withSSRContext } from 'aws-amplify';
4
5export async function getServerSideProps({ req }) {
6 const SSR = withSSRContext({ req });
7 const todos = await SSR.DataStore.query(Todo);
8
9 return {
10 props: {
11 todos: serializeModel(todos),
12 },
13 };
14}

V6

1import {
2 reqResBasedClient,
3 runWithAmplifyServerContext
4} from '@/utils/amplifyServerUtils';
5import { listTodos } from '@/graphql/queries';
6
7// Server-Side
8const getServerSideProps = async ({ req, res }) => {
9 const data = await runWithAmplifyServerContext({
10 nextServerContext: { request: req, response: res },
11 operation: async (contextSpec) => {
12 const variables = {
13 filter: {
14 _deleted: { eq: false }
15 }
16 };
17
18 return reqResBasedClient.graphql(contextSpec, {
19 query: listTodos,
20 variables,
21 authMode: 'apiKey' // May be required when using multi-auth
22 });
23 }
24 });
25
26 return {
27 props: {
28 todos: data.listTodos.items
29 }
30 };
31};

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.

V5

1import { serializeModel } from '@aws-amplify/datastore/ssr';
2import { Todo } from '@/src/models';
3import { withSSRContext } from 'aws-amplify';
4
5export async function getServerSideProps({ req }) {
6 const SSR = withSSRContext({ req });
7
8 const original = await SSR.DataStore.query(Todo, '123456');
9 const updatedTodo = await SSR.DataStore.save(
10 Todo.copyOf(original, updated => {
11 updated.lastViewed = new Date()
12 })
13);
14
15 return {
16 props: {
17 todo: serializeModel(updatedTodo),
18 },
19 };
20}

V6

1import {
2 reqResBasedClient,
3 runWithAmplifyServerContext
4} from '@/utils/amplifyServerUtils';
5import { updateTodo } from '@/graphql/queries';
6import { GRAPHQL_AUTH_MODE } from
7
8// Server-Side
9const getServerSideProps = async ({ req, res }) => {
10 const data = await runWithAmplifyServerContext({
11 nextServerContext: { request: req, response: res },
12 operation: async (contextSpec) => {
13 const todoDetails = {
14 id: '123456',
15 lastViewed: new Date()
16 };
17
18 return reqResBasedClient.graphql(contextSpec, {
19 query: updateTodo,
20 variables: { input: todoDetails },
21 authMode: 'userPool' // May be required when using multi-auth
22 });
23 }
24 });
25
26 return {
27 props: {
28 todo: data.updateTodo
29 }
30 };
31};