- 投稿日:2020-03-17T20:47:25+09:00
Node.js/Expressのボディパーサーの仕様確認(エコーサーバー(echo-server))
背景/目的
クライアント側からのリクエストを各種パーサーがどのようなオブジェクトや配列にしてくれるのを確認することを目的にリクエストの情報をまとめてレスポンスにそのまま返却するようなエコーサーバーを作ってみました。
その他、HTTPクライアントのリクエストが想定通りになっているかを確認するのにも使えると思うので記録として残しておきます。仕様
リクエストの以下の情報をレスポンスJSONにまとめて返す。
- ヘッダー情報
- パス
- HTTPメソッド(Verb)
- クエリパラメータ
- リクエストボディ(ある場合のみ)
準備
npmとかnode.jsはインストールされている前提。
mkdir echo-server cd echo-server npm init -f npm install -y express touch index.js
ソースコード
↑で作ったindex.js
index.js'use strict'; const express = require('express'); const app = express(); const bodyParser = require('body-parser'); // Set body parser app.use(bodyParser.urlencoded({ extended: true, type: 'application/x-www-form-urlencoded' })); app.use(bodyParser.json({ type: 'application/json' })); app.use(bodyParser.raw({ type: '*/*' })); // All request process. app.use((req, res, next) => { res.json({ path: req.originalUrl.indexOf('?') === -1 ? req.originalUrl : req.originalUrl.substring(0, req.originalUrl.indexOf('?')), method: req.method, query: req.query, headers: req.headers, body: req.body ? (Object.keys(req.body).length ? req.body : undefined) : undefined }); }); // Create HTTP server. const http = require('http'); const server = http.createServer(app); const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000; server.listen(port); server.on('error', (err) => { console.error(err); }); server.on('listening', () => { console.log('listening on ' + port); });実行
node index.js
実行結果確認
GETリクエスト
以下のようなクエリパラメータ名にすると配列にしてくれるみたいです。
request$ curl -s -X GET -H "Authorization: Bearer dummmy" "http://localhost:3000/foo/bar?page=1&size=10&qarr[]=q1&qarr[]=q2&qarr[]=q3" | jq .response{ "path": "/foo/bar", "method": "GET", "query": { "page": "1", "size": "10", "qarr": [ "q1", "q2", "q3" ] }, "headers": { "host": "localhost:3000", "user-agent": "curl/7.68.0", "accept": "*/*", "authorization": "Bearer dummmy" } }POST(Json)
これはJSONなのでそのまま出る感じなのでさらっと。
request$ curl -s -X POST -H "Authorization: Bearer dummmy" -H "Content-Type: application/json" -d '{"foo":"foo-value", "bar": "bar-value", "arr": [{"sub": "sub1"},{"sub": " sub2"}]}' "http://localhost:3000/hogehoge" | jq .response{ "path": "/hogehoge", "method": "POST", "query": {}, "headers": { "host": "localhost:3000", "user-agent": "curl/7.68.0", "accept": "*/*", "authorization": "Bearer dummmy", "content-type": "application/json", "content-length": "81" }, "body": { "foo": "foo-value", "bar": "bar-value", "arr": [ { "sub": "sub1" }, { "sub": "sub2" } ] } }POST(フォーム)
Web画面(HTML)のフォーム送信(
Content-Type: application/x-www-form-urlencoded
)のケースです。
これは、obj[sub]=obj-sub-value
という名前の付け方をすると、以下のようなjsonにしてくれます。"obj": { "sub": "sub-value" }ネストしたオブジェクトの場合は
nested[sub1][sub2]=sub1-sub2
という名前の付け方をすると"nested": { "sub1": { "sub2": "sub1-sub2" } }つまり、各フォーム要素のname属性を意識してつければ、例えばBackendのデータベースがMongoDBのようなオブジェクト形式だった場合にサーバー側でFormをオブジェクトの形に整形(組み立てる)処理を書かなくても済むわけです。もちろんJSonスキーマだったり、バリデーションのチェックは必要ですが。。
request$ curl -s -X POST -H "Authorization: Bearer dummmy" -H "Content-Type: application/x-www-form-urlencoded" -d 'foo=foo-valoue&bar=bar-value&arr=arr1&arr=arr2&arr=arr3& obj[sub]=sub-value&nested[sub1][sub2]=sub1-sub2' "http://localhost:3000/foo/bar" | jq .response{ "path": "/foo/bar", "method": "POST", "query": {}, "headers": { "host": "localhost:3000", "user-agent": "curl/7.68.0", "accept": "*/*", "authorization": "Bearer dummmy", "content-type": "application/x-www-form-urlencoded", "content-length": "103" }, "body": { "foo": "foo-valoue", "bar": "bar-value", "arr": [ "arr1", "arr2", "arr3" ], "obj": { "sub": "sub-value" }, "nested": { "sub1": { "sub2": "sub1-sub2" } } } }
- 投稿日:2020-03-17T19:25:31+09:00
Docker-composeを使ってExpressの環境構築
Expressの環境構築
git clone https://github.com/Old-rever-brave/Express-Docker cd Express-Docker docker-compose up --build npm install npm start http://localhost:3000/
- 投稿日:2020-03-17T18:23:46+09:00
JavascriptのPromiseを解説します
User.query({where: {name:nm}, andWhere: {password: pw}}).fetch()これの戻り値が Promise オブジェクトが返ります。
(new Promise()).then(成功時の関数).catch(失敗時の関数)みたいな
- 投稿日:2020-03-17T16:33:44+09:00
Node.jsでDeprecationWarningを出さずにencodeとdecode
環境
$ node --version v12.16.1経緯
Buffer
を使って、encodeしたかった時にDeprecationWarning
が出てしまったため対処
encode自体は問題なくできるが、nodejsのドキュメント的には対応した方が良さそうtest.jsconst before = 'hogehoge' // これをbase64でencodeしたい const buffer = new Buffor(before) const after = buffer.toString('base64') console.log(after)↓
$ node test.js aG9nZWhvZ2U= (node:631) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.やったこと
new Buffer
の書き方を修正したtest.jsconst before = 'hogehoge' const after = Buffer.from(before).toString("base64"); console.log(after)↓ 無事エラーが消えた!
$ node test.js aG9nZWhvZ2U=ちなみに、decodeはこちら
test.jsconst after = 'aG9nZWhvZ2U' const before = Buffer.from(after, 'base64').toString() console.log(before)↓
$ node test.js hogehoge
参考
https://stackoverflow.com/questions/23097928/node-js-throws-btoa-is-not-defined-error
- 投稿日:2020-03-17T14:45:50+09:00
ログインなしでコードを共有できるサイトつくた
成果物
リポジトリ
https://github.com/yuzuru2/code_site
開発環境
- ubuntu 18.04
- docker
- docker-compose
使用ライブラリ周り
フロントエンド
- parcel
- bootstrap
- highlight.js
- react
- nginx(静的ファイルを配信)
バックエンド
- typescript
- nodejs
- pm2
データベース
- mongodb
成果物を使うケース
- ちょろっと書いたコードを誰かに見せたい時
UI
ホーム画面
コード閲覧画面
- 投稿日:2020-03-17T14:45:50+09:00
コード共有サイト作成 docker react node.js mongodb
成果物
リポジトリ
https://github.com/yuzuru2/code_site
開発環境
- ubuntu 18.04
- docker
- docker-compose
使用ライブラリ周り
フロントエンド
- parcel
- bootstrap
- highlight.js
- react
- nginx(静的ファイルを配信)
バックエンド
- typescript
- nodejs
- pm2
データベース
- mongodb
成果物を使うケース
- ちょろっと書いたコードを誰かに見せたい時
UI
ホーム画面
コード閲覧画面
- 投稿日:2020-03-17T14:45:50+09:00
コード共有サイトつくた
成果物
リポジトリ
https://github.com/yuzuru2/code_site
開発環境
- ubuntu 18.04
- docker
- docker-compose
使用ライブラリ周り
フロントエンド
- parcel
- bootstrap
- highlight.js
- react
- nginx(静的ファイルを配信)
バックエンド
- typescript
- nodejs
- pm2
データベース
- mongodb
成果物を使うケース
- ちょろっと書いたコードを誰かに見せたい時
UI
ホーム画面
コード閲覧画面
- 投稿日:2020-03-17T08:19:43+09:00
NFCシールを活用して自動打刻ツールを(個人的に)作ってみた話
はじめに
今回はNFCシールを使用して会社の自動打刻システムを、完全に自分用で作ります!
要件定義
なぜつくるか?
現在、弊社の勤怠は、エクセルで管理されています。
実際の打刻フローとしては、
- 出社したら出社時刻をエクセル開いて手動で打刻
- 保存
- 退社するときに退社時刻をエクセル開いて手動で打刻
- 保存
。。。
毎日エクセルポチポチするの面倒すぎる!!!!!!!!!作業自体も面倒なのに、何日か打刻を忘れるとまあ面倒臭いことになります。
せっかくIT企業にいるんだからいろいろスマートにやりたい...
ということで、今回の自動打刻システムの開発を決意しました。どう作るか?
今回開発する自動打刻システムでは、NFCシールを活用していきます。
処理の流れとしては、
- NFCシールにスマホをかざして専用のWEBサイトを表示する
- WEBサイトから自動打刻システムにリクエストを投げる
- 打刻する
といった感じにしようかと思います。
NFCシールにスマホかざすのとリクエストを投げるところにWEBサイト表示をはさんでいるのは、
意図しない打刻を防ぐためです。クライアント側で打刻される時刻の確認、出社なのか退社なのかの選択できた方が、
手順は増えますが確実かなあということでワンクッションはさみました。また、現状では個人用なのでユーザーの識別は行いません。
どう使うか?
想定される使用フローは下記のとおりです。
- 出社したらデスクのどこかしらに貼ったNFCシールにスマホをかざす
- 表示されるWEBサイトで打刻時間を確認、出社ボタンを押す
- (退社時も同じ)
かなりスマート...!(な気がする)
使用技術
インフラはAWSの各サービスを利用します。
メイン処理の部分にはLambda、エクセルファイル(勤怠管理)はS3に保存します。
サーバーサイドにnode.js、WEBフロントエンドにはVue.jsを使用しつつ、HTTP通信はaxiosを使用します。
なぜNFCシールを使うか?
ただ使ってみたかった。
実は今回の開発、個人的にNFCシールを使ったアプリを作ってみたかったので、NFCシールありきで考えていました。
スマホかざすだけで打刻できるのステキじゃん...
なぜnode.jsか?
一番の理由は、Lambdaがnode.jsで書けるからです笑
他にも
java, ruby, pythonなどなど、いろんな言語で書くことができます。フロントエンド開発に興味があり、日頃からJavaScriptを勉強しているので、
サーバーサイドもJavaScriptで書こう!ということでnode.jsを選びました。なぜLambdaか?
Lambdaとは、AWSが提供するサーバレスアーキテクチャを構築するためのサービスです。
通常は、EC2インスタンスは常時存在し、アプリケーションも常時起動されているのですが、Lambdaはリクエストが送られてきたときのみインスタンスを生成→アプリケーションを実行→インスタンスを破棄という挙動をします。
今回作成するアプリは常時起動している必要もないので、コストを抑える意味でもLambdaが適しているのではないかと考えました。
なぜVue.jsか?
個人的に使い慣れているのでフロントはVue.jsで書きます。
と言っても、現在時刻を表示するのと、ボタンを二つ配置するだけなので全く難しいことはしません笑
強いて言えば、ローディングのアニメーションを作り込むくらいでしょうか...。
HTTP通信はaxiosを使用します。
設計
アーキテクチャ図
アーキテクチャの全体像としては、下記の通りです。
構成は至ってシンプルで、
HTTPリクエストをAPI Gatewayで受け付け、Lambdaに投げます。勤怠を管理しているエクセルはS3においておき、Lambdaからそのエクセルファイルに書き込みをしていく感じです。
処理が完了すると、処理結果をSuccessかFailでクライアントに通知します。
アプリケーションの実装
自動打刻システム(node.js)実装
コードの全貌は下記のGitHubリポジトリを御覧ください。
GitHub リポジトリ
エクセルファイルの操作には、「xlsx-populate」というライブラリを使用しました。
最初は「xlsx」というライブラリを使って実装していましたが、このライブラリだと処理をして、保存するとマクロや書式が無効化された状態になってしまうのでつかえず...。
個人的にはドキュメントも「xlsx-populate」のほうが読みやすかったです!
処理としてはファイルを読み込んで、シートを指定して、セルを指定して値を書き込み、保存しているだけです。弊社の勤怠表は月ごとにシートが分かれているので、処理の頭でDateオブジェクトを生成して、得られた各値でシートや記入するセルを判定しています。
また、弊社は30分ごとに勤務時間として打刻できるので、打刻する時刻を30分単位に変換する関数を用意しています。
今後もっと本格的に運用していくことになったら、このあたりで拡張の余地がありますね。
実装で苦労したのは非同期処理とAWS S3からファイルを取得して、書き込んだものをアップロードし直す処理のところ。
const params = { Bucket: 'バケット名', Key: 'キー' } s3.getObject(params, (err, data) => { }上記のように記述すれば、指定されたバケットのオブジェクト(ファイル)を取得できて、data変数に格納されます。
また、アップロードするときは、
const params = { Bucket: 'バケット名', Key: 'キー', Body: 'アップロードしたいファイル' } s3.putObject(params, (err, data) => { })でアップロードできます!
ここがnode.jsの情報がなかなか転がってなくて苦労しました。
取得も書き込みも注意点としては、取ってきたり送信するためには、データ形式に気をつけなければなりません。
今回僕は、これらの処理の前後にエクセルファイルをバッファーに変換する処理をはさみ、変換したものをparams変数のBodyとしています。
クライアントサイド(vue.js)実装
クライアントサイド(WEB)はVue.jsで作りました。
最終的には静的サイトとしてビルドして、Netlifyでホスティングします。
こちらは特に難しいことはしていません。
UIはVuetifyを使ったので適当に作った割には整っています。
スクショですが、下記のようになりました。
打刻すると、vue-loading-templateを使用したアニメーションが流れて、レスポンスが帰ってくるとアラートが表示されます。
(若干左によってるのはスクショが下手だからです...笑)
インフラ環境構築
構築したもの
いよいよインフラの構築に入ります。今回は、メインのAPIをLambdaで動かします。
勤怠表(エクセルファイル)はS3にアップロードしておき、Lambdaから読み取り、書き込みを行います。
HTTPリクエストの受け口として、APIGatewayを配置します。
ここに想定されるリクエストがとんできたら、それをトリガーにLambda関数が動く仕組みにしていきます。
また、クライアントサイド(WEBアプリ)はNetlifyという静的サイトのホスティングサービスを利用します。
Netlifyに関しては後日別記事で言及します。
ハマったポイント
私はインフラ超初心者なので、インフラ構築でかなりつまづきました...。
「LambdaからS3のファイルをとってこれない」
作成したLambdaに正しくロールを付与していなかったため、アクセス権限 is 何の状態が1時間くらい続きました...。
「Lambda関数(メインAPI)が非同期処理になっていて肝心の処理を行う前にLambdaが終了してしまっていた」今回使用した「xlsx-populate」は非同期処理をすることが前提のライブラリです。
恥ずかしながら、node.jsだけでなく非同期処理の知識も乏しく、Lambdaはエラーなく終了するのに肝心の処理が実行できてない...。
という状態で約5日間潰しました。
エラー箇所の切り出しが下手だったなあと反省しています。
いろんな記事や書籍を読み漁りつつ、async awaitを駆使してなんとか解決しました。
「CROS」
実際にクライアントサイドからリクエストを投げる時にはまりました。
今までなーんとなくしか理解してなかったですが、これを機にしっかり学べました。
CROSに関しては別記事でまとめます。
実際につかってみた
明日から出社と退社が楽しみになりそう。
(ちょっと処理は遅いですが)個人的にほぼノンストレスに打刻できるようになったので満足です。
ただ、本当にきちんと勤怠をつけるためには小難しい会社のルールがあるみたいなので、そのうちきちんとしたものも作りたいです。
今のところはほぼ毎日きっちり定時に退社しているので細かい調整はそんなに必要なさそう...だと思ってます笑
おわりに
NFCさいこう!!!!!たのしい!!!!!
- 投稿日:2020-03-17T01:42:18+09:00
2020年から始めるAzure Cosmos DB - JavaScript SDK (SQL API)を見てみる (Part.1)
この記事について
本記事は、2020年3月6日 (米国時間) にて、Azure Cosmos DB に新しく Free Tier (無償利用枠) が登場したことに伴い、改めて Azure Cosmos DB を色々と触っていく試みの 3 回目です。
今回は、前回記事 にて作成した CRUD アプリ内で使用している Microsoft Azure Cosmos JavaScript SDK について見ていきたいと思います。対象読者
- Azure Cosmos DB について学習したい方
- Node.js で Azure Cosmos DB への CRUD 操作を行いたい方
- Microsoft Azure Cosmos JavaScript SDK の動作について理解したい方
Microsoft Azure Cosmos JavaScript SDK
実際に、Microsoft Docs の内容を元に、JavaScript SDK (SQL API) の中身を見ていきます。
今回は Azure Cosmos DB に接続する際に生成する、CosmosClient について確認します。CosmosClient
TypeDoc の記載は、以下の通りです。
Provides a client-side logical representation of the Azure Cosmos DB database account.
This client is used to configure and execute requests in the Azure Cosmos DB database service.
Azure Cosmos DBデータベースアカウントのクライアント側の論理表現を提供します。
このクライアントは、Azure Cosmos DBデータベースサービスで要求を構成および実行するために使用されます。const client: CosmosClient = new CosmosClient({ endpoint, key });上にある通り、endpoint と key を使用してインスタンスを作成しています。
- endpoint: Azure Cosmos アカウント URI、
- key: Azure Cosmos アカウントのプライマリキー or セカンダリキー
どのコンストラクタが動いているのかを確認すると
(alias) new CosmosClient(options: CosmosClientOptions): CosmosClient (+1 overload) import CosmosClientとあり、
CosmosClient(options: CosmosClientOptions)
が動いているようです。
中身を確認してみます。Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/CosmosClient.ts/** * Creates a new {@link CosmosClient} object. See {@link CosmosClientOptions} for more details on what options you can use. * @param options bag of options - require at least endpoint and auth to be configured */ constructor(options: CosmosClientOptions); // tslint:disable-line:unified-signaturesあれ、1行しかない?と思ったらすぐその下に
constructor(optionsOrConnectionString: string | CosmosClientOptions)
があったので焦りました。。
このコンストラクタは長いので、部分ごとに見ていきます。コンストラクタ処理 (1)
if (typeof optionsOrConnectionString === "string") { optionsOrConnectionString = parseConnectionString(optionsOrConnectionString); }これは単純に、コンストラクタの引数が
string
かCosmosClientOptions
かを判別しています。
引数が string の場合は、if 文の中で接続文字列を使って CosmosClientOptions を生成し、 optionsOrConnectionString に代入しています。
コンストラクタの引数が string と CosmosClientOptions と異なっているので、お決まりな感じの処理です。
コンストラクタの引数の型が違うからと言い、内容がほとんど重複するようなコンストラクタをしっかり分けて書く人をたまに見かけます。このコードは「そんな無駄なことしなくていいよ」と教えてくれるいい例ですね。コンストラクタ処理 (2)
optionsOrConnectionString.connectionPolicy = Object.assign( {}, defaultConnectionPolicy, optionsOrConnectionString.connectionPolicy );ここでは、CosmosClientOptions の
connectionPolicy
に値を設定しています。
その前に、CosmosClientOptions って何者だ?、という疑問があるので、先にこちらを見てみます。Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/CosmosClientOptions.tsexport interface CosmosClientOptions { endpoint: string; key?: string; resourceTokens?: { [resourcePath: string]: string }; tokenProvider?: TokenProvider; permissionFeed?: PermissionDefinition[]; connectionPolicy?: ConnectionPolicy; consistencyLevel?: keyof typeof ConsistencyLevel; defaultHeaders?: CosmosHeaders; agent?: Agent; userAgentSuffix?: string; plugins?: PluginConfig[]; }CosmosClientOptions には 11 個のプロパティが定義されています。どうも CosmosClientOptions の中に endpoint や key をはじめとした Azure Cosmos DB を利用するための各種設定値が格納されるようです。
ちなみに TypeScript では、他の言語ではメンバー変数やフィールドと呼ばれる、名前を持ち、指定された型のデータを保持するものをプロパティといいます。この CosmosClientOptions のプロパティの 1 つに
ConnectionPolicy
がありますので、先ほどの処理はこのプロパティの値を設定している部分と理解しました。
ConnectionPolicy は何かというのを確認するために、さらに中身をみてみます。Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/documents/ConnectionPolicy.tsexport interface ConnectionPolicy { connectionMode?: ConnectionMode; requestTimeout?: number; enableEndpointDiscovery?: boolean; preferredLocations?: string[]; retryOptions?: RetryOptions; useMultipleWriteLocations?: boolean;ConnectionPolicy の中には、
connectionMode
やrequestTimeout
などの接続に関するポリシー設定があるようです。(これ以上、クラスを深くみると大変なので、一旦ここまでにします。)一旦元に戻って、
(再掲)optionsOrConnectionString.connectionPolicy = Object.assign( {}, defaultConnectionPolicy, optionsOrConnectionString.connectionPolicy );をみると、
Object.assign
を使用しています。
Object.assign() が何かについては、こちら を参照してください。
つまり、CosmosClientOptions の ConnectionPolicy プロパティ内で未定義となっているものについて、デフォルト値を代入している感じです。コンストラクタ処理 (3)
optionsOrConnectionString.defaultHeaders = optionsOrConnectionString.defaultHeaders || {}; optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.CacheControl] = "no-cache"; optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.Version] = Constants.CurrentVersion; if (optionsOrConnectionString.consistencyLevel !== undefined) { optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.ConsistencyLevel] = optionsOrConnectionString.consistencyLevel; } optionsOrConnectionString.defaultHeaders[Constants.HttpHeaders.UserAgent] = getUserAgent( optionsOrConnectionString.userAgentSuffix );この
optionsOrConnectionString.defaultHeaders
は、CosmosHeaders クラスです。
中身を見てみます。Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/queryExecutionContext/CosmosHeaders.tsexport interface CosmosHeaders { [key: string]: string | boolean | number; }キー(key) と キーに紐づく値(value) を格納できるようにしています。いわゆる連想配列の部分です。
連想配列をインタフェースで定義するという使い方もあるんですね。勉強になります。つまり、ここの処理は、HTTP リクエストの各種ヘッダー情報を CosmosHeaders クラス (連想配列) を使って設定しているという感じです。
コンストラクタ処理 (4)
const globalEndpointManager = new GlobalEndpointManager( optionsOrConnectionString, async (opts: RequestOptions) => this.getDatabaseAccount(opts) );新しい
GlobalEndpointManager
というヤツが出てきました。
GlobalEndpointManage にある、どのコンストラクタが動いているのかを確認すると(alias) new GlobalEndpointManager(options: CosmosClientOptions, readDatabaseAccount: (opts: RequestOptions) => Promise<ResourceResponse<DatabaseAccount>>): GlobalEndpointManager import GlobalEndpointManagerが動いているようです。中身を確認します。
Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/globalEndpointManager.tsexport class GlobalEndpointManager { private defaultEndpoint: string; public enableEndpointDiscovery: boolean; private isRefreshing: boolean; private options: CosmosClientOptions; private preferredLocations: string[]; constructor( options: CosmosClientOptions, private readDatabaseAccount: (opts: RequestOptions) => Promise<ResourceResponse<DatabaseAccount>> ) { this.options = options; this.defaultEndpoint = options.endpoint; this.enableEndpointDiscovery = options.connectionPolicy.enableEndpointDiscovery; this.isRefreshing = false; this.preferredLocations = this.options.connectionPolicy.preferredLocations; } }ナンダコレ、、、
となったので、TypeDoc を見てみます。This internal class implements the logic for endpoint management for geo-replicated database accounts.
この内部クラスは、地理的に複製されたデータベースアカウントのエンドポイント管理のロジックを実装します。ああ、そういうことか! とこの絵を思い出しました。
Azure Cosmos DB で、これ忘れたらダメなヤツやん。。Microsoft Docs にある Azure Cosmos DB の概要 にも一番最初に
Azure Cosmos DB は、Microsoft によってグローバルに配布されるマルチモデル データベース サービスです。
とあります。
Cosmos DB では、Cosmos アカウントに複数リージョンを関連付けさせることができ、関連付けられたすべてのリージョンにデータがシームレスにレプリケートされるようになっています。これはそれに関連する設定まわりの処理と認識しました。コンストラクタ処理 (5)
this.clientContext = new ClientContext(optionsOrConnectionString, globalEndpointManager);クライアントコンテキストがやっと出てきました。
どのコンストラクタが動いているのかを確認すると、(alias) new ClientContext(cosmosClientOptions: CosmosClientOptions, globalEndpointManager: GlobalEndpointManager): ClientContext import ClientContextが動いているようです。
Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/ClientContext.tsexport class ClientContext { private readonly sessionContainer: SessionContainer; private connectionPolicy: ConnectionPolicy; public partitionKeyDefinitionCache: { [containerUrl: string]: any }; public constructor( private cosmosClientOptions: CosmosClientOptions, private globalEndpointManager: GlobalEndpointManager ) { this.connectionPolicy = cosmosClientOptions.connectionPolicy; this.sessionContainer = new SessionContainer(); this.partitionKeyDefinitionCache = {}; } }(クライアントコンテキストについては、説明するまでもないと思いますが) いわゆる、コンテキストの伝播 とか言われているモノです。
ざっくり言うと、クライアントに関連する情報を保持している場所
と私は思っています。メソッドの引数にクライアント情報をいちいち与えずに、全部ここを見ましょうよ、的なやつです。(適当)
クライアントコンテキストについて、良い説明となる資料を見つけられなかったので、見つけたら追記したいです。コンストラクタ処理 (6)
this.databases = new Databases(this, this.clientContext); this.offers = new Offers(this, this.clientContext);やっと最後の処理です。
Databases
は、新しいデータベースの作成、およびすべてのデータベースの読み取り/クエリの操作を行うクラスです。
Offers
については、聞きなれない単語だと思います。(筆者もちゃんと理解しているわけではありませんが) REST経由で、SQL APIを使用する際に登場するもののようです。
Offer resource というものがあるんですね。別途、学習を進めたいと思います。さいごに
今回は、Azure Cosmos DB に JavaScript/Node.js で接続する際に最初に生成される、CosmosClient について、中身を確認してみました。
今回の内容は、前回、実際に CRUD アプリを作成 (前回記事) した時はたったの 1 行で終わってしまった内容です。普段のアプリ開発では、あまり意識しない世界なのかもしれませんが、
実際にコンストラクタの中で何が行われているのかを確認する
ことは、Azure Cosmos DB の仕組みや使用するライブラリへの深い知見を得る ためには必要な事かな、と思いました。次回は、
Database
クラスについて見ていこうと思います。関連リンク
前回記事
参考情報
npm
GitHub
Microsoft Docs
developer.mozilla.org
- 投稿日:2020-03-17T00:54:47+09:00
mockyでリクエストの内容をレスポンスに反映しようとしてハマったメモ。
やりたいこと
node.jsで動くWebAPIモックサーバーのmockyを使って、
{ "name": "John Smith" }をPOSTリクエストしたら
{ "id": 1, "name": "John Smith" }が返却され、
{ "name": "Taro Yamada" }をPOSTリクエストしたら
{ "id": 1, "name": "Taro Yamada" }が返却されるように、リクエストの内容をレスポンスに反映したかったのだが、つまづいたのでメモを残す。
mockyのインストール方法、基本の使い方はGithubのREADMEを参考にしてください。結論
基本の使い方は公式のREADMEといいつつ、mocky自体かなりメンテされていないみたいなので少し書き方は今風にしてる箇所もあるが基本的に一緒。
mock.jsconst mocky = require('mocky'); mocky.createServer([ { url: '/users', method: 'POST', res: (req, res, callback) => { const params = JSON.parse(req.body); callback(null, { status: 201, body: JSON.stringify( { "id": 1, "name": params.name } ) }); } } ]).listen(4321);
JSON.parse
でリクエストのjsonをパースしてあげないとうまく変数を抽出できなかったり、JSON.stringify
でjson形式で書いたレスポンスをstring型に変換しないとうまく動作しない。(ここでつまづいた...?)
特にJSON.stringify
を忘れたために、_http_outgoing.js:670 throw new ERR_INVALID_ARG_TYPE('first argument', ^ TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer. Received an instance of Object at write_ (_http_outgoing.js:670:11) at ServerResponse.write (_http_outgoing.js:638:15) at sendRes (/app/node_modules/mocky/lib/mocky.js:166:9) at /app/node_modules/mocky/lib/mocky.js:74:10 at Timeout._onTimeout (/app/mocky.js:16:9) at listOnTimeout (internal/timers.js:549:17) at processTimers (internal/timers.js:492:7) { code: 'ERR_INVALID_ARG_TYPE' }というエラーが出続けて泣いた?
動作確認
これでcurlで確認すればうまくいくはず。
$ curl -X POST 'http://localhost:4321/users' -d '{ "name": "John Smith" }' {"id":1,"name":"John Smith"}みにくいので
python -m json.tool
をパイプで渡すと見やすくなる。$ curl -X POST 'http://localhost:4321/users' -d '{ "name": "John Smith" }' | python -m json.tool { "id": 1, "name": "John Smith" }Reference
- 投稿日:2020-03-17T00:49:58+09:00
入門の次のステップに進めないVue.js学習履歴(随時更新)
概要
ドットインストールの「Vue.js入門」を実施してある程度Vue.jsをわかった気になった。
https://dotinstall.com/lessons/basic_vuejs_v2しかし、実際にリリースされているVue.jsのソースを見るとさっぱりわからなかった。
このため、疑問点と調査経緯を自分のメモ目的でこの記事に残していく。疑問点
yarn run xxx
package.jsonのscriptsで定義されたxxxを実行する。
"scripts": { "xxx": "~~~~~~~~", ←これを実行 "yyy": "~~~~~~~~", : : },■yarn runのドキュメントはこちら。
https://classic.yarnpkg.com/ja/docs/cli/runapp.use(nuxt.render)
expressのミドルウェアとしてNuxt.jsを使う。
const express = require('express') const app = express() : app.use(nuxt.render) :Node.jsの「ミドルウェア」という概念がいまいちわかっていない・・・。
■API: nuxt.render(req, res)
https://ja.nuxtjs.org/api/nuxt-render/■ExpressのミドルでNuxt.jsを利用
https://www.wakuwakubank.com/posts/666-nuxtjs-express-middle/module.exports={}
外部(別ファイル)から参照できるようにする。
module.exports = { mode: 'xxx', router: { base: '/yyy/' },上記の定義をしたjsファイルをrequireすると、jsファイルの中身を参照できる。
■module.exportsとは何か、どうもわからなかったので実験してみた〜Node.jsにて外部moduleをrequireする〜
http://karoten512.hatenablog.com/entry/2018/01/28/191928this.$store.dispatch(~~~)
「$store」はどこにも宣言されていない。
Vuex(ビューックス?)のステートオブジェクトとのこと。
セッションみないたもの??セッションよりは奥が深そう。■Vuexステート
https://vuex.vuejs.org/ja/guide/state.html■Vue.js + Vuexでデータが循環する全体像を図解してみた
https://qiita.com/m_mitsuhide/items/f16d988ec491b7800acethis.$store.dispatch(~~~)その② dispatchについて
「$store.dispatch」でステートのactionを実行(らしい)
actionは非同期処理(らしい)actionの実行とは何か?
this.$store.dispatch(~~~)その③ actionについて
「$store.dispatch」でステートのactionを実行するとしてactionとは何をするのか?
実態が不明。
\store\index.js
にexport const actions
やexport const mutations
があった。
つまり「$store.dispatch」を実行すると、\store\index.js
にexport const actions
のような気がしてきた。async function() {const response = await this.$store.dispatch(~~~)}
asyncで非同期関数。
awaitでpromiseのresolveを実行。
つまり、\store\index.js
のactionを非同期実行した結果がresponseに入る?
※このあたり、理解が曖昧。以下などを見て、なんとなく上記の理解。https://qiita.com/niusounds/items/37c1f9b021b62194e077
() => {~~~}
何もない
()
はJavaScriptで1,2を争う理解できない記述。
catchは例外をキャッチするんだろうな~という何となく想像ができるがその後の() =>
は全く想像できない。具体的なソースだとこんな感じ。const response = await this.$store .dispatch('xxx', { key1: valu1, key2: value2 }) .catch(() => { ←ここがわからない。 return false })こちらがわかりやすかった。
https://qiita.com/may88seiji/items/4a49c7c78b55d75d693b(引数,...)=>{...関数の本体...}つまり
() => {~~~}
は引数なしの無名関数を宣言しているだけ。
上記具体例だと、catchが呼ばれたらfalseをreturnする無名関数を実行する、ということか。