😼
Serverless Frameworkを使ってTypeScript製LambdaからAppSyncを叩くAPIをつくる
ユースケース
AmplifyとかでAppSyncを使ったサーバレスなウェブアプリケーションは構築したけど、そのDBにバッチでゴニョゴニョするサムシングが欲しいなー。うーんアプリケーション内で実装すると煩雑になるし。。。
せや!Lambdaに切り分けたろ!Serverless Frameworkの出番や!
手順
- 雑にserverless createでAPI Gateway+Lambdaのアプリケーションをつくる
- アプリからAppSyncを叩いてDBにアクセスする
- Userを作成してみる
完成物
環境
- Serverless Framework
- TypeScript
- Node.js 14.x
つくっていく
0. Serverless Frameworkの環境構築
$ npm i -g serverless
1. TypeScript用のテンプレをつくる
$ sls create -t aws-nodejs-typescript -p serverless-playground
2. GraphQL Clientの実装
$ npm i apollo-cache-inmemory aws-appsync graphql-tag isomorphic-fetch uuid
次に、src/functions/hello/graphql
というフォルダを用意し、Amplifyなどで生成されたGraphQLの型をぶち込みます
中身はここを参考にしてみてください
3. handler.tsの実装
src/functions/hello/handler.ts
import 'source-map-support/register';
import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/apiGateway';
import { formatJSONResponse } from '@libs/apiGateway';
import { NormalizedCacheObject } from 'apollo-cache-inmemory';
import { middyfy } from '@libs/lambda';
import { AWSAppSyncClient, AUTH_TYPE } from 'aws-appsync';
import schema from './schema';
import * as api from './graphql/Model';
import * as mutations from './graphql/mutations';
import * as queries from './graphql/queries';
import 'isomorphic-fetch';
const gql = require('graphql-tag');
const env = require("process").env;
const region = env.AWS_REGION;
const appSyncUrl = env.ENDPOINT_URL;
const apiKey = env.API_KEY;
const hello: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => {
const appSyncClient = new AWSAppSyncClient({
url: appSyncUrl,
region,
auth: {
type: AUTH_TYPE.API_KEY,
apiKey: apiKey,
},
disableOffline: true
});
try {
// Userを作成
const createUserInput: api.CreateUserInput = {
cognito_username: 'tmp',
email: event.body.email,
role: event.body.role,
};
const createUserResponse = await createUser(appSyncClient, createUserInput);
const userId = createUserResponse.data.createUser.id;
console.log(`userId is: ${userId}`);
// 作成したUserを検索
const listUsersFilter: api.ModelUserFilterInput = {
id: { eq: userId },
};
const user = await getUser(appSyncClient, listUsersFilter);
// 返り値
return formatJSONResponse({
result: user,
});
} catch (err) {
return formatJSONResponse({
result: err,
});
};
};
// Userを作成する非同期関数
const createUser = async (appSyncClient: AWSAppSyncClient<NormalizedCacheObject>, input: api.CreateUserInput): Promise<api.Output<api.CreateUserMutation>> => {
const createUserResponse = await appSyncClient.mutate({
mutation: gql(mutations.createUser),
variables: { input: input },
}) as api.Output<api.CreateUserMutation>;
return createUserResponse;
}
// Userを検索する非同期関数
const getUser = async (appSyncClient: AWSAppSyncClient<NormalizedCacheObject>, filter: api.ModelUserFilterInput): Promise<api.User | undefined> => {
const listUsersResponse = await appSyncClient.query({
fetchPolicy: 'network-only',
query: gql(queries.listUsers),
variables: filter,
}) as api.Output<api.ListUsersQuery>;
const user = listUsersResponse.data.listUsers.items[0] as api.User | undefined;
return user;
}
export const main = middyfy(hello);
おわり
Ex 1. node_modulesをlayerにする
$ npm install -D serverless-layers
serverless.ts
...
custom: {
...
'serverless-layers': {
layersDeploymentBucket: 'sample-bucket', // layerをuploadするS3 Bucket名
dependenciesPath: './package.json',
},
},
...
plugins: [
'serverless-webpack',
'serverless-layers', // ここを追加
],
Ex 2. API GatewayにAPI Keyを指定する
$ npm install -D serverless-add-api-key
serverless.ts
...
custom: {
...
apiKeys: [
{
name: 'sample', // API Keyの名前
},
],
},
...
plugins: [
'serverless-webpack',
'serverless-layers',
'serverless-add-api-key', // ここを追加
],
src/functions/hello/index.ts
export default {
...
environment: { // これを追加
API_KEY: process.env.API_KEY,
ENDPOINT_URL: process.env.ENDPOINT_URL,
},
}
デプロイしていく
$ sls deploy --stage prd --aws-profile sample
呼び出してみる
$ sls invoke -f hello --stage prd --aws-profile sample --log
Discussion