Slide 1

Slide 1 text

Amplify Gen2を 拡張してみよう JAWS-UG北陸新幹線 ( 福井開催 ) 2024-04-06 株式会社永和システムマネジメント 村上雅彦 (a.k.a @fossamagna)

Slide 2

Slide 2 text

村上 雅彦 株式会社永和システムマネジメント Amplify Japan User Group 運営メンバー AWS Community Builder (Front-End Web & Mobile since 2022) GitHub: https://github.com/fossamagna X(旧Twitter): https://twitter.com/fossamagna 自己紹介

Slide 3

Slide 3 text

● Amplify Gen2とは? ● Amplify Gen2のカスタマイズ ● Amplify Gen2のアーキテクチャ アジェンダ

Slide 4

Slide 4 text

● AWS CDKをベースとする次世代のAmplifyのバックエン ド構築ツール ● TypeScriptによるバックエンド定義を提供 ● CoC(設定より規約を優先する) ● GitのブランチとAWS環境を1:1でマッピング ● 開発者毎の独立したsandbox環境を提供 Amplify Gen2とは?

Slide 5

Slide 5 text

● AWS CDKをベースにTypeScriptで バックエンドを定義 ● AppSyncのGraphQLスキーマも TypeScriptで定義 ● 認可設定もTypeScriptで定義 // amplify/data/resource.ts import { a } from '@aws-amplify/backend'; const schema = a.schema({ Todo: a .model({ content: a.string(), done: a.boolean(), priority: a.enum(['low', 'medium', 'high']) }) .authorization([a.allow.owner(), a.allow.public().to(['read'])]), }); Amplify Gen2とは?

Slide 6

Slide 6 text

● AWS CDKをベースにTypeScriptで バックエンドを定義 ● defineBackでAmplifyのバックエン ドリソース全体を定義 // amplify/backend.ts import { defineBackend } from '@aws-amplify/backend'; import { auth } from './auth/resource'; import { data } from './data/resource'; defineBackend({ auth, data }); Amplify Gen2とは?

Slide 7

Slide 7 text

● CoC(設定より規約を優先する) ● amplify/backend.ts ○ amplifyのバックエンドリ ソースの定義 ● amplify/xxx/resource.ts ○ auth, dataなどカテゴリ毎 のリソース定義 ├── amplify/ │ ├── auth/ │ │ └── resource.ts │ ├── data/ │ │ └── resource.ts │ ├── backend.ts │ └── package.json ├── node_modules/ ├── .gitignore ├── package-lock.json ├── package.json └── tsconfig.json Amplify Gen2とは?

Slide 8

Slide 8 text

GitのブランチとAWS環境を1:1でマッピング Amplify Gen2とは? https://docs.amplify.aws/gen2/how-amplify-works/concepts/#build-fullstack-apps-with-typescript

Slide 9

Slide 9 text

開発者毎の独立したsandbox環境を提供 Amplify Gen2とは? https://docs.amplify.aws/gen2/how-amplify-works/concepts/#build-fullstack-apps-with-typescript

Slide 10

Slide 10 text

● 現在、Amplify Gen2のバックエンド としてauth, data, function, storageが提供されている ● Amplify Gen2はAWS CDKベース なので任意のCDK Constructを利 用してカスタマイズ可能 ○ https://docs.amplify.aws/ge n2/build-a-backend/add-aw s-services Amplify Gen2のカスタマイズ

Slide 11

Slide 11 text

export class AmplifyBuildNotification extends Construct { constructor ( scope: Construct, id: string, props: AmplifyBuildNotificationProps ) { super(scope, id); … new chatbot.SlackChannelConfiguration (this, "SlackChannel" , { slackChannelConfigurationName: "AmplifyBuildNotificationSlackChannel" , slackWorkspaceId: props.slackWorkspaceId , slackChannelId: props.slackChannelId , notificationTopics: [topic], } ); // 以下、他のConstructの定義が続く } Amplify Gen2のカスタマイズ(CDK Construct) // amplify/backend.ts import { defineBackend } from '@aws-amplify/backend' ; import { auth } from './auth/resource' ; import { data } from './data/resource' ; import { AmplifyBuildNotification } from './custom/build-notification/amplify-build-notification-construct'; const backend = defineBackend ({ auth, data }); new AmplifyBuildNotification ( backend.createStack ("NotificationStack" ), 'Notification' , { slackWorkspaceId: "TXXXXXXX" , // Slack Workspace's ID slackChannelId: "CXXXXXXX" , // Slack Channel ID } ); AWS Chatbotの設定などを含むカスタムのConstruct(一部抜粋) カスタムのConstructを含むStackをAmplify Gen2に追加する例

Slide 12

Slide 12 text

Amplify Gen2のプラグイン化した カスタムのConstructを 利用する例 amplify/build-notification/resource.ts import { defineBuildNotification } from "amplify-backend-build-notification" ; import { secret } from "@aws-amplify/backend" ; export const buildNotification = defineBuildNotification ({ slackWorkspaceId: secret("SLACK_WORKSPACE_ID" ), slackChannelId: secret("SLACK_CHANNEL_ID" ), }); Amplify Gen2のカスタマイズ(プラグイン化) import { defineBackend } from '@aws-amplify/backend' ; import { data } from "./data/resource" ; import { auth } from "./auth/resource" ; import { buildNotification } from "./build-notification/resource" const backend = defineBackend ({ auth, data, buildNotification , }); Amplify Gen2の独自プラグインをAmplify Gen2に追加する例 amplify/backend.ts

Slide 13

Slide 13 text

define*関数の定義 import type { BackendSecret } from "@aws-amplify/plugin-types"; import type { AmplifyBuildNotificationConditions } from "amplify-build-notification-construct"; export type AmplifyBuildNotificationFactoryProps = { slackChannelId: BackendSecret; slackWorkspaceId: BackendSecret; conditions?: AmplifyBuildNotificationConditions; }; export const defineBuildNotification = ( props: AmplifyBuildNotificationFactoryProps ): ConstructFactory> => new AmplifyBuildNotificationFactory(props, new Error().stack); ● バックエンド( defineBackend関数の引 数)定義には、任意の数の ConstructFactoryインスタンス を含め ることができる ● これらは define* 関数 (defineAuth、 defineFunction など) によって生成さ れるオブジェクト ● カスタムプラグインでは独自の ConstructFactoryインスタンスを返す define* 関数を定義する ● この関数がAmplifyユーザーがプラグイン を利用するエントリーポイントとなる

Slide 14

Slide 14 text

ConstructFactoryの定義 export class AmplifyBuildNotificationFactory implements ConstructFactory > { readonly provides = "build-notification" private generator : ConstructContainerEntryGenerator ; constructor ( private readonly props: AmplifyBuildNotificationFactoryProps , private readonly importStack = new Error().stack ) {} getInstance ( getInstanceProps : ConstructFactoryGetInstanceProps ): ResourceProvider { const { constructContainer } = getInstanceProps ; if (!this.generator ) { this.generator = new BuildNotificationContainerEntryGenerator ( this.props, getInstanceProps ); } return constructContainer .getOrCompute ( this.generator ) as ResourceProvider ; } } ● getInstanceメソッドでResourceProviderのイ ンスタンスを返すのが役割 ● 実際のResouceProviderのインスタンス生成は ConstructContainerEntryGeneratorインタ フェースを実装したクラスに実装 ● amplify-backendでResouceProviderのインス タンスをキャッシュしていて amplify-backendが必 要に応じて ConstructContainerEntryGeneratorの generateContainerEntryメソッドを呼び出して インスタンスを生成し管理 ● providesで宣言した名前で ConstructFactory のインスタンスが管理される ● ConstructFactoryが依存する他の ConstructFactoryを利用可能

Slide 15

Slide 15 text

ConstructContainerEntry Generatorの定義 import type { ConstructContainerEntryGenerator,ConstructFactoryGetInstanceProps,GenerateContainerEntryProps, ResourceProvider } from "@aws-amplify/plugin-types"; import type { AmplifyBuildNotificationFactoryProps } from "./types"; import { AmplifyBuildNotification } from "amplify-build-notification-construct"; export class BuildNotificationContainerEntryGenerator implements ConstructContainerEntryGenerator { readonly resourceGroupName: "build-notification"; constructor( private readonly props: AmplifyBuildNotificationFactoryProps, private readonly getInstanceProps: ConstructFactoryGetInstanceProps ) {} generateContainerEntry( props: GenerateContainerEntryProps ): ResourceProvider { const { scope, backendSecretResolver } = props; return { resources: new AmplifyBuildNotification( scope, "AmplifyBuildNotification", { slackChannelId: backendSecretResolver .resolveSecret(this.props.slackChannelId) .unsafeUnwrap(), // slackChannelIdに対するシークレット値を解決する slackWorkspaceId: backendSecretResolver .resolveSecret(this.props.slackWorkspaceId) .unsafeUnwrap(), // slackWorkspaceIdに対するシークレット値を解決する conditions: this.props.conditions, } ), }; } } ● generateContainerEntryメソッドで ResourceProviderのインスタンスを生成 して返すのが役割 ● 引数としてamplify-backend から渡さ れる、BackendSecretResolver ● を利用してデプロイ環境毎のシークレッ ト値を解決できる。 ● 実際のResouceProviderのインスタンス作 成は ConstructContainerEntryGeneratorイン タフェースを実装したクラスに実装する。 ● amplify-backend側でResouceProvider のインスタンスを管理する仕組みがあるた め。amplify-backendがインスタンスが必 要になった時に ConstructContainerEntryGeneratorの generateContainerEntryメソッドを呼び出 してインスタンスを生成し管理する。

Slide 16

Slide 16 text

● Amplify Gen2のアーキテクチャの資料 https://github.com/aws-amplify/amplify-backend/blob/ma in/PROJECT_ARCHITECTURE.md ● この発表で紹介したプラグインのコードのリポジトリ https://github.com/fossamagna/amplify-build-notification Amplify Gen2のアーキテクチャの資料