AWS AmplifyでAPI(AppSync, GraphQL, Dynamo DB)を構築してReactで参照/更新する
前回の記事はこれ。
今回は以下のリファレンスに従って amplify add api
を試す。
https://aws-amplify.github.io/docs/cli/graphql
Schema ファイルの作成
src/graphql/schema/schema.gql
type Blog @model { id: ID! name: String! posts: [Post] @connection(name: "BlogPosts") } type Post @model { id: ID! title: String! blog: Blog @connection(name: "BlogPosts") comments: [Comment] @connection(name: "PostComments") } type Comment @model { id: ID! content: String post: Post @connection(name: "PostComments") }
@connection
というディレクティブが見慣れないが、 同ページの下の方に説明があるので読むと良い。
要はリレーションを構築する仕組みで、相互に同じ name
で紐付ける。
1対1, 1対多 のリレーションをサポートしているが、紐づけ用の中間テーブルの利用により多対多も実現可能。
テーブル/コード作成
設置したスキーマファイルを参照してコードまで生成。
-> % amplify add api ? Please select from one of the below mentioned services GraphQL ? Provide API name: amplifysample ? Choose an authorization type for the API Amazon Cognito User Pool Use a Cognito user pool configured as a part of this project ? Do you have an annotated GraphQL schema? Yes ? Provide your schema file path: src/graphql/schema/schema.gql -> % amplify push ? Do you want to generate code for your newly created GraphQL API Yes ? Choose the code generation language target javascript ? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes ? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
終わるとDynamoDBにテーブルが作成され、ローカルではこんな感じでファイルが生成されている。
-> % ls src/graphql/ mutations.js queries.js schema schema.json subscriptions.js
queries が参照系。mutations が更新系。subscriptions がサーバからのPUSH受け取る系(WebSocket使用)。
この分け方はGraphQLのお作法らしい。
データ作成
以下のコマンドで AppSync のクエリページに飛ぶ。
-> % amplify console api ? Please select from one of the below mentioned services GraphQL
※ 最初左下のVariablesペインに気づかなかった。
ここで色々とデータ入れる。
参照
src/graphql/queries.js
の中から使いたいクエリを選んで実行すれば良い。
import React from "react"; import "./App.css"; import { withAuthenticator } from "aws-amplify-react"; import { API, graphqlOperation } from "aws-amplify"; import * as Query from "./graphql/queries"; class App extends React.Component { constructor(props) { super(props); this.state = { postList: [] }; } listQuery = async () => { try { var ret = await API.graphql(graphqlOperation(Query.listPosts)); this.setState({ postList: ret }); console.log(this.state.postList.data.listPosts.items); } catch (e) { console.error(e); } }; componentDidMount() { this.listQuery(); }
更新
src/graphql/mutations.js
の中から使いたいクエリを選んで実行すれば良い。
import * as Mutation from "./graphql/mutations"; : : var ret = await API.graphql( graphqlOperation(Mutation.createPost, { input: {title: "ポストのタイトル"} }) );
AWS Amplify x React.js で ログイン機能(Cognito)追加
Amplify の authについて調べてたら、amplify add
コマンドを使用しないでAWS Consoleで手作業でCognitoの設定をしている記事が多かった。
なんか違う感があったので amplify add auth
使うとどうやって作れるかというのを雑にメモ。
Reactアプリ作成
$ npm install create-react-app $ create-react-app amplify-sample $ cd amplify-sample $ npm run start // 起動
デフォルトAppが立ち上がる
Amplify 組み込み
$ npm install --save aws-amplify $ npm install --save aws-amplify-react $ amplify init $ amplify add auth $ amplify add push
これやってからAWS Consoleに行くとUser Poolができてる
コード編集
--- a/src/index.js +++ b/src/index.js import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; +import Amplify from 'aws-amplify'; +import config from "./aws-exports"; +Amplify.configure(config);
--- a/src/App.js +++ b/src/App.js import React from 'react'; import logo from './logo.svg'; import './App.css'; +import { withAuthenticator } from 'aws-amplify-react'; function App() { return ( @@ -23,4 +24,4 @@ function App() { ); } -export default App; +export default withAuthenticator(App);
詰まったところ
configure の部分を以下のように書いている記事が多かったが、現状はaws-exports.js
を使うのが正しいっぽかった。ちゃんとドキュメント読めばよかった。
Amplify.configure({ Auth: { identityPoolId: 'ap-northeast-1:XXXXXXX', region: 'ap-northeast-1', userPoolId: 'ap-northeast-1_XXXXXX', userPoolWebClientId: 'XXXXXXXXX', } });
上の書き方で書いてたときはそれぞれが何を示すのか分からなかったので以下に一応メモ。
identityPoolId
は、Cognitoの「全般設定」のページの「プール ARN」の中のap-northeast-1:XXXXXXX(XXXXは数字)。
userPoolId
は同ページの「プール ID」。
userPoolWebClientId
は「アプリクライアントの設定」の中の「clientWeb」の方のID。
動作確認
AWS Console > Cognito > ユーザプール > ユーザとグループ > ユーザの追加 でユーザ追加。
npm run start
でアプリを立ち上げるとログイン画面になる。設定がうまく行ってればログインしてデフォルト画面が出るはず。
所感
FirebaseだとFirebase ConsoleのUI上で完結するところが、AmplifyだとCognito等のAWSサービスも意識しないといけない(Amplify Authみたいな機能ではない)のが若干敷居が高いかもしれない。
慣れの問題かもしれないけど。
Nuxt.jsプロジェクトでデバイスに応じて要素を出し分ける
画像サイズを、mobileなら64x64, それ以外なら128x128とかにしたかった。
こいつが便利だった。 nuxt-device-detect - npm
setupに書いてあるとおりですが、
yarn add nuxt-device-detect
してあげて
nuxt.config.js の modules に nuxt-device-detect を追記して
yarn run dev 再実行すれば使えます。
こんな感じで画像サイズのmobile, desktop 出し分け出来ました。
<p v-if="$device.isMobile" class="image is-64x64"> <img :src="user.image"> </p> <p v-else class="image is-128x128"> <img :src="user.image"> </p>
Nuxt.jsプロジェクトのプログラム上でURLを生成する
コード内で特定のルートへの絶対パスを取得するには以下のように書けば良い。
let path = this.$router.resolve({ name: 'page_name', params: { id: 1} }).href var fullUrl = window.location.origin + '/' + path
が、Nuxt.jsで生成したプロジェクトの pages/articles/_title.vue
的なページに遷移したかったのに、以下のコードだとエラーが出た。
let path = this.$router.resolve({ name: 'articles', params: { title: 'hogehoge'} }).href var fullUrl = window.location.origin + '/' + path
# エラー [vue-router] Route with name 'articles' does not exist
リファレンス見たら name の部分が間違ってるのが分かった。
pages/articles/_title.vue
のnameは articles-title
になる。
というわけで以下で解決。
let path = this.$router.resolve({ name: 'articles-title', params: { title: 'hogehoge' } }).href var fullUrl = window.location.origin + '/' + path
Vue.jsの v-bindで Unexpected 'v-bind' エラー
v-bind
でこんな感じで書いてたら
<input v-model="title" v-bind:class="{ 'is-danger': titleError}" class="input" type="text" placeholder="記事タイトル" >
エラーが出た
20:15 error Unexpected 'v-bind' before ':' vue/v-bind-style ✖ 1 problem (1 error, 0 warnings) 1 error and 0 warnings potentially fixable with the `--fix` option.
eslint に引っかかってた。eslint: v-bind-style
v-bind
省略したら大丈夫。
<input v-model="title" :class="{ 'is-danger': titleError}" class="input" type="text" placeholder="記事タイトル" >
lintに引っかかてるの分かりにくいなあと思ったので一応メモ。
Cloud Functions for Firebase から Cloud SQL上の MySQL を参照する
モチベーション
Firebaseを使ったプロジェクトでリレーショナルなDBを組むため、Cloud SQLと繋げてみることにした。
GCPにしたのはなんとなくfirebaseとの連携がラクそうなイメージだったから。
Cloud SQL以外で良い方法があるなら知りたいです。
Cloud SQL は第2世代インスタンスのMySQLを利用。
ほとんど公式リファレンスのリンク貼ってるだけだけど、手順メモとして残します。
※ 以下の記事にある通り、Function系サービスとRDBの相性は良くないので取り扱いには要注意
なぜAWS LambdaとRDBMSの相性が悪いかを簡単に説明する - Sweet Escape
やったこと
簡単な Cloud Function を ローカル実行 & deploy
とりあえず作業スペース確保と動作確認ということで。慣れてないので
Cloud SQL の準備
特筆すべきこともなく、公式ドキュメントなり、適当なブログを見ながらインスタンス作成。
Cloud SQL for MySQL のクイックスタート | Cloud SQL for MySQL | Google Cloud
ローカルのMySQL ClientからProxyを使って Cloud SQL に繋げる
これも公式ドキュメントに従って実施。
Cloud SQL Proxy を使用して MySQL クライアントに接続する | Cloud SQL ドキュメント | Google Cloud
Sequel Pro で繋いでテスト用のテーブルを作ったりした。
Cloud Function のコードを編集
ここにテストコードがあるので、コピペでおk
Connecting to Cloud SQL | Cloud Functions Documentation | Google Cloud
関数として実行したかったのでちょっと変えた。
- exports.mysqlDemo = (req, res) => { + exports.mysqlDemo = functions.https.onRequest((req, res) => {
テスト
以下のコード内のクエリが実行されれば成功です。
mysqlPool.query('SELECT NOW() AS now', (err, results) => { if (err) { console.error(err); res.status(500).send(err); } else { res.send(JSON.stringify(results)); } });
コマンドラインからテストします。
-> % firebase experimental:functions:shell i functions: Preparing to emulate functions. ✔ functions: helloWorld ✔ functions: mysqlDemo firebase > mysqlDemo.get(); Sent request to function. firebase > info: User function triggered, starting execution info: Execution took 926 ms, user function completed successfully RESPONSE RECEIVED FROM FUNCTION: 200, "[{\"now\":\"2019-01-09T21:38:48.000Z\"}]"
できてました。
あとはコード内のqueryをいじれば色々できました。
読書ログ_201611
なぜ、我々はマネジメントの道を歩むのか
マネージャーになった時に先輩からの薦めで読んだ。
結構前に読んだけど、マネージャーという役割が、ただの役職ではなく、非常に大きな責任を伴う仕事なのだと認識させられた。
マネージャーとして歩み始めるタイミングで読めて良かった。
文章も読みやすい。
部下を持ったら必ず読む 「任せ方」の教科書 「プレーイング・マネージャー」になってはいけない
部下を持ったら必ず読む 「任せ方」の教科書 「プレーイング・マネージャー」になってはいけない (ノンフィクション単行本)
- 作者: 出口治明
- 出版社/メーカー: 角川書店
- 発売日: 2013/11/22
- メディア: 単行本
- この商品を含むブログ (5件) を見る
これも先輩の薦めで読んだ。
具体的な仕事の任せ方に関して記載されている本。フレームワーク的な、仕事にすぐ使える考え方が書いてある。
結構前に読んだ本だけど、特に色褪せる内容ではないので今も仕事に活かせていると思う。
精神論も大事だけど行動が伴わないと部下からの信頼は得られないので、こういう本も読んでおいて良かった。
こういう内容は研修でも習うんだけど、研修のタイミングを初めての学びにするか反省/改善のタイミングにするかの違いは大きかった。
サーバント・リーダー 「権力」ではない。「権威」を求めよ
- 作者: ジェームズ・ハンター,James Hunter,高山祥子
- 出版社/メーカー: 海と月社
- 発売日: 2012/05/21
- メディア: 単行本(ソフトカバー)
- クリック: 3回
- この商品を含むブログ (1件) を見る
会社でちょっと話題になっていたので読んだ。
奉仕の精神こそ真のリーダーシップを生み出すという話。
「理想のリーダー像」と「キリスト教における行為としての愛」の共通点を述べ、「行為としての愛とはどのようなものか?」という視点でリーダシップを述べている。
ストーリー仕立てになっており、仕事と家庭に悩んだ管理職の男が主人公。
「仕事と家庭に悩んだ」というのが面白いところで、この本に記載されていることはマネジメント以外の対人関係でも有効と思われる。
印象に残ったのは以下の内容。
- 権力を行使するときは、なぜ使う必要があるのか考えるべき
- ときにリーダーは犠牲を払う
- 嫌いな人、苦手な人ほど愛することが出来る
- 考えより行動が大切
- 人の成長の4段階(「無意識の未熟」「意識して未熟」「意識して成熟」「無意識の成熟」)
- 欲求ではなくニーズを満たす(甘やかしではない)
- 人の前で怒れば全体からマイナス、人の前で褒めれば全体にプラス