RX100で撮り歩くブログ

RX100で撮った写真を中心によりよい生活を目指すブログ

Amplify React Typescriptで認証+Todoっぽいやつ

やってみたメモです。

nodeのバージョンはv12.13.1でやっています。

Create React App

typesctiptでcreate react app

create-react-app noteapp --typescript

create react appの初期画面が表示されるか確認

cd noteapp

yarn start

Amplify 初期設定

まずはAmplify CLIをインストール

npm install -g @aws-amplify/cli

バージョン確認(v4.2.0でやってます)

amplify -v

その後の細かい設定はこちらの記事の手順を参考に

qiita.com

Amplify CLIでAWS環境構築

まずは以下のコマンド

amplify init

いろいろ聞かれる。今回は以下のように設定。

Enter a name for the project (noteapp)
Enter a name for the environment (dev)
Choose your default editor: (Visual Studio Code)
hoose the type of app that you're building (javascript)
What javascript framework are you using (react)
Source Directory Path:  (src)
Distribution Directory Path: (build)
Build Command:  (npm run-script build)
Start Command: (npm run-script start)
Do you want to use an AWS profile? (Yes)
Please choose the profile you want to use (default)

環境構築が終わると。amplifyディレクトリやsrc以下にaws-exports.jsができている。

GraphQLのAPI追加

以下のコマンドでAPIを追加

amplify add api

またいろいろ聞かれる

Please select from one of the below mentioned services: GraphQL
Provide API name: noteapp
Choose the default authorization type for the API API key
Enter a description for the API key: noteapp api
After how many days from now the API key should expire (1-365): 7
Do you want to configure advanced settings for the GraphQL API No, I am done.
Do you have an annotated GraphQL schema? No
Do you want a guided schema creation? No
? Provide a custom type name Note

終わると「amplify/backend/api/schema.graphql」ができている。

type Note @model {
    id: ID!
    title: String!
    content: String!
    price: Int
    rating: Float
}

これは後から変更可能

blog.mismith.me

以下で反映させる

amplify push

変更の状態が表示される。Operetionが変更点。CreateやUpdate、No Changeなど。

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Api      | noteapp       | Create    | awscloudformation |

またいろいろ聞かれる

Are you sure you want to continue? Yes
Do you want to generate code for your newly created GraphQL API Yes
Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.ts
Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
Enter maximum statement depth [increase from default if your schema is deeply nested] 2
Enter the file name for the generated code src/API.ts

AWS コンソール画面のサービスから「AWS AppSync」へ行くと、APIができている。

App Syncのメニューの「クエリ」を開き、左側の入力エリアに以下をコピペし、▷(実行)をクリックすると実際にデータが追加される。

mutation create {
    createNote(input:{
    title:"ノート1"
    content:"今日は晴れでした"
  }){
    id title content
  }
}

AWSメニューのDynamoDBを開くとテーブルができている。該当のテーブルを選択し、項目タブを見ると上記のデータがある。

再びApp Syncに戻り、次は以下を実行するとNoteの一覧が取得できる。先ほど登録した一件が表示されるはず。他にも登録していれば複数取得できる。

query list {
  listNotes{
    items{
      id title content
    }
  }
}

ここまでできればあとはフロントで追加や取得のリクエストを送るだけ

その前にデフォルトでできたschemeのpriceとratingはいらないので消してみる。amplify/backend/api/schema.graphqlを以下に修正。

type Note @model {
    id: ID!
    title: String!
    content: String!
}

反映させる

amplify push

Updateになってるか確認。

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Api      | noteapp       | Update    | awscloudformation |

続ける

Are you sure you want to continue? Yes
Do you want to update code for your updated GraphQL API Yes
This will overwrite your current graphql queries, mutations and subscriptions Yes

完了するとAPI.tsとかにもちゃんと反映されている。

React

まずは以下でAmplify関連のライブラリをインストール

yarn add aws-amplify aws-amplify-react

index.tsxを書き換える

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import Amplify from "aws-amplify";  // 追加
import config from "./aws-exports";  // 追加
Amplify.configure(config);  // 追加

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

App.tsを以下のように書き換える。ほぼ書き換えるので長いけどまとめて貼る

import * as React from "react";
import "./App.css";
import { API, graphqlOperation } from "aws-amplify";
import { createNote } from "./graphql/mutations";
import { onCreateNote } from "./graphql/subscriptions";
import {
  ListNotesQuery,
  CreateNoteMutationVariables,
  OnCreateNoteSubscription
} from "./API";

type Note = {
  id: string;
  title: string;
  content: string | null;
};

type FormState = {
  title: string;
  content: string;
};

type NoteSubscriptionEvent = { value: { data: OnCreateNoteSubscription } };

const ListNotes = `
  query list {
    listNotes {
      items{
        id title content
      }
    }
  }
`;

const App: React.FC = () => {
  const [notes, setNotes] = React.useState<Note[]>([]);

  const [input, setInput] = React.useState<FormState>({
    title: "",
    content: ""
  });

  const onFormChange = ({
    target: { name, value }
  }: React.ChangeEvent<HTMLInputElement>) => {
    setInput(prev => ({ ...prev, [name]: value }));
  };

  const onPost = () => {
    if (input.title === "" || input.content === "") return;
    const newPost: CreateNoteMutationVariables = {
      input: {
        title: input.title,
        content: input.content
      }
    };
    setInput({ title: "", content: "" });
    API.graphql(graphqlOperation(createNote, newPost));
  };

  React.useEffect(() => {
    (async () => {
      const result = await API.graphql(graphqlOperation(ListNotes));
      if ("data" in result && result.data) {
        const posts = result.data as ListNotesQuery;
        if (posts.listNotes) {
          setNotes(posts.listNotes.items as Note[]);
        }
      }

      const client = API.graphql(graphqlOperation(onCreateNote));
      if ("subscribe" in client) {
        client.subscribe({
          next: ({ value: { data } }: NoteSubscriptionEvent) => {
            if (data.onCreateNote) {
              const note: Note = data.onCreateNote;
              setNotes(prev => [...prev, note]);
            }
          }
        });
      }
    })();
  }, []);

  return (
    <div className="App">
      <div>
        <div>
          タイトル
          <input value={input.title} name="title" onChange={onFormChange} />
        </div>
        <div>
          内容
          <input
            value={input.content}
            name="content"
            onChange={onFormChange}
          />
        </div>
        <button onClick={onPost}>追加</button>
      </div>

      <div>
        {notes.map(data => {
          return (
            <div key={data.id}>
              <h4>{data.title}</h4>
              <p>{data.content}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
};

export default App;

入力フォームと先ほどApp Syncで登録したデータが表示されてるはず。入力フォームから送信するとリアルタイムでリストに反映されます。

認証機能の追加

以下のコマンドを打つ

amplify add auth

またいろいろ聞かれるので答える

Do you want to use the default authentication and security configuration? Default configuration
How do you want users to be able to sign in? Username
Do you want to configure advanced settings? No, I am done.

完了したら、またamplify pushで反映させる

App.tsxのimport部分の追記とexport部分を以下に変更する

import { withAuthenticator } from "aws-amplify-react";

// 省略


export default withAuthenticator(App, true);

yarn startで起動するとAmplifyで用意された認証画面が。よくあるログイン画面なので、ユーザー登録してログインして使えると思います。

Amplifyのデフォルト認証画面

認証を追加するとログインしないと先ほどの画面が見れないようになりました。

Amplifyのデフォルト認証画面

最後に

これで一通りの手順は完了です。基本的なログイン機能とTodoみたいな追加、取得はできるようになりました。

ただこのままでは更新・削除がないですし、登録したデータがユーザーに紐づいていないのでログインすれば誰かが登録した内容が誰でも見れる状態です。調べてる限り今回作ったNoteにownerIdか何かを追加して、filterかけて一覧取得すればよさそうかなと思ってます。

また、API keyの有効期限を7日間にしてるので以下の問題も。 blog.mismith.me

その辺りはまた別の機会に試します。

参考サイト

qiita.com

qiita.com

qiita.com