How to Deploy a Next.js 13 Site with AWS Amplify
Published: 2022-12-05 13:39:51

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](https://github.com/vercel/next.js/pull/41482)).

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:
```bash
yarn create next-app
```
When this is done, open the project in your code editor. 
```bash
cd project-name
code . (for vs code users)
```
 
## 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.

```jsx
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
```

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


![](https://paper-attachments.dropboxusercontent.com/s_C348D939E6A4675C774669788E74848D253B658E5A5202CCDD1897F0CBCF7A6B_1669799137769_screencapture-localhost-3000-getServersideprops-2022-11-30-10_03_50.png)

## 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:
```jsx

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

When you run your server, you will see this:

![](https://paper-attachments.dropboxusercontent.com/s_C348D939E6A4675C774669788E74848D253B658E5A5202CCDD1897F0CBCF7A6B_1669800093905_Screenshot+2022-11-30+at+10.21.23.png)


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`.
```javascript

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
        }
    }
}
```
Then we use `getStaticPaths` to fetch all the product data and assign the id of each product to `params.productId`.

```js
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
    }
}
```
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
```jsx

<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',
    }}
/>
```
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.
```js

//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))
    }
}
```

## 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
```js
export default function handler(req, res) {
    res.status(200).json({ name: 'Christain Nwamba' })
}
```
You can consume the endpoint on a `page` like this:
```jsx

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


## 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](https://aws.amazon.com/amplify/hosting/) and click on the **Host your web app** button. You’ll be redirected to this page.

![](https://paper-attachments.dropboxusercontent.com/s_C348D939E6A4675C774669788E74848D253B658E5A5202CCDD1897F0CBCF7A6B_1669803115361_Screenshot+2022-11-30+at+11.11.34.png)


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.


![](https://paper-attachments.dropboxusercontent.com/s_C348D939E6A4675C774669788E74848D253B658E5A5202CCDD1897F0CBCF7A6B_1669803259035_Screenshot+2022-11-30+at+11.13.55.png)


Click the Next button. 

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


![](https://paper-attachments.dropboxusercontent.com/s_C348D939E6A4675C774669788E74848D253B658E5A5202CCDD1897F0CBCF7A6B_1669803702880_screencapture-us-east-1-console-aws-amazon-amplify-home-2022-11-30-11_20_08.png)


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.