5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amplify Gen2 からは TypeScript でバックエンド開発をしよう

Last updated at Posted at 2024-12-06

はじめに

AWS Amplify Advent Calendar 2024 の7日目の記事です。

個人的に Amplify が好きなので、これまで(*1)も Amplify 関連記事を書いてきました。今年は Amplify Gen2 のバックエンド周りの情報を整理していきたいと思います。

(*1)

Amplify Gen2 とは

Amplify は2024年5月に Gen1(第一世代)から Gen2(第二世代)に進化しました。Gen2 で進化したポイントは以下の4つです。

TypeScript フルスタック開発
フロントとバック両方を TypeScript, AWS CDK でコード化

Amplify コンソールで統合管理
ビルド、ホスティング、環境変数、シークレット、データ、認証、ストレージ

開発者別のサンドボックス環境
Gen1 ではチーム共有環境だったものが、開発者ごとの独立環境へ

Git ベースの環境管理
ブランチに応じた本番・ステージング・プレビュー環境

TypeScript によるフルスタック構成

フロントエンドとバックエンド両方を TypeScript で型安全・宣言的に定義していくことができます。

データモデル(DynamoDB)、API(AppSync)、認証・認可(Cognito)、ストレージ(S3)、関数(Lambda)を TypeScript で宣言的に定義すると AWS リソースが自動生成されます。また AWS CDK を TypeScript で記述した IaC でクラウドインフラを構築・管理できます。そして、このバックエンドと React, Vue.js などのモダンフロントエンドを接続することができます。

手を動かして理解を深める

AWS Summit Japan 2024 で Gen2 に触発されて書いた記事の続きから手を動かして理解を深めていきたいと思います。

Auth

image.png

バックエンド
メールログインでリソース定義します。

amplify/auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';

/**
 * Define and configure your auth resource
 * @see https://docs.amplify.aws/gen2/build-a-backend/auth
 */
export const auth = defineAuth({
  loginWith: {
    email: true,
  },
});

フロントエンド
UI コンポーネントで Authenticator が用意されているので利用します。

src/App.tsx
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'

function App() {
...省略...

  return (
    <Authenticator>
      {({ signOut }) => (
        <main>
          <h1>My todos</h1>
          ...省略...
          <button onClick={signOut}>Sign out</button>          
        </main>
      )}
    </Authenticator>
  );
}
export default App;

Data

image.png

バックエンド
DynamoDB のデータモデル定義をします。

amplify/data/resource.ts
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
    })
    .authorization((allow) => [allow.publicApiKey()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "apiKey",
    // API Key is used for a.allow.public() rules
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
});

フロントエンド

src/App.tsx

import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import { useEffect, useState } from "react";
import type { Schema } from "../amplify/data/resource";
import { generateClient } from "aws-amplify/data";

const client = generateClient<Schema>();

function App() {

  ...データ操作はこの辺りの実装です....
  const [todos, setTodos] = useState<Array<Schema["Todo"]["type"]>>([]);

  useEffect(() => {
    client.models.Todo.observeQuery().subscribe({
      next: (data) => setTodos([...data.items]),
    });
  }, []);

  function createTodo() {
    client.models.Todo.create({ content: window.prompt("Todo content") });
  }

  function deleteTodo(id: string) {
    client.models.Todo.delete({ id })
  }
  ...データ操作はこの辺りの実装です....


  return (
    <Authenticator>
      {({ signOut }) => (
        <main>
          <h1>My todos</h1>

          ...データ操作はこの辺りの実装です....
          <button onClick={createTodo}>+ new</button>
          <ul>
            {todos.map((todo) => (
              <li
                key={todo.id}
                onClick={() => deleteTodo(todo.id)}
              >{todo.content}</li>
            ))}
          </ul>
          ...データ操作はこの辺りの実装です....

          <button onClick={signOut}>Sign out</button>
        </main>
      )}
    </Authenticator>
  );
}

export default App;

Storage

image.png

image.png

バックエンド
S3 のリソース設定をします。

amplify/storage/resource.ts
import { defineStorage } from '@aws-amplify/backend';

export const storage = defineStorage({
  name: 'amplifyTeamDrive',
  access: (allow) => ({
    'profile-pictures/{entity_id}/*': [
      allow.guest.to(['read']),
      allow.entity('identity').to(['read', 'write', 'delete'])
    ],
    'picture-submissions/*': [
      allow.authenticated.to(['read', 'write']),
      allow.guest.to(['read', 'write'])
    ],
  })
});

フロントエンド

src/App.tsx

import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import { useEffect, useState } from "react";
import { uploadData } from 'aws-amplify/storage';

function App() {
  ...Storage操作はこの辺り...
  const [file, setFile] = useState<File | undefined>();

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (files) {
      setFile(files[0]);
    }
  };

  const handleUpload = () => {
    if (file) { // undefined チェック
      uploadData({
        path: `picture-submissions/${file.name}`,
        data: file,
      })
      alert(`${file.name} がアップロードされました`)
    } else {
      alert(`ファイルが選択されていません`)
    };
  };
  ...Storage操作はこの辺り...

  return (
    <Authenticator>
      {({ signOut }) => (
        <main>
          <h1>My todos</h1>
          ...Storage操作はこの辺り...
          <div>
            <input type="file" onChange={handleChange} />
            <button
              onClick={handleUpload}>
              Upload
            </button>
          </div>
          ...Storage操作はこの辺り...
          <button onClick={signOut}>Sign out</button>          
        </main>
      )}
    </Authenticator>
  );
}

export default App;

おわりに

今回は Auth, Data, Storage 周りをさらっと触りました。他にも Function 等もあり、まだまだ深堀りがいがありそうですね。

それでは良いお年を。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?