📘

Amplify | Getting Started with Next.js

2023/08/06に公開

Amplify Summary

  • Amplify CLI
    • コマンドラインでバックエンドの設定が行える。
  • Amplify Libraries
    • 認証、データ、ファイルストレージなどの一般的なユースケースを構築するためのクライアントライブラリ。
  • Amplify UI Components
    • フロントエンドアプリを構築するためのUIライブラリ。
  • Amplify Hosting
    • CI/CDできるホスティングサービス。

Tutorial with Next.js

amplify configure
  • CLIからIAMでログインする。
npx create-next-app@latest next-amplified --no-app
cd next-amplified

amplifyでバックエンドサービスを作成

amplify init
  • ここで行われること(ざっくり)
    • amplifyのバックエンド定義をするディレクトリが作成される。
      -amplifyで作成した構成を保持するaws-exports.jsが作られる。これによりクライアントはバックエンドサービスに関する必要な情報を取得できる。
    • .gitignoreでいくつかのファイルをignoreリストに入れてくれる
    • AWS Amplifyのコンソールにプロジェクトが作成される。

amplifyライブラリのインストール

npm install aws-amplify @aws-amplify/ui-react

GraphQl API とデータベースを作成する

amplify add api
  • amplify/backend/api/nextamplified/schema.graphqlこのファイルにスキーマを定義する
  • apiのデプロイ
amplify push
  • APIのテスト
ampify mock api

SSRを使用したAPI

  • _apps.jsで設定
import '@aws-amplify/ui-react/styles.css';
  • pages/index.js
// pages/index.js
import { Authenticator } from '@aws-amplify/ui-react';
import { Amplify, API, Auth, withSSRContext } from 'aws-amplify';
import Head from 'next/head';
import awsExports from '@/aws-exports';
import { createPost } from '@/graphql/mutations';
import { listPosts } from '@/graphql/queries';
import styles from '../styles/Home.module.css';

Amplify.configure({ ...awsExports, ssr: true });

export async function getServerSideProps({ req }) {
  const SSR = withSSRContext({ req });
  
  try {
    const response = await SSR.API.graphql({ query: listPosts, authMode: 'API_KEY' });
    return {
      props: {
        posts: response.data.listPosts.items,
      },
    };
  } catch (err) {
    console.log(err);
    return {
      props: {},
    };
  }
}

async function handleCreatePost(event) {
  event.preventDefault();

  const form = new FormData(event.target);

  try {
    const { data } = await API.graphql({
      authMode: 'AMAZON_COGNITO_USER_POOLS',
      query: createPost,
      variables: {
        input: {
          title: form.get('title'),
          content: form.get('content')
        }
      }
    });

    window.location.href = `/posts/${data.createPost.id}`;
  } catch ({ errors }) {
    console.error(...errors);
    throw new Error(errors[0].message);
  }
}

export default function Home({ posts = [] }) {
  return (
    <div className={styles.container}>
      <Head>
        <title>Amplify + Next.js</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>Amplify + Next.js</h1>

        <p className={styles.description}>
          <code className={styles.code}>{posts.length}</code>
          posts
        </p>

        <div className={styles.grid}>
          {posts.map((post) => (
            <a className={styles.card} href={`/posts/${post.id}`} key={post.id}>
              <h3>{post.title}</h3>
              <p>{post.content}</p>
            </a>
          ))}

          <div className={styles.card}>
            <h3 className={styles.title}>New Post</h3>

            <Authenticator>
              <form onSubmit={handleCreatePost}>
                <fieldset>
                  <legend>Title</legend>
                  <input
                    defaultValue={`Today, ${new Date().toLocaleTimeString()}`}
                    name="title"
                  />
                </fieldset>

                <fieldset>
                  <legend>Content</legend>
                  <textarea
                    defaultValue="I built an Amplify project with Next.js!"
                    name="content"
                  />
                </fieldset>

                <button>Create Post</button>
                <button type="button" onClick={() => Auth.signOut()}>
                  Sign out
                </button>
              </form>
            </Authenticator>
          </div>
        </div>
      </main>
    </div>
  );
}

重要なポイント

  • Amplify.configure
    • 認証されたリクエストがサーバー上で機能するには資格情報を使用できるようにクライアントを設定する必要がある。

SSGを使用したAPI

import { Amplify, API, withSSRContext } from 'aws-amplify';
import Head from 'next/head';
import { useRouter } from 'next/router';
import awsExports from '@/aws-exports';
import { deletePost } from '@/graphql/mutations';
import { getPost } from '@/graphql/queries';
import styles from '../../styles/Home.module.css';

Amplify.configure({ ...awsExports, ssr: true });

export async function getServerSideProps({ req, params }) {
  const SSR = withSSRContext({ req });
  const { data } = await SSR.API.graphql({
    query: getPost,
    variables: {
      id: params.id
    }
  });
  return { 
    props: {
      post: data.getPost
    }
  };
}

export default function Post({ post }) {
  const router = useRouter();

  if (router.isFallback) {
    return (
      <div className={styles.container}>
        <h1 className={styles.title}>Loading&hellip;</h1>
      </div>
    );
  }

  async function handleDelete() {
    try {
      await API.graphql({
        authMode: 'AMAZON_COGNITO_USER_POOLS',
        query: deletePost,
        variables: {
          input: { id: post.id }
        }
      });

      window.location.href = '/';
    } catch ({ errors }) {
      console.error(...errors);
      throw new Error(errors[0].message);
    }
  }

  return (
    <div className={styles.container}>
      <Head>
        <title>{post.title} – Amplify + Next.js</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>{post.title}</h1>

        <p className={styles.description}>{post.content}</p>
      </main>

      <footer className={styles.footer}>
        <button onClick={handleDelete}>💥 Delete post</button>
      </footer>
    </div>
  );
}

ホスティング

  • next.jsのSSRアプリはamplifyでCI/CDおよびホスティングサービスが使用できる
amplify add hosting

終わり

Discussion