前にReactで作った掲示板をNextにしてみる。SSRをまず使ってみる。
nextでプロジェクトを作成
npx create-next-app
amplify init
? Enter a name for the project nextamplify1
The following configuration will be applied:
Project information
| Name: nextamplify1
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: react
| Source Directory Path: src
| Distribution Directory Path: build
| Build Command: npm.cmd run-script build
| Start Command: npm.cmd run-script start
? Initialize the project with the above configuration? No
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path: src
? Distribution Directory Path: .next <<===== ここ
? Build Command: npm.cmd run-script build
? Start Command: npm.cmd run-script start
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile
今のcliのバージョンなら認識するかもしれないが、自分のときは Distribution Directory Path がbuildになったので、.nextに変更する。上記の部分。
Githubとの連帯とデプロイ
githubにレポジトリを作って、ブラウザでamplify consoleにアクセスして、Githubと連帯する。
このとき、設定を追加する項目で、nextのバージョン : latestを追加する。
参考 : AWS AmplifyでNext.jsのISRを試してみる
https://dev.classmethod.jp/articles/amplify-next-js-isr/
ここまででnextをAmplifyでデプロイすることができた。
Amplify APIを追加
amplify add apiでAPIを追加。
amplify add api
amplify push
amplify push
ライブラリをインストール
npm install aws-amplify @aws-amplify/ui-react
_app.jsに以下を追加
import Amplify from 'aws-amplify';
import config from '../src/aws-exports';
Amplify.configure({
...config, ssr: true
});
GraphQLスキーマを変更
GraphQLスキーマを変更してamplify pushする。前の記事参照
Nextコードを書いていく
indexページを移植
import { useState } from "react";
import { API, graphqlOperation, withSSRContext } from "aws-amplify";
import { byCreatedAt } from "../src/graphql/queries.js";
import { createComment, createThread } from "../src/graphql/mutations.js";
import Link from "next/link";
// SSR時にスレッド一覧を取得
export async function getServerSideProps(req) {
const { API } = withSSRContext(req);
const { data } = await API.graphql(
graphqlOperation(byCreatedAt, {
type: "t",
sortDirection: "DESC",
})
);
console.log(data);
return {
props: {
threadList: data.byCreatedAt.items,
},
};
}
function Index(props) {
const [threadList, setThreadList] = useState(props.threadList);
const formFirstValue = { title: "", firstComment: "" };
const [formState, setFormState] = useState(formFirstValue);
// フォームをクリック時の動作
const onSubmitForm = async (e) => {
e.preventDefault();
// フォームが空なら終了
if (!formState.title || !formState.firstComment) return;
// スレッドとコメントの作成
const newThread = await createNewThread();
await createFirstComment(newThread.id);
setFormState(formFirstValue);
// 画面に反映
setThreadList([newThread, ...threadList]);
};
// スレッドの作成
const createNewThread = async () => {
const newThread = { title: formState.title, type: "t" };
const { data } = await API.graphql(
graphqlOperation(createThread, { input: newThread })
);
console.log(data);
return data.createThread;
};
// 最初のコメントを作成
const createFirstComment = async (threadId) => {
const newComment = { threadId: threadId, title: formState.firstComment };
const { data } = await API.graphql(
graphqlOperation(createComment, { input: newComment })
);
console.log(data);
};
// フォームに入力時、formStateを更新する
const onChange = (event, propaty) => {
setFormState({ ...formState, [propaty]: event.target.value });
};
return (
<div>
<div>掲示板(Amplify + Next)</div>
<div>
{threadList.map((thread, index) => {
return (
<div key={index}>
<Link href={"/" + thread.id}>
<a>{thread.title}</a>
</Link>
</div>
);
})}
</div>
<div>
<form>
<div>
<div>
<label htmlFor="threadTitle">スレッドタイトル</label>
</div>
<input
id="threadTitle"
value={formState.title}
onChange={(event) => onChange(event, "title")}
></input>
</div>
<div>
<div>
<label htmlFor="threadFirstComment">最初のコメント</label>
</div>
<input
id="threadFirstComment"
value={formState.firstComment}
onChange={(event) => onChange(event, "firstComment")}
></input>
</div>
<button onClick={onSubmitForm}>送信</button>
</form>
</div>
</div>
);
}
export default Index;
threadページ
import { API, graphqlOperation, withSSRContext } from "aws-amplify";
import { useEffect, useState, useRef } from "react";
import { createComment } from "../src/graphql/mutations";
import { getThread } from "../src/graphql/queries";
import { onCommentByThreadId } from "../src/graphql/subscriptions";
import { useRouter } from "next/router";
// SSRでスレッド情報を取得
export async function getServerSideProps(req) {
const { API } = withSSRContext(req);
const { data } = await API.graphql(
graphqlOperation(getThread, { id: req.params.threadId })
);
console.log(data);
return {
props: {
thread: data.getThread,
},
};
}
export default function Thread(props) {
const router = useRouter();
const { threadId } = router.query;
const [thread, setThread] = useState(props.thread);
const [commentForm, setCommentForm] = useState({ title: "" });
const subscriptionRef = useRef();
// サブスクを設定する
useEffect(() => {
attachSubscription();
return () => {
if (subscriptionRef) subscriptionRef.current.unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// subscriptionを設定
const attachSubscription = () => {
subscriptionRef.current = API.graphql(
graphqlOperation(onCommentByThreadId, { threadId: threadId })
).subscribe({
next: (data) => {
console.log(data);
addComment(data.value.data.onCommentByThreadId);
},
error: (error) => console.warn(error),
});
};
// コメントのリストに新たに追加
const addComment = (newComment) => {
setThread((thread) => {
// リスト内に既にあったら追加しない
const idx = thread.comments.items.findIndex(
(comment) => comment.id === newComment.id
);
if (idx !== -1) return thread;
console.log("update comment list", thread);
return {
...thread,
comments: { items: [...thread.comments.items, newComment] },
};
});
};
// フォーム送信時
const onSubmit = async (e) => {
e.preventDefault();
if (!commentForm.title) return;
const newComment = await createNewComment();
setCommentForm({ title: "" });
// thread情報の更新
setThread((thread) => {
return {
...thread,
comments: { items: [...thread.comments.items, newComment] },
};
});
};
// コメントの作成
const createNewComment = async () => {
const { data } = await API.graphql(
graphqlOperation(createComment, {
input: { threadId: threadId, title: commentForm.title },
})
);
console.log(data);
return data.createComment;
};
return (
<div>
<h1>{thread.title}</h1>
{thread.comments.items.map((comment, idx) => {
return <div key={idx}>{comment.title}</div>;
})}
<div>
<form>
<div>
<label htmlFor="commentForm">コメント</label>
</div>
<input
id="commentForm"
value={commentForm.title}
onChange={(event) => setCommentForm({ title: event.target.value })}
></input>
<button onClick={onSubmit}>送信</button>
</form>
</div>
</div>
);
}
動いた。ヤッター。
サイト公開アドレス :
https://main.d1xvmlla5wn67x.amplifyapp.com/
公式ドキュメント
AWS AmplifyJavaScriptライブラリのSSRサポート
https://aws.amazon.com/jp/blogs/mobile/ssr-support-for-aws-amplify-javascript-libraries/
AWSAmplifyでリアルタイムデータを使用してNext.jsSSRアプリをホストします
https://aws.amazon.com/jp/blogs/mobile/host-a-next-js-ssr-app-with-real-time-data-on-aws-amplify/
HOSTING Next.js
https://docs.amplify.aws/guides/hosting/nextjs/q/platform/js#adding-amplify-hosting-1