ã¯ããã«
Serverless Framework ã䜿ã£ãŠããŠã床ã ãããã€æã«æåã§èšå®ããŠããäœæ¥å 容ãèªååããããªãšæãããã©ã°ã€ã³äœæã®ç¥èç¿åŸãå ŒããŠã©ã€ãã©ãªãäœæã NPM ã§å ¬éããŠã¿ãŸããã
https://www.npmjs.com/package/serverless-amplify-auth
ä»åŸãéçºããå¯èœæ§ã¯ãããããªã®ã§ Serverless ã®ãã©ã°ã€ã³ã TypeScript ã§äœæããéã®æé ããŸãšããŠãããŸãããåæé ã¯ã¶ãã¯ãªãšçŽ¹ä»ãã€ã€ãäž»ã«ãã®éçšã§ããã£ãç¹ã工倫ããç¹ã«éãããããŠèšäºãæžããŠãããŸãã
åäœç°å¢
- Node.js 12.19.0
- Serverless Framework
- Framework Core: 2.10.0
- Plugin: 4.1.1
- SDK: 2.3.2
- Components: 3.3.0
éçºç°å¢ãæŽãã
æ¬èšäºã®å 容ãæåŸãŸã§å®è·µããéã®æçµçãªãããžã§ã¯ãã®ãã£ã¬ã¯ããªæ§é ã¯äžèšã«ãªããŸãã
tree -I node_modules -L 2 ./
./
âââ example # ã©ã€ãã©ãªã®åäœæ€èšŒçšã®ãµã³ãã«ã³ãŒããé
眮ãããã©ã«ã
â  âââ handler.js
â  âââ package.json
â  âââ serverless.yml
âââ lib # src ãã©ã«ãå
ã®ãã¡ã€ã«ãã³ã³ãã€ã«ããçµæãé
眮ãããã©ã«ã (ã©ã€ãã©ãªãšããŠå©çšããéã«å«ãŸãããœãŒã¹ã³ãŒã矀)
â  âââ index.js
â  âââ index.js.map
âââ package-lock.json
âââ package.json
âââ src # Serverless ãã©ã°ã€ã³ã®ãœãŒã¹ã³ãŒããé
眮ãããã©ã«ã
â  âââ index.ts
âââ tsconfig.json
åºæ¬çã«ã¯ TypeScript 㧠Serverless Framework ã® Plugin ãæžããŠã¿ã | Developers.IO ã®æé ããªãã£ãŠããã ãã§ç°å¢æ§ç¯èªäœã¯å¯èœã§ããããã§ãããã§ã¯èªåãªãã«å·¥å€«ããç®æã«ã€ããŠèšèŒããŠãããŸãã
ãŸãã¯ãéçºã«å¿ èŠãªããã±ãŒãžãäžèšã³ãã³ãã§ãŸãšããŠã€ã³ã¹ããŒã«ããŸãã
# TypeScript ã®éçºã«å¿
èŠãªããã±ãŒãžã€ã³ã¹ããŒã«
npm i -D typescript
# TypeScript ã®åå®çŸ©ãã¡ã€ã«ã®ã€ã³ã¹ããŒã«
npm i -D @types/node @types/serverless
# ä»å㯠AWS ãããã€ããŒåãã®éçºãè¡ããã SDK ãã€ã³ã¹ããŒã«ãã
npm i --save aws-sdk
TypeScript ã®ã³ã³ãã€ã«æã«å¿
èŠãšãªã tsconfig.json
ã¯äžèšã®ããã«èšå®ããŸããã
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"strict": true,
"strictBindCallApply": false,
"strictNullChecks": false,
"outDir": "lib",
"sourceMap": true
},
"include": ["src/**/*"]
}
compilerOptions.strict
ã«ã¯ true
ãèšå®ãã€ã€ãcompilerOptions.strictNullChecks
çã«ã¯ false
ãèšå®ããããšã§ãéšåçã« TypeScript ã®ã³ã³ãã€ã«ãã§ãã¯ãå€ãããã«ããŸããã
outDir
ã«ã¯ lib
ãæå®ããããšã§ãã³ã³ãã€ã«ããã TypeScript ãã¡ã€ã«ã¯ lib
ãã©ã«ãã«åºåãããããèšå®ããŸããã
include
ã«ã¯ src/**/*
ãæ瀺çã«æå®ããŠãããsrc ãã©ã«ãå
ã®å
šãã¡ã€ã«ãã³ã³ãã€ã«å¯Ÿè±¡ã«ããŠãããŸãã
package.json
ã®å
容ã¯éšåçã«æç²ãã説æãå¿
èŠãããªé
ç®ã«ã€ããŠèª¬æããããŸãã
å
šå®¹ãææ¡ãããæ¹ã¯ ãã¡ã ããã確èªããã ããŸãã
{
"main": "lib/index.js",
"files": ["lib"],
"scripts": {
"build": "rm -rf lib && tsc",
"test": "echo \"Error: no test specified\" && exit 1"
}
}
main
ã«ã¯ src/index.ts
ãã³ã³ãã€ã«ãããšçæããã lib/index.js
ãæå®ããŸããããã®ãããã©ã€ãã©ãªã®ãšã³ããªãŒãã€ã³ã㯠lib/index.js
ãèšå®ãããŸãã
files
ã«ã¯ lib
ãã©ã«ããæå®ããããšã§ãTypeScript ãã³ã³ãã€ã«ããçµæã®ã¿ãã©ã€ãã©ãªã®ãœãŒã¹ã³ãŒããšããŠåã蟌ãŸããããã«ãªããŸãã
Serverless ãã©ã°ã€ã³ã®éçºãé²ãã
éçºç°å¢ãæŽã£ããšããã§æ©é Serverless Plugin ã®ãœãŒã¹ã³ãŒããæžããŠãããŸããTypeScript ã®ãœãŒã¹ã³ãŒã㯠src/index.ts
ã«é
眮ããŸãã
Serverless ãã©ã°ã€ã³ã®ããã°ã©ã ãæžã
// src/index.ts
import * as Serverless from "serverless";
import { SharedIniFileCredentials, config } from "aws-sdk";
/**
* serverless.yml ã® custom property ã®åå®çŸ©
*/
interface Variables {
value1: string;
value2: number;
value3: boolean;
profile?: string;
}
export default class Plugin {
serverless: Serverless;
options: Serverless.Options;
hooks: {
[event: string]: () => Promise<void>;
};
variables: Variables;
/**
* ãã©ã°ã€ã³ã®åæåé¢æ°ã
* 泚æç¹ãšããŠãåæåé¢æ°å
ã§ã¯ serverless.yml å
ã®å€æ°å±éãè¡ãããªãã®ã§ã
* ${ssm:~} çã§èšå®ããå€ãåŒã³åºããŠããé©åã«å€ãèšå®ãããªãç¶æ
ã§åŒã³åºãããšã«ãªãã
*/
constructor(serverless: Serverless, options: Serverless.Options) {
this.serverless = serverless;
this.options = options;
/**
* serverless.service.custom å
ã®ç¹å®ããããã£ãååŸããããã®èšè¿°
* ä»å㯠Serverless ã®ãã©ã°ã€ã³åã« serverless-typescript ãèšå®ããããã
* serverless-typescript æååãããŒãšããŠæå®ããã
*/
this.variables = serverless.service.custom["serverless-typescript"];
/**
* ãã©ã°ã€ã³ãããã¯ããé¢æ°ãæå®ãããè€æ°æå®ããããšãå¯èœã ãã
* ä»å㯠before:package:createDeploymentArtifacts ãæå®ããŠã
* ããã±ãŒãžã³ã°ã®æåã®åŠçãå®çŸ©ãã run é¢æ°ã§ããã¯ããã
*/
this.hooks = {
"before:package:createDeploymentArtifacts": this.run.bind(this),
};
}
/**
* before:package:createDeploymentArtifacts æã«å®è¡ãããé¢æ°
*/
async run() {
/**
* ãã©ã°ã€ã³å®è¡æã«å¿
èŠãšãªããã£ãŒã«ããã»ãããããŠããªããã°åŠçãã¹ããããã
*/
if (!this.variables) {
this.serverless.cli.log(
`serverless-typescript: Set the custom.serverless-typescript field to an appropriate value.`
);
return;
}
/**
* this.serverless.getProvider é¢æ°ãçšããããšã§ã
* ãããã€æã®ã¢ã«ãŠã³ãã®åçš®æ
å ±ã«ã€ããŠååŸããããšãåºæ¥ã
*/
const awsProvider = this.serverless.getProvider("aws");
const region = await awsProvider.getRegion();
const accountId = await awsProvider.getAccountId();
const stage = await awsProvider.getStage();
/**
* serverless.yml ã§æå®ããå€ã AWS æ
å ±ãååŸã§ããŠãããã
* 確èªããããã«æšæºåºåãã
*/
this.serverless.cli.log(
`serverless-typescript values: ${JSON.stringify({
stage: stage,
region: region,
accountId: accountId,
variables: this.variables,
})}`
);
/**
* ãã©ã°ã€ã³å
ã§åŠçãå®è¡ããéãå¥ã®ç¹å® Profile ãçšãããéã¯ã
* AWS SDK ã® SharedIniFileCredentials ãçšããŠåãæ¿ãããšæ¥œã«åæ¿å¯èœã
* ãã®é㯠process.env.AWS_SDK_LOAD_CONFIG ã«å€ãèšå®ããŠããããš
*/
if (this.variables.profile) {
process.env.AWS_SDK_LOAD_CONFIG = "true";
const credentials = new SharedIniFileCredentials({
profile: this.variables.profile,
});
config.credentials = credentials;
}
}
}
module.exports = Plugin;
ãœãŒã¹ã³ãŒãå ã«ããã€ãã³ã¡ã³ããæ®ããŸããããäœç¹ãè£è¶³ã®èª¬æãããŠãããŸãã
serverless.service.custom['serverless-typescript']
ãåŒã³åºãããšã§ãserverless.yml
å
ã®äžèšã®èšè¿°å
容ã Object
ãšããŠååŸã§ããŸãã
# serverless.yml
custom:
# custom.serverless-typescript å
ã®å®çŸ©ã Object ãšããŠååŸå¯èœ
serverless-typescript:
value1: "value1"
value2: 0
value3: true
# profile: default (optional)
this.hooks
ã«ã¯å¿
èŠã«å¿ããŠããã¯ãæå®ããŸããããã¯ã®æžãæ¹ã«ã€ããŠã¯ å
¬åŒããã¥ã¡ã³ã ã«è©³çŽ°ãèšèŒãããŠããŸããããã¯ã®çš®é¡ã«ã€ããŠã¯ Gist ã§ãŸãšããŠãã ãã£ãŠããæ¹ãããŸããã
this.serverless.getProvider('aws')
ãçšããããšã§ããããã€æã«ã¢ã«ãŠã³ãã®åçš®æ
å ±ã«ã€ããŠååŸããããšãåºæ¥ãŸãããã®èšè¿°ãå©çšããããšã§ Serverless Pseudo Parameters ã®ãããªã·ã³ã¿ãã¯ã¹ãèªèº«ã®ãã©ã°ã€ã³ã«åã蟌ãããšãå¯èœã«ãªããŸãã
ç§ãäœæãããã©ã°ã€ã³ã§ã serverless.yml 㧠ARN ãæ§ç¯ããéã«å©çšããŠããŠãindex.ts å
ã§å©çšããŸããã
ãŸãããã©ã°ã€ã³å ã§ãããã€æãšã¯ç°ãªã Profile ã䜿çšãããã±ãŒã¹ãããããšåããŸãããã㯠AWS SDK ã® SharedIniFileCredentials ãçšããããšã§ç°¡æã«å®è£ ã§ããŸããã
泚æç¹ãšããŠãSharedIniFileCredentials
ãçšããŠãããã¡ã€ã«ãåãæ¿ããæã¯ãç°å¢å€æ°ã« AWS_SDK_LOAD_CONFIG=“true” ãèšå®ããå¿
èŠããããŸããã
èšå®ããªããš ConfigError: Missing region in config
ãšãããšã©ãŒãçºçããŠããŸãããããã¡ã€ã«ãåãæ¿ããããšãåºæ¥ãŸããã§ããã
ããã§ã¯ã次ã«ãã©ã°ã€ã³ã®åäœæ€èšŒçšã³ãŒãã example
ãã©ã«ãã«é
眮ããŠãããŸãã
Serverless ãã©ã°ã€ã³ã®åäœæ€èšŒçšããã°ã©ã ãæžã
example
ãã©ã«ãå
ã«ã¯æ€èšŒçšãããžã§ã¯ããäœæããã®ã§ããã®åæºåãšã㊠example/package.json
ãäœæããŸãã
# package.json ãã¡ã€ã«ãäœæãã
cd example && npm init -y
example/package.json
ãã¡ã€ã«ãäœæãããéçºçšã®ã¹ã¯ãªããã example/package.json
ã«è¿œèšããŸãã
{
"scripts": {
"prestart": "cd ../ && npm run build",
"start": "sls package",
"test": "echo \"Error: no test specified\" && exit 1"
}
}
scripts
å
ã® prestart
㯠start
ã¹ã¯ãªããå®è¡åã«å®è¡ãããã¹ã¯ãªããã§ããnpm start
ãå®è¡ãããš prestart
ã§ãã©ã°ã€ã³ã® build
ã¿ã¹ã¯ãå®è¡ããåŸã Serverless Framework ã®ããã±ãŒãžã³ã°ãè¡ãããšã§ãã©ã°ã€ã³ã®åäœç¢ºèªãè¡ããŸãã
ä»å㯠Serverless ã® before:package:createDeploymentArtifacts
ããã¯ãå©çšããŠããã®ã§ãsls package
ã³ãã³ãã§åäœæ€èšŒãå¯èœãšãªã£ãŠããŸããbefore:deploy:deploy
çã®ãããã€äžã«å®è¡ãããããã¯ãå©çšããé㯠sls deploy --noDeploy
ã³ãã³ãçã§åäœæ€èšŒãè¡ãå¿
èŠããããŸãã
次ã«åäœæ€èšŒçšã® serverless.yml
ã example
ãã©ã«ãã«é
眮ããŸãã
# serverless.yml
service:
name: serverless-typescript
publish: false
# ãã©ã°ã€ã³å
ã§å©çšããèšå®å€ãå®çŸ©ãã
custom:
serverless-typescript:
value1: "value1"
value2: 0
value3: true
profile: custom_profile
provider:
name: aws
runtime: nodejs12.x
region: ap-northeast-1
# ãã©ã°ã€ã³ã®ãã¹ãæå®ããŠèªã¿èŸŒã
plugins:
localPath: "../../"
modules:
- serverless-typescript
# äœã§ãè¯ãã®ã§åäœæ€èšŒçšã®é¢æ°ãå®çŸ©ãã (é¢æ°ã®å®çŸ©ã¯åŸè¿°)
functions:
hello:
handler: handler.hello
example
ãã©ã«ãå
ã« handler.js
ãé
眮ã㊠functions.hello.handler
ã§çšããæ€èšŒçšã®é¢æ°ãå®çŸ©ããŸãã
// example/handler.js
"use strict";
// æ€èšŒçšã®é¢æ°ãserverless.yml å
ã§ã¯ handler.hello ã§åç
§å¯èœ
module.exports.hello = (event, context, callback) => {
callback(null, {
statusCode: 200,
body: "Hello World!",
});
};
äžèšäœæ¥ãå®äºæ¬¡ç¬¬ãcd example && npm start
ãå®è¡ããŠåäœæ€èšŒããŠã¿ãŸãã
cd example && npm start
> example@1.0.0 prestart /Users/nika/Desktop/serverless-typescript/example
> cd ../ && npm run build
> serverless-typescript@1.0.0 build /Users/nika/Desktop/serverless-typescript
> rm -rf lib && tsc
> example@1.0.0 start /Users/nika/Desktop/serverless-typescript/example
> sls package
Serverless: Configuration warning at 'service': unrecognized property 'publish'
Serverless:
Serverless: Learn more about configuration validation here: http://slss.io/configuration-validation
Serverless:
# src/index.ts å
ã® this.serverless.cli.log ã®åºåå
容
# åçš®å€ãæ£åžžã«ã»ãããããŠããããšã確èªåºæ¥ã
Serverless: serverless-typescript values: {"stage":"dev","region":"ap-northeast-1","accountId":"XXXXXXXXXX","variables":{"value1":"value1","value2":0,"value3":true,"profile":"custom_profile"}}
Serverless: Packaging service...
Serverless: Excluding development dependencies...
æšæºåºåã«ãããã©ã°ã€ã³å ã§åºåãããã°ãããé©åã«å€ãååŸåºæ¥ãŠããããšã確èªåºæ¥ãã° OK ã§ãã
AWS Profile ã®åãæ¿ããã§ããã確èªããŠã¿ã
Serverless ãã©ã°ã€ã³ã§ã® Profile ã®åãæ¿ãã«ã€ããŠãåäœæ€èšŒããŸã åºæ¥ãŠããªãã®ã§ç¢ºèªããŠãããŸãã
serverless.yml
å
ã® custom.serverless-typescript.profile
ã«èšå®ç®æã¯æ¢ã«çšæããŠããã®ã§ã~/.aws/credentials
ã«å®åšãã Profile åãæå®ããŸãã
# serverless.yml (äžéšæç²)
custom:
serverless-typescript:
profile: <ãã©ã°ã€ã³å®è¡æã«äœ¿çšããã Profile å>
åäœæ€èšŒã®ãããsrc/index.ts
å
ã«ãã°åºåã®èšè¿°ãå ããŸãã
// src/index.ts
import * as Serverless from "serverless";
import { SharedIniFileCredentials, config } from "aws-sdk";
interface Variables {
value1: string;
value2: number;
value3: boolean;
profile?: string;
}
export default class Plugin {
serverless: Serverless;
options: Serverless.Options;
hooks: {
[event: string]: () => Promise<void>;
};
variables: Variables;
constructor(serverless: Serverless, options: Serverless.Options) {
this.serverless = serverless;
this.options = options;
this.variables = serverless.service.custom["serverless-typescript"];
this.hooks = {
"before:package:createDeploymentArtifacts": this.run.bind(this),
};
}
async run() {
if (!this.variables) {
this.serverless.cli.log(
`serverless-typescript: Set the custom.serverless-typescript field to an appropriate value.`
);
return;
}
const awsProvider = this.serverless.getProvider("aws");
const region = await awsProvider.getRegion();
const accountId = await awsProvider.getAccountId();
const stage = await awsProvider.getStage();
this.serverless.cli.log(
`serverless-typescript values: ${JSON.stringify({
stage: stage,
region: region,
accountId: accountId,
variables: this.variables,
})}`
);
if (this.variables.profile) {
process.env.AWS_SDK_LOAD_CONFIG = "true";
const credentials = new SharedIniFileCredentials({
profile: this.variables.profile,
});
config.credentials = credentials;
// Profile ãåãæ¿ããããã確èªããããã«ãã°ãåºåãã
this.serverless.cli.log(
`serverless-typescript profile: ${JSON.stringify(config.credentials)}`
);
}
}
}
module.exports = Plugin;
æ©é cd example && npm start
ãå®è¡ããŠæ£åžžã« profile ãåãæ¿ããããŠãããã確èªããŠã¿ãŸãã
# æåæã®å®è¡çµæ
cd example && npm start
# ...
# accessKeyId ã®ãã£ãŒã«ãã« ~/.aws/credentials å
ã«ååšããå€ãåºåãããŠãã
Serverless: serverless-typescript profile: {"expired":false,"expireTime":null,"refreshCallbacks":[],"accessKeyId":"XXXXXXXXXXXXXX","profile":"XXXXXXXXXXXXXX","disableAssumeRole":false,"preferStaticCredentials":false,"tokenCodeFn":null,"httpOptions":null}
# ...
ã¡ãªã¿ã«ååšããªã Profile ãæå®ããå Žåã®åºåã¯äžèšã®ããã«ãªããŸãã
# 倱ææã®å®è¡çµæ
cd example && npm start
# ...
# accessKeyId ã®ãã£ãŒã«ããååšããªãæ㯠Profile ãæ£ããèšå®åºæ¥ãŠããªã
Serverless: serverless-typescript profile: {"expired":false,"expireTime":null,"refreshCallbacks":[],"profile":"custom_profile","disableAssumeRole":false,"preferStaticCredentials":false,"tokenCodeFn":null,"httpOptions":null}
# ...
ãããã«
ä»ååã㊠Serverless ãã©ã°ã€ã³ã®éçºãããŠã¿ãŠãæ軜ã«åºæ¥ãããšãåãã£ãã®ã§èªåååºæ¥ãããªäœæ¥ã¯ç©æ¥µçã«ãã©ã°ã€ã³åããŠãããããªãšæããŸããã
ãã©ã°ã€ã³åããåŸã¯ Git ãªããžããªã«ã¢ããããã ãã§ãªããNPM ã®ããã±ãŒãž ã GitHub Packages ãšããŠå ¬éããŠãããšãåŸã ãã©ã°ã€ã³ãå©çšããéã«äŸ¿å©ã§ãããŸããå ¬éããŠã©ã€ãã©ãªã®ã¹ã¿ãããèŠãã®ã¯æ¡å€æ¥œããéçºã®ã¢ãããŒã·ã§ã³ã«ãç¹ããã®ã§ãªã¹ã¹ã¡ã§ãã