How to Deploy a Next.js 13 Site with AWS Amplify

Christian Nwamba - Dec 5 '22 - - Dev Community

At the Nextjs 2022 Conference, the team announced Nextjs v13 with some amazing features such as improved next/image, React Server Components, Turbopack (Webpack successor), improvement to the Middleware API, and a new app directory. The stable version of Nextjs 13 has most of these features while other features available such as app directory, and Turbopack are currently in beta.

One of the biggest concerns developers have, when new version updates are released, is what the breaking changes are and how they would impact their current development setup. In Nextjs v13, the breaking changes that could disrupt developer environments are:

  • The minimum React version has been bumped from 17.0.2 to 18.2.0.
  • The minimum Node.js version has been bumped from 12.22.0 to 14.6.0 since 12.x has reached end-of-life (PR).

These changes can currently be deployed to AWS Amplify Hosting and you will learn the steps to do so in this article. I should mention that all of Next.js features are supported including server side rendering.

Start with a Next.js App

Let’s get started by bootstrapping a nextjs project. Run this command on your terminal:

yarn create next-app
Enter fullscreen mode Exit fullscreen mode

When this is done, open the project in your code editor.

cd project-name
code . (for vs code users)
Enter fullscreen mode Exit fullscreen mode

Server Side Rendering

Use getServerSideProps when you need to render a page whose data must be fetched at request time. For server-side rendered apps that need to render the page with user data directly on the server.

Let’s fetch user data from an external API and render this page with the data.

const getServersideprops = ({ data }) => {
    return (
        <div className="my-12">
            <h2 className='text-4xl font-extrabold dark:text-gray-900'>Load page with Server side data </h2>
            {data.map(dt => (
                <div className="border-2 shadow-sm border-gray-200 rounded p-3 space-y-2 my-4">
                    <h3 className='my-4 text-2xl text-gray-800'> {dt.name.firstname} {dt.name.lastname}</h3>
                    <p className='my-4 text-lg text-gray-800 font-bold'> {dt.email}</p>
                    <p className='my-4 text-lg text-gray-500 uppercase'> {dt.address.city}</p>
                    <p className='my-4 text-lg text-gray-800'> {dt.phone}</p>
                </div>
            ))}
        </div>
    )
}

export async function getServerSideProps() {
    // Fetch data from external API
    const res = await fetch('https://fakestoreapi.com/users')
    const data = await res.json()
    // Pass data to the page via props
    return { props: { data } }
}

export default getServersideprops
Enter fullscreen mode Exit fullscreen mode

Pass the data you want to render as props to the component. Run your server with yarn dev. You should get something like this.

Static Site Generation

Use getStaticPaths() when you’re using dynamic routes with getStaticProps and need to define a list of paths to be statically generated. This comes in handy when working with a content management system (CMS) or statically pre-rendering pages with data from a database.

getStaticProps statically render a page at build time and passes the data to the component using props.

Let’s render a list of products from our fake API on a page. Update your pages/product/index.js with this:


import Link from "next/link"
const getStaticprops = ({ product }) => {
    return (
        <div className="my-12">
            {product.map(pr => (
                <div className="border-2 shadow-sm border-gray-200 rounded p-3 space-y-4 my-4" key={pr.id}>
                    <p> Title: {pr.title}</p>
                    <p> Description: {pr.description}</p>
                    <p> ${pr.price}</p>
                    <Link href={`products/${pr.id}`} className='font-medium text-blue-600 dark:text-blue-500 hover:underline'>
                        <p> Get here</p>
                    </Link>
                </div>
            ))
            }
        </div>
    )
}
export async function getStaticProps() {
    // Run API calls in parallel
    const productsResponse = await fetch('https://fakestoreapi.com/products')
    const products = await productsResponse.json()
    return {
        props: {
            product: products
        },
    }
}
export default getStaticprops
Enter fullscreen mode Exit fullscreen mode

When you run your server, you will see this:

We need to pre-render a page with data from each of those products by generating paths using getStaticPath().

First, let’s fetch single product data from API using the productId and then pass the data as props to the Component using getStaticProps.


export async function getStaticProps(context) {
    const { params } = context
    // Call an external API endpoint to get posts
    const response = await fetch(`https://fakestoreapi.com/products/${params.productId}`)
    const data = await response.json()
    return {
        props: {
            product: data
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Then we use getStaticPaths to fetch all the product data and assign the id of each product to params.productId.

export async function getStaticPaths() {
    const response = await fetch('https://fakestoreapi.com/products')
    const data = await response.json()
    const paths = data.map(product => {
        return {
            params: {
                productId: `${product.id}`
            }
        }
    })
    return {
        paths,
        fallback: true
    }
}
Enter fullscreen mode Exit fullscreen mode

When you run this, you should be able to view data of a single product item pre-rendered on a page with its dynamically generated path.

Image Optimization

There are improvements to the Nextjs image in this Nextjs v13. Most importantly, these two new improvements:

  • Easier to style and configure
  • More accessible requiring alt tags by default

<Image
    alt="layout Responsive"
    src={pr.image}
    width={700}
    placeholder="blur"
    blurDataURL={blurDataURL}
    height={475}
    sizes="(max-width: 768px) 100vw,(max-width: 1200px) 50vw, 33vw"
    style={{
        width: '100%',
        height: 'auto',
    }}
/>
Enter fullscreen mode Exit fullscreen mode

Here, we have an optimized image with a blur placeholder. It’s also responsive on all screen sizes.

Middleware

Nextjs introduced middlewares in Nextjs v12 so developers can run code before a request is completed and manipulate the incoming request. This is important when running A/B tests, setting authentication guards, or setting request headers. In Nextjs v13, you can now respond to a middleware using NextResponse and set request headers.

Let’s create a middleware to redirect users to /products when they go to the /redirect path.


//middleware.js
import { NextResponse } from 'next/server'
export function middleware(request) {
    if (request.nextUrl.pathname.startsWith('/redirect')) {
        return NextResponse.redirect(new URL('/products', request.url))
    }
}
Enter fullscreen mode Exit fullscreen mode

API Routing

API Routes enable developers to build GraphQL or REST APIs. All files inside pages/api is mapped to /api/* and will be treated as an API endpoint instead of a page.

Let’s update pages/api/hello.js with this

export default function handler(req, res) {
    res.status(200).json({ name: 'Christain Nwamba' })
}
Enter fullscreen mode Exit fullscreen mode

You can consume the endpoint on a page like this:


const ApiRouting = () => {
    const [hello, setHello] = useState('')
    const fetchName = async () => {
        if (global.window) {
            const response = await fetch("/api/hello")
            const data = await response.json()
            setHello(data)
        }
    }
    fetchName()
    return (
        <div>
            <h2 className='text-4xl font-extrabold dark:text-gray-900'> Using API Routes </h2>
            <h3 className='text-3xl font-extrabold dark:text-gray-700'> Hey {hello.name} </h3>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Deploy to Amplify Hosting

Amplify Hosting supports all Next.js features, including the new Next.js 13 features. Let’s walk through the steps to deploy your app.

Ensure you’ve created and pushed your code to GitHub or your preferred Git provider.

Head to AWS Amplify Hosting and click on the Host your web app button. You’ll be redirected to this page.

Select your Git provider (Github, in my case) and click on the Connect branch button. Ensure you install and authorize AWS Amplify to access your repositories.

Click the Next button.

You’ll be redirected to this page to configure build settings.

Click on Next. Review the settings and click on the Save and deploy button.

The deployment will take a few minutes and when it’s done, click on the deployed app link and check if all the features are working correctly.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .