Page updated Nov 20, 2023

Server-Side Rendering

This guide walks through how to use Amplify Auth and Data APIs from Next.js server-side runtimes.

Before you begin, you will need:

Install the Amplify Next.js adapter

To use Amplify APIs server-side, you need to install the Amplify Next.js adapter in addition to the Amplify libraries:

npm add aws-amplify @aws-amplify/adapter-nextjs
1npm add aws-amplify @aws-amplify/adapter-nextjs

Configure Amplify APIs for server-side usage

You will need to create a runWithAmplifyServerContextRunner function to use Amplify APIs 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 });
1import { createServerRunner } from '@aws-amplify/adapter-nextjs';
2import config from '@/amplifyconfiguration.json';
3
4export const { runWithAmplifyServerContext } = createServerRunner({
5 config
6});

You can use the exported runWithAmplifyServerContext function to call Amplify APIs within isolated request contexts. You can review examples under the Calling Amplify category APIs on the server side section.

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.

'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; }
1'use client';
2
3import config from '@/amplifyconfiguration.json';
4import { Amplify } from 'aws-amplify';
5
6Amplify.configure(config, {
7 ssr: true // required when using Amplify with Next.js
8});
9
10export default function RootLayoutThatConfiguresAmplifyOnTheClient({
11 children
12}: {
13 children: React.ReactNode;
14}) {
15 return children;
16}

To avoid repetitive calls to Amplify.configure, you can call it once in a top-level client-side rendered layout component.

Learn more
Configure Amplify in a Next.js App Router application

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:

'use client'; import { Amplify } from 'aws-amplify'; import config from '../amplifyconfiguration.json'; Amplify.configure(config, { ssr: true }); export default function ConfigureAmplifyClientSide() { return null; }
1'use client';
2
3import { Amplify } from 'aws-amplify';
4import config from '../amplifyconfiguration.json';
5
6Amplify.configure(config, { ssr: true });
7
8export default function ConfigureAmplifyClientSide() {
9 return null;
10}

layout.tsx:

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 ( <html lang="en"> <body className="container pb-6"> <> <ConfigureAmplifyClientSide /> {children} </> </body> </html> ); }
1import ConfigureAmplifyClientSide from '@/components/ConfigureAmplifyClientSide';
2import './globals.css';
3
4import type { Metadata } from 'next';
5
6export const metadata: Metadata = {
7 title: 'Create Next App',
8 description: 'Generated by create next app',
9};
10
11export default function RootLayout({
12 children,
13}: {
14 children: React.ReactNode;
15}) {
16 return (
17 <html lang="en">
18 <body className="container pb-6">
19 <>
20 <ConfigureAmplifyClientSide />
21 {children}
22 </>
23 </body>
24 </html>
25 );
26}

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. When you set 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:

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).*)' ] };
1import { fetchAuthSession } from 'aws-amplify/auth/server';
2import { NextRequest, NextResponse } from 'next/server';
3import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils';
4
5export async function middleware(request: NextRequest) {
6 const response = NextResponse.next();
7
8 const authenticated = await runWithAmplifyServerContext({
9 nextServerContext: { request, response },
10 operation: async (contextSpec) => {
11 try {
12 const session = await fetchAuthSession(contextSpec);
13 return session.tokens !== undefined;
14 } catch (error) {
15 console.log(error);
16 return false;
17 }
18 }
19 });
20
21 if (authenticated) {
22 return response;
23 }
24
25 return NextResponse.redirect(new URL('/sign-in', request.url));
26}
27
28export const config = {
29 matcher: [
30 /*
31 * Match all request paths except for the ones starting with:
32 * - api (API routes)
33 * - _next/static (static files)
34 * - _next/image (image optimization files)
35 * - favicon.ico (favicon file)
36 */
37 '/((?!api|_next/static|_next/image|favicon.ico|sign-in).*)'
38 ]
39};

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 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 data from Server-side Runtimes.

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 of supported APIs.

With Next.js App Router

Dynamic rendering in React server component

Dynamic rendering is based on a user session extracted from an incoming request.

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 ( <AuthFetchResult description="The API is called on the server side." data={currentUser} /> ); } catch (error) { console.error(error); return <p>Something went wrong...</p>; } }
1import { cookies } from 'next/headers';
2import { getCurrentUser } from '@aws-amplify/auth/server';
3import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils';
4
5// This page always dynamically renders per request
6export const dynamic = 'force-dynamic';
7
8export default async function AuthGetCurrentUserServer() {
9 try {
10 const currentUser = await runWithAmplifyServerContext({
11 nextServerContext: { cookies },
12 operation: (contextSpec) => getCurrentUser(contextSpec)
13 });
14
15 return (
16 <AuthFetchResult
17 description="The API is called on the server side."
18 data={currentUser}
19 />
20 );
21 } catch (error) {
22 console.error(error);
23 return <p>Something went wrong...</p>;
24 }
25}

Static rendering in React server component

Static rendering does not 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).

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 ( <Image src={splashUrl.url.toString()} alt="Splash Image" width={500} height={500} /> ); } catch (error) { console.error(error); return <p>Something went wrong...</p>; } }
1import { getUrl } from 'aws-amplify/storage/server';
2import Image from 'next/image';
3import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils';
4
5// Re-render this page every 60 minutes
6export const revalidate = 60 * 60; // in seconds
7
8export default async function StaticallyRenderedPage() {
9 try {
10 const splashUrl = await runWithAmplifyServerContext({
11 nextServerContext: null,
12 operation: (contextSpec) =>
13 getUrl(contextSpec, {
14 key: 'splash.png'
15 })
16 });
17
18 return (
19 <Image
20 src={splashUrl.url.toString()}
21 alt="Splash Image"
22 width={500}
23 height={500}
24 />
25 );
26 } catch (error) {
27 console.error(error);
28 return <p>Something went wrong...</p>;
29 }
30}

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

In route handlers require implementing an API route that enables GET /apis/get-current-user.

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 }); }
1import { getCurrentUser } from 'aws-amplify/auth/server';
2import { cookies } from 'next/headers';
3import { NextResponse } from 'next/server';
4import { runWithAmplifyServerContext } from '@/utils/amplifyServerUtils';
5
6export async function GET() {
7 const user = await runWithAmplifyServerContext({
8 nextServerContext: { cookies },
9 operation: (contextSpec) => getCurrentUser(contextSpec)
10 });
11
12 return NextResponse.json({ user });
13}

When you call fetch('/apis/get-current-user') it returns a payload that contains the user data for the current 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.

export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { const currentUser = await runWithAmplifyServerContext({ nextServerContext: { request: req, response: res }, operation: (contextSpec) => getCurrentUser(contextSpec) }); return { props: { currentUser } }; };
1export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
2 const currentUser = await runWithAmplifyServerContext({
3 nextServerContext: { request: req, response: res },
4 operation: (contextSpec) => getCurrentUser(contextSpec)
5 });
6
7 return { props: { currentUser } };
8};

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.

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 }; }
1export async function getStaticProps() {
2 const splashUrl = await runWithAmplifyServerContext({
3 nextServerContext: null,
4 operation: (contextSpec) => getUrl(contextSpec, { key: 'splash.png' })
5 });
6
7 return {
8 props: { imageUrl: splashUrl.url.toString() },
9 revalidate: (splashUrl.expiresAt.getTime() - Date.now()) / 1000 // in seconds
10 };
11}

Supported APIs for Next.js server-side usage

All APIs that support use on the server are exported from the aws-amplify/<category>/server sub paths. You must use these APIs for any server-side use cases.

CategoryAPIsServer (Node.js) Amplify Hosting/VercelVercel Edge Runtime (middleware)
AuthfetchAuthSession
AuthfetchUserAttributes
AuthgetCurrentUser
DatagenerateServerClientUsingCookies
DatagenerateServerClientUsingReqRes
StoragegetUrl
StoragegetProperties
Storagelist
Storageremove
Storagecopy