1. 概要
前回はAmplify(Gen2)やAppSyncを使い、DynamoDBを操作する内容でした。今回は前回作成したAppSync APIにアクセスする内容となります。
対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。
2. nodeのインストール
こちらを参考
3. プロジェクトを作成
3-1-1. こちらを参考
4. 必要なライブラリをインストール
npm install aws-amplify
5. アプリと統合する
※前回作成したAPI画面の内容に従い、セットアップする

5-1. クライアントを接続
5-1-1. API設定のスニペットを使用して Amplify をセットアップ
import { Amplify } from 'aws-amplify';
Amplify.configure({
API: {
GraphQL: {
endpoint: 'https://12345678901234567890123456.appsync-api.ap-northeast-1.amazonaws.com/graphql',
region: 'ap-northeast-1',
defaultAuthMode: 'apiKey',
apiKey: 'ab2-abcdefghijklmnopqrstuvwxyz'
}
}
});
5-1-2. codegenを生成
npx @aws-amplify/cli codegen add --apiId 12345678901234567890123456 --region ap-northeast-1
※デフォルトでクライアントヘルパーコードが「src/graphql」フォルダに生成される。
※APIをデプロイするたびに、次のコマンドを再実行して、更新されたGraphQLステートメントとタイプを生成
npx @aws-amplify/cli codegen
6. ソースコード
6-1. 上記コマンド実行より自動生成
- src
- API.ts
- graphql
- mutations.ts
- queries.ts
- subscriptions.ts
6-2. クライアント側のソースコードを作成
6-2-1. src/app/appsync-client.ts
import { Amplify } from "aws-amplify";
import { generateClient } from "aws-amplify/api";
Amplify.configure({
API: {
GraphQL: {
endpoint: 'https://12345678901234567890123456.appsync-api.ap-northeast-1.amazonaws.com/graphql',
region: 'ap-northeast-1',
defaultAuthMode: 'apiKey',
apiKey: 'ab2-abcdefghijklmnopqrstuvwxyz'
},
},
});
const appsyncClient = generateClient();
export { appsyncClient };
6-2-2. src/app/app.css
body {
margin: 0;
background: linear-gradient(180deg, rgb(117, 81, 194), rgb(255, 255, 255));
display: flex;
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
height: 100vh;
width: 100vw;
justify-content: center;
align-items: center;
}
main {
display: flex;
flex-direction: column;
align-items: stretch;
}
input {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
margin-right: 8px;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
color: white;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
ul {
padding-inline-start: 0;
margin-block-start: 0;
margin-block-end: 0;
list-style-type: none;
display: flex;
flex-direction: column;
margin: 8px 0;
border: 1px solid black;
gap: 1px;
background-color: black;
border-radius: 8px;
overflow: auto;
}
li {
background-color: white;
padding: 8px;
}
li:hover {
background: #dadbf9;
}
a {
font-weight: 800;
text-decoration: none;
}
6-2-3. src/app/page.tsx
"use client";
import {
ChangeEvent,
MouseEvent,
KeyboardEvent,
useState,
useEffect,
} from "react";
import { appsyncClient } from "./appsync-client";
import { listPosts, getPost } from "../graphql/queries";
import { createPost, updatePost, deletePost } from "@/graphql/mutations";
import { onSubscribePost } from "@/graphql/subscriptions";
import { Post } from "@/API";
import "@/app/app.css";
export default function Home() {
const [content, setContent] = useState<string>("");
const [posts, setPosts] = useState<Post[]>();
const getListPosts = async (limit: number) => {
const { data, errors } = await appsyncClient.graphql({
query: listPosts,
variables: {
limit,
},
});
if (errors) console.error(`[ERROR]:${JSON.stringify(errors)}`);
if (data) {
const postData = data.listPosts?.posts as Array<Post>;
postData.map((post) => {
setPosts((prev) => [...(prev ?? []), post]);
});
}
};
useEffect(() => {
getListPosts(10);
}, []);
const getPostData = async (id: string, e: MouseEvent<HTMLLIElement>) => {
e.preventDefault();
const { data, errors } = await appsyncClient.graphql({
query: getPost,
variables: {
id,
},
});
if (errors) console.error(`[ERROR]:${JSON.stringify(errors)}`);
};
const applyPost = async () => {
if (content.length == 0) return;
const { data, errors } = await appsyncClient.graphql({
query: createPost,
variables: {
title: "My Post",
content,
author: "Sondon",
},
});
if (errors) console.error(`[ERROR]:${JSON.stringify(errors)}`);
setContent("");
};
const keyDownHandlerPost = async (e: KeyboardEvent<HTMLInputElement>) => {
if (e.nativeEvent.isComposing || e.key !== "Enter") return;
await applyPost();
};
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
setContent(e.currentTarget.value);
};
const createPostData = async (e: MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
await applyPost();
};
const updatePostData = async (
id: string,
author: string,
expectedVersion: number,
content: string,
e: MouseEvent<HTMLButtonElement>
) => {
e.preventDefault();
const { data, errors } = await appsyncClient.graphql({
query: updatePost,
variables: {
id,
author,
content,
expectedVersion,
},
});
if (errors) console.error(`[ERROR]:${JSON.stringify(errors)}`);
};
const deletePostData = async (
id: string,
e: MouseEvent<HTMLButtonElement>
) => {
e.preventDefault();
const { data, errors } = await appsyncClient.graphql({
query: deletePost,
variables: {
id,
},
});
if (errors) console.error(`[ERROR]:${JSON.stringify(errors)}`);
};
useEffect(() => {
const sub = appsyncClient.graphql({ query: onSubscribePost }).subscribe({
next: ({ data }) => {
if (!data.onSubscribePost) return;
const { mutationType, post } = data.onSubscribePost;
if (post) {
setPosts((prev) => {
switch (mutationType) {
case "CREATE":
return [...(prev ?? []), post];
case "UPDATE":
return prev?.map((p) => (p.id === post.id ? post : p));
case "DELETE":
return prev?.filter((p) => p.id !== post.id);
}
});
}
},
error: (error) => {
console.error(`[ERROR] ${JSON.stringify(error)}`);
},
});
return () => sub.unsubscribe();
}, []);
return (
<div>
<h2>My posts</h2>
<input
type="text"
value={content}
onChange={(e) => handleChange(e)}
onKeyDown={keyDownHandlerPost}
autoFocus={true}
/>
<button onClick={(e) => createPostData(e)}>Register</button>
<ul>
{posts &&
posts.map((post) => (
<li key={post.id} onClick={(e) => getPostData(post.id, e)}>
<div
style={{
display: "flex",
flexWrap: "wrap",
overflow: "hidden",
justifyContent: "space-between",
alignItems: "center",
}}
>
<div>{post.content}</div>
<div>
<button onClick={(e) => deletePostData(post.id, e)}>
Delete
</button>
</div>
</div>
</li>
))}
</ul>
</div>
);
}
7. サーバーを起動
7-1-1. NextJSを立ち上げる
npm run dev
8. ブラウザで確認
- http://localhost:3000
8-1-1. 画面

9. ディレクトリの構造
.
├── README.md
├── eslint.config.mjs
├── next-env.d.ts
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── schema.json
├── src
│ ├── API.ts
│ ├── app
│ │ ├── app.css
│ │ ├── appsync-client.ts
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ └── graphql
│ ├── mutations.ts
│ ├── queries.ts
│ └── subscriptions.ts
└── tsconfig.json
4 directories, 24 files
10. 備考
今回は前回作成したAppSync APIにアクセスする内容についてでした。
11. 参考
- Docs | Next.js (nextjs.org)
- Quick Start – React
- Getting Started with Redux | Redux
- Getting Started with React Redux | React Redux (react-redux.js.org)
- Material UI: React components based on Material Design (mui.com)
- Set up Amplify GraphQL API – Next.js – AWS Amplify Gen 1 Documentation
投稿者プロフィール

-
開発好きなシステムエンジニアです。
卓球にハマってます。