Deploy a Next.js app on AWS Amplify

Deploy a Next.js app on AWS Amplify

A Practical Guide

AWS Amplify announced server-side rendering support here, which means we can fully utilize Next.js server-side rendering.

In this article you would build a user store app that shows you a list of users, you'll utilize Next.js server-side rendering to get up-to-date data, create an API route, consume that endpoint, and deploy it on AWS Amplify.

AWS Amplify supports all the Next JS features so this is a great hosting choice for us.

The code for this project can be gotten from https://github.com/inaju/userstore

If you're only interested in deploying your Next.js app on Amplify kindly fork and clone the repo and head to the Deploy to AWS Amplify section

Why AWS Amplify?

AWS Amplify is a service that helps us build and deploy applications very quickly without concerning ourselves with a more complex infrastructure setup. Amplify lets you add things like Storage, Authentication, Monitoring, and PubSub functionalities easily while letting you focus on getting your product live.

Amplify supports Next.js, and you'll get to enjoy:

  1. Faster builds. You should expect 3x build speed when deploying.

  2. Amazon CloudWatch integration. Logs are sent to CloudWatch to enable you to monitor and observe your application.

  3. Fully managed hosting infrastructure reduces operational overhead for development teams, with fewer resources to manage in your AWS account. To learn more about pricing visit https://aws.amazon.com/amplify/pricing/.

Prerequisites

  • Create an AWS account if you haven't.

  • Next.js 13 is installed on your machine.

  • Vs Code Installed (Optional)

Create Next JS app

Let's start by creating a next JS app

If you use npm please copy this into your terminal

npx create-next-app@latest userstore --typescript --eslint

or yarn

yarn create next-app@latest userstore --typescript --eslint

you should be prompted with some questions on the CLI, something like this

When your package managers are done installing open the project in vs code using these commands

cd userstore

. code

We need dummy user data that would give us basic details about a user like first name, last name, age, location, profile image etc, for this project we'll be using dummy user data from https://randomuser.me/

Create a usercard component

This component would be in charge of displaying single-user data

Create a component folder with a usercard.tsx file

// usercard.tsx
import { Result } from '@/pages'
import Image from 'next/image'
import React from 'react'

const UserCard = (props: Result) => {
    return (
        <div
            key={props.id.value}
            className='bg-black/[0.05] hover:bg-black/[0.09] hover:shadow-2xl hover:cursor-pointer p-4 rounded-lg shadow-xl  w-full lg:w-[620px]'>
            <div className='flex flex-col  lg:flex-row justify-between items-start md:items-center'>
                <div className='flex gap-2'>

                    <Image
                        src={props.picture.medium}
                        priority
                        className=' rounded-full border-teal-400 border-2'
                        width={50} height={50} alt={`image for ${props.name.first}`} />
                    <h3 className='text-[2rem] text-neutral-700 mr-3'>
                        <>
                            {props.name.first} {props.name.last}
                        </>
                    </h3>
                </div>

                <p className='mt-2 md:mt-0 bg-white w-fit h-fit px-4  rounded-full text-green-500'>&#x2022; Available</p>
            </div>
            <div className='mt-4  border-red-300'>


                <div className='flex gap-8  text-neutral-500'>

                    <p className=''><b>

                        Phone:&ensp;
                    </b>
                        {props.cell}
                    </p>
                    <p> <b>
                        Age: &ensp;
                    </b>
                        {props.dob.age}
                    </p>
                </div>
                <p className='mt-1 text-neutral-500'><b>Location:</b>  {props.location.city},  {props.location.country}</p>
                <p className='mt-1 text-neutral-500'><b>Gender:</b>   {props.gender}</p>
            </div>
            <div className='bg-teal-400 p-2 px-4 rounded-lg w-fit mt-4 hover:cursor-pointer hover:bg-teal-300 text-white '>Hire User</div>
        </div>
    )
}

export default UserCard

It should look something like this on your browser

Create a types folder and an index.tsx file and paste this into the file

export interface IData {

  data:
  {
    results:
    Result[]
  }
  info: Info
}

export interface Result {
  gender: string
  name: Name
  location: Location
  email: string
  login: Login
  dob: Dob
  registered: Registered
  phone: string
  cell: string
  id: Id
  picture: Picture
  nat: string
}

export interface Name {
  title: string
  first: string
  last: string
}

export interface Location {
  street: Street
  city: string
  state: string
  country: string
  postcode: number
  coordinates: Coordinates
  timezone: Timezone
}

export interface Street {
  number: number
  name: string
}

export interface Coordinates {
  latitude: string
  longitude: string
}

export interface Timezone {
  offset: string
  description: string
}

export interface Login {
  uuid: string
  username: string
  password: string
  salt: string
  md5: string
  sha1: string
  sha256: string
}

export interface Dob {
  date: string
  age: number
}

export interface Registered {
  date: string
  age: number
}

export interface Id {
  name: string
  value: string
}

export interface Picture {
  large: string
  medium: string
  thumbnail: string
}

export interface Info {
  seed: string
  results: number
  page: number
  version: string
}

your global.css file should look like this

@tailwind base;
@tailwind components;
@tailwind utilities;

Configure your next.config.js file to allow images from randomuser.me, the file should look like this

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'randomuser.me',
      },
    ],
  },
}

module.exports = nextConfig

Serverside Rendering

What is Server Side Rendering in Next.js?

This is a rendering method in Next.js as a way of pre-rendering a page from the server on request ie: A server-rendered page will show the latest data. This is useful for improving SEO performance.

To enable this we need to export an async function called getServerSideProps. This function would be called and return some data by the server on every request.

On your index.tsx file make these changes.

// index.tsx
import { Inter } from 'next/font/google'
import UserCard from '@/components/usercard'
import { IData, Result } from '@/types'

const inter = Inter({ subsets: ['latin'] })

export default function Home({ data }: IData) {

  return (
    <main
      className={` flex flex-row flex-wrap mx-auto  max-w-[1324px] p-4 gap-y-8 gap-x-8  h-full justify-between ${inter.className}`}
    >
      {data.map((item: Result, index: number) => (
        <div key={index}>
          <UserCard {...item} />
        </div>
      )
      )}
    </main>
  )
}


// This gets called on every request

export async function getServerSideProps() {
  // Fetch data from external API

  const res = await fetch(`https://randomuser.me/api/?results=10`)
  let data = await res.json()
  data = data.results
  // Pass data to the page via props
  return { props: { data } }
}

Run your server with the yarn dev command, you should see this on your browser

Setup API Routes

Next.js enables developers to build REST APIs or GRAPHQL. The api folder is located in the pages directory and all folders and files would be mapped to an api endpoint instead of a page ie pages/api/getuser.ts would be mapped to /api/randomuser

Let us create a randomuser.ts file inside the pages/api directory, which should look like this

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const response = await fetch(`https://randomuser.me/api/`);
  let data = await response.json();

  res.status(200).json({ data });
}

This file returns a random user from the external API, so when you call the endpoint /api/randomuser you would get a random user details.

You can test this api, by navigating to localhost:3000/api/randomuser on your browser, you should have this json response on your browser

Let's get a page to consume this endpoint

Create a singleusers folder with an index.tsx file inside the pages folder, so you should have a file that looks like this

import React, { useMemo, useEffect, useState } from 'react'

const SingleUser = () => {
    const [resData, setResData] = useState<any>()

    const fetchName = useMemo(async () => {
        if (global.window) {
            const response = await fetch("/api/randomuser")
            const data = await response.json()
            if (data.error) {
                setResData({ resData: "Please try again later" })
            }
            setResData(data)
        }
    }, [])

    useEffect(() => {
        fetchName
    }, [fetchName])

    return (
        <div>
            <p>
                hey there  {resData?.data.results[0].name.first}   {resData?.data.results[0].name.last}
            </p>
            <p>
                you are   {resData?.data.results[0].dob.age} years old
            </p>
        </div>
    )
}

export default SingleUser

This file calls the endpoint you just created and displays it, the useMemo hook was used so that the function doesn't get called multiple times and it's inside a useEffect hook so that it calls the endpoint only once on page load.

Navigate to http://localhost:3000/singleuser on your browser, you should see this

Deploy to AWS Amplify

Amplify Hosting supports all Next.js features. Let’s go over the steps to for deployment.

Step 1: Push to git

To push to git run the following commands in your terminal

git add .

git commit -m "worked on the basic features"

git push origin main

Step 2: Create Amplify App

Navigate to the Amplify Console to create your amplify app, click on the get started button.

It would take you to another part of the same page, click on Get started

Step 3: Select your git provider

For my case, I used GitHub, so I'll select GitHub

You'll be prompted to allow AWS Amplify Console have permission to your github, click on the green button.

Then click on Continue on your aws console

Step 4: Select your repository and branch

Select your repository and ensure that the right branch is selected, since we created ours on the main branch, we can leave it on the main branch.

Step 5: Build Settings page

The next page is the build settings page, Amplify automatically detects the optimal settings so there's no need to change the file, so leave it that way and click next

Step 5: Deploy your app

On the review page, we can see that it automatically detects that we have SSR functionality, click Save and Deploy

Your app has been created and Amplilify Hosting will provision an isolated build and hosting environment for your project and deploy it.

When the deployment has been completed, click on the provision, build or deploy to get to the build summary page, then click on the Domain Link.

Conclusion.

You've been introduced to Next.js server-side rendering, Next.js API routes, and how to handle deployment on AWS Amplify.

If you found this article helpful please like or leave a comment, share it with your friends/network who might need this, and connect with me on Linkedin, Twitter, and Dev.to.