AWS Amplify Advent Calendar 2024 の8日目の記事です!
この記事 is 何
Amplifyを実装している中で使ったTipsを簡単にまとめる記事です。
テーマは以下の2つです。
- バックエンドのCustom Resolverの実装&テスト
- Amplify生成リソースのタグ管理
1. バックエンドのCustom Resolverの実装&テスト
Amplify Gen2からバックエンドのAPIのCustom Resolverを簡単に実装できるようになりました。
バックエンドのAPIにバリデーションなどの実装を追加する場合にはCustom Resolverを利用します。
例えば、郵便番号を受け取って保存するAPIを想定したときに、リクエストされるデータは「半角数字7桁」であることを強制したりするときに利用します。
実装方法
使い方は簡単で、backendのスキーマにCustom Resolverとなるjsのコードを参照するような実装を追記します。
const schema = a.schema({
Todo: a
.model({
content: a.string(),
})
.authorization((allow) => [allow.publicApiKey()]),
createNumber7Todo: a // 7桁の10進数を表す文字列を受け取るCustom Resolverをエンドポイントに紐づける
.mutation()
.arguments({
content: a.string(),
})
.returns(a.ref("Todo"))
.authorization((allow) => [allow.publicApiKey()])
.handler(a.handler.custom({
dataSource: a.ref('Todo'),
entry: './createNumber7.js'
}))
});
あとはCustom Resolverとなるjsファイルを作成し、リクエストで受け取った値をutilsライブラリを使ってチェックをかけたりすることができます。
import { util } from '@aws-appsync/utils';
export function request(ctx) {
const { content } = ctx.args;
// 半角数字7桁かどうかをチェック
if (content.length !== 7 || !util.matches('^[0-9]{7}$', content)) {
return util.error('Invalid input: content must be a 7-digit number');
}
const now = util.time.nowISO8601();
return {
operation: 'PutItem',
key: util.dynamodb.toMapValues({
id: util.autoId()
}),
attributeValues: util.dynamodb.toMapValues({
content,
createdAt: now,
updatedAt: now
})
};
}
export function response(ctx) {
return ctx.result
}
utilsライブラリは他にも色々なmatcherなどの実装があるので、それらを利用しながらより複雑な実装をすることもできます。
テストの実装方法
Custom Resolverでロジックの実装をする場合、きちんとテストを書きたくなります。
バックエンドに対するAPIレベルでのテストを実施するのも良いですが、ここではSmallなテストをする場合のサンプルを書いておきます。
こちらのGitHubを参考にさせていただいています!
https://github.com/naedx/amplify-playground/tree/dev/projects/amplify-appsync-vitest
基本的には @aws-appsync/utils
をモックすることでSmallテストを実装します。
郵便番号のInputに関するテストは以下のように実装することでテストコードがかけます。
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { request } from './amplify/data/createNumber7Todo';
import { util } from '@aws-appsync/utils';
// util のモック
vi.mock('@aws-appsync/utils', () => ({
util: {
matches: vi.fn(),
time: {
nowISO8601: vi.fn().mockReturnValue('2024-03-20T00:00:00Z'),
},
autoId: vi.fn().mockReturnValue('mock-id'),
dynamodb: {
toMapValues: vi.fn(x => x),
},
error: vi.fn(msg => ({ message: msg })),
},
}));
describe('createNumber7', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('request', () => {
test('正常な7桁の数字を受け付ける', () => {
util.matches.mockReturnValue(true);
const ctx = {
args: {
content: '1234567',
},
};
const result = request(ctx);
expect(result).toEqual({
operation: 'PutItem',
key: {
id: 'mock-id',
},
attributeValues: {
content: '1234567',
createdAt: '2024-03-20T00:00:00Z',
updatedAt: '2024-03-20T00:00:00Z',
},
});
});
test('7桁未満の数字はエラーを返す', () => {
util.matches.mockReturnValue(false);
const ctx = {
args: {
content: '123456',
},
};
const result = request(ctx);
expect(result).toEqual({
message: 'Invalid input: content must be a 7-digit number',
});
});
test('7桁を超える数字はエラーを返す', () => {
util.matches.mockReturnValue(false);
const ctx = {
args: {
content: '12345678',
},
};
const result = request(ctx);
expect(result).toEqual({
message: 'Invalid input: content must be a 7-digit number',
});
});
test('数字以外の文字を含む場合はエラーを返す', () => {
util.matches.mockReturnValue(false);
const ctx = {
args: {
content: '123a567',
},
};
const result = request(ctx);
expect(result).toEqual({
message: 'Invalid input: content must be a 7-digit number',
});
});
});
});
2.Amplify生成リソースのタグ管理
Amplifyを使うといろんなAWSリソースがバンバン作られていきます。
1つのAWSアカウントで複数のAmplifyプロジェクトを利用する場合、似たようなリソースがたくさんできてしまい、コスト管理などに支障をきたします。
その対策として、タグ付けをしてリソース管理を実現できるようにします。
実装方法
やり方自体は簡単で、backendリソースを管理しているCDKにタグのコードを追記するだけです。
authやdataなどのリソース事に細かくタグ付けを分けることもできます。
import { Tags } from 'aws-cdk-lib';
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
const backend = defineBackend({
auth,
data,
});
// タグをリソース毎に追加
const backendTags = Tags.of(backend.stack);
backendTags.add('project', 'amplify-test-app');
const authTags = Tags.of(backend.auth.stack);
authTags.add('service', 'amplify-test-app-auth');
const dataTags = Tags.of(backend.data.stack);
dataTags.add('service', 'amplify-test-app-data');
結果はこんな感じになります!
参考:
https://docs.amplify.aws/react/build-a-backend/add-aws-services/tagging-resources/
以上です!
なにかのお役に立てば幸いです。
こういった発信等、Xで行っていますのでよろしければフォローいただけると嬉しいです🙇