todoアプリのモックを30分くらいでAWS CDK(with TypeScript)とNuxt.jsで作成する方法
はじめに
最近、自分でプロダクト開発を行ってみようかなと思っているので、「なるべく小さく動く」「他人と分担しやすい」フロントエンドとバックエンド環境を作る方法について棚卸ししてみました。
基本的な方針として、なるべくブラックボックスを作らないようにします。
例えば、 create-nuxt-app
を使うことで使わないディレクトリを作ったりはなるべくやらずに、 npm i --save nuxt
mkdir pages
という感じで作り上げていく感じですね。ただ、cdk init
は使います。
とりあえずCDKとかNuxt.js始めてみたいけど、なるべく小さく始めてみたい、という人の参考になれば、と思います。
30分くらいあればこの記事でやったことは出来ると思います。
結論
こんな感じでやれます
この記事を書くにあたってコーディングしてみた模様を動画で公開しているものです
記事の内容とは若干違うのですが、雰囲気を掴む手助けにはなるかと思います。
前提
- AWSアカウントを所有していること
- CDKのCLIが利用できる状態になっていること
- デフォルト、もしくは何かしらのプロファイル設定なども済んでいること(つまりいきなりcdk initとかできる状態になっていること)
- npmが利用できる状態になっていること
- CDKにはTypeScriptを利用する
- Nuxt.jsはSPAモードで動作する
やってみたこと
とりあえずLambdaが動くようにする
CDKのスケルトン作成&初期設定
- CDKのスケルトンを作成&必要なライブラリのインストール
mkdir back
cd back
cdk init --language typescript
npm init
cdk bootstrap
npm run watch
- ビルド&デプロイ
npm run build; cdk deploy
ここまでで、とりあえずスタックが作成された状態になります。
Lambdaのアプリケーションコードを書く
- 必要なライブラリやファイルを追加
npm install --save @aws-cdk/aws-lambda
mkdir -p src/lambda/
code src/lambda/index.ts
export const handler = async ( event:any = {} ) : Promise<any> => {
const res = {
content: { "id": "1", "task": "dummy" }
}
return {
statusCode: 200,
body: JSON.stringify(res),
headers: {
"Content-Type":"application/json",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": '*',
"Access-Control-Allow-Methods": "OPTIONS,GET"
}
}
}
LambdaのCDKコードを書く
- CDKのコードを修正
code lib/back-stacks.ts
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
export class TestCdk1Stack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// lambda
const getTodos = new lambda.Function(this, "getTodosLambda", {
runtime: lambda.Runtime.NODEJS_14_X,
handler:"index.handler",
code: lambda.Code.fromAsset("src/lambda")
})
}
}
npm run build; cdk deploy
とりあえず、ここまでで次を返すLambda関数ができました
content: { "id": "1", "task": "dummy" }
REST API化(API Gateway経由でLambdaを叩けるようにする)
LambdaをHTTP経由で実行し、結果を得られるようにしましょう。
npm install --save @aws-cdk/aws-apigateway
code lib/back-stacks.ts
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apigateway from '@aws-cdk/aws-apigateway';
export class TestCdk1Stack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// lambda
const getTodos = new lambda.Function(this, "getTodosLambda", {
runtime: lambda.Runtime.NODEJS_14_X,
handler:"index.handler",
code: lambda.Code.fromAsset("src/lambda")
})
// apigateway
const getTodosApi = new apigateway.RestApi(this, "getTodosApi", {})
getTodosApi.root.addMethod("GET", new apigateway.LambdaIntegration(getTodos));
}
}
npm run build; cdk deploy
cdk deployの実行結果にAPI GatewayへのURLが入ってきます。そのURLを叩くと getTodosLambda
の実行結果が返ってくるというモックAPIが出来上がった状態になります。
とりあえずNuxt.jsが動くようにする
バックエンドのモックができたのでフロントエンドから呼び出せるようにしていきましょう。
なるべく段階を踏んで。
最低限起動するだけ
mkdir front
cd front
npm init
code package.json
package.jsonのscripts.devに "dev": "nuxt --spa"
を追加
{
"name": "front",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1",
"dev": "nuxt --spa"
},
"author": "",
"license": "ISC"
}
npm i --save nuxt
mkdir pages
touch pages/index.vue
code pages/index.vue
<template>
<div>
Hello Nuxt.
</div>
</template>
npm run dev
これで、とりあえず「Hello Nuxt」と表示するだけの静的ページが作成できた状態になります。
イベントハンドラを設定してみる
次に、動的なサイトにしていく一歩を踏み出します。
ボタンを設置して、押したらページの表示が切り替わるようにします。
- イベントハンドラを追加する
code pages/index.vue
<template>
<div>
<button v-on:click="clicked">click me</button>{{text}}
</div>
</template>
<script>
export default {
data(){
return {
text: "hoge"
}
},
methods: {
clicked: function() {
this.text = "huga"
}
}
}
</script>
npm run dev
これで、とりあえずボタンでリアクティブに内容が切り替わる動的なページが作成できました。
APIを叩くようにしてみる
次に、外部とのデータを入出力できるように、つまりAPIを利用できるようにしていく一歩を踏み出します。
ただ、APIがおかしいのかフロントエンドの実装がおかしいのか切り分けるために、インターネット上で公開されているAPIモックを利用して、フロントエンドの実装だけ作りましょう。
- モックAPIを叩くようにする(with JSONPlaceHolder)
code pages/index.vue
<template>
<div>
<button v-on:click="clicked">click me</button>{{text}}
</div>
</template>
<script>
export default {
data(){
return {
text: "hoge"
}
},
methods: {
clicked: async function() {
fetch(url, { mode: 'cors' })
await new Promise( (resolve) => {
setTimeout( () => {
resolve();
} , 3000)
} )
await fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => this.text = json)
}
}
}
</script>
- URLを差し替えて見る(さっき作ったオリジナルのヤツ)
code pages/index.vue
<template>
<div>
<button v-on:click="clicked">click me</button>{{text}}
</div>
</template>
<script>
// デプロイしたAPI GatewayのURL
const url = "https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/todos"; // 先程作成したAPI GatewayのURLを入れる
export default {
data(){
return {
text: "hoge"
}
},
methods: {
clicked: async function() {
await fetch(url)
.then( response => response.json() )
.then( json => this.items = json)
},
}
}
</script>
これで、インターネット上のデータをやりとりできる動的なページが作成できました。
おわりに
今まではいろいろなサイトをググったりしてましたが、面倒なのでよくやることを記事化して一本化してみました。
だれかの参考になれば幸いです。
自分が使いそうだけど。
Discussion