Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Amplify Gen2を 拡張してみよう JAWS-UG北陸新幹線 ( 福井開催 ) 2024-04-06/Let's extend Amplify Gen2

Amplify Gen2を 拡張してみよう JAWS-UG北陸新幹線 ( 福井開催 ) 2024-04-06/Let's extend Amplify Gen2

MURAKAMI Masahiko

April 06, 2024
Tweet

More Decks by MURAKAMI Masahiko

Other Decks in Technology

Transcript

  1. 村上 雅彦 株式会社永和システムマネジメント Amplify Japan User Group 運営メンバー AWS Community

    Builder (Front-End Web & Mobile since 2022) GitHub: https://github.com/fossamagna X(旧Twitter): https://twitter.com/fossamagna 自己紹介
  2. • 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とは?
  3. • 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とは?
  4. • 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とは?
  5. • 現在、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のカスタマイズ
  6. 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に追加する例
  7. 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
  8. 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<ResourceProvider<AmplifyBuildNotification>> => new AmplifyBuildNotificationFactory(props, new Error().stack); • バックエンド( defineBackend関数の引 数)定義には、任意の数の ConstructFactoryインスタンス を含め ることができる • これらは define* 関数 (defineAuth、 defineFunction など) によって生成さ れるオブジェクト • カスタムプラグインでは独自の ConstructFactoryインスタンスを返す define* 関数を定義する • この関数がAmplifyユーザーがプラグイン を利用するエントリーポイントとなる
  9. ConstructFactoryの定義 export class AmplifyBuildNotificationFactory implements ConstructFactory <ResourceProvider <AmplifyBuildNotification >> {

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