- 投稿日:2020-11-27T23:58:05+09:00
nodeのexpressをtsで作って無料でazureに公開したメモ
概要
Azure で Node.js Web アプリを作成するを試してみたメモ。
ついでにTypescriptでトランスパイルするところまで。nodeアプリの作成
- expressで文字列を返すだけのアプリを作成する
npm run start
で開始して、http://localhost:3000/api/hello で文字列がかえることを確認。Azureに公開
VS Code 拡張機能をインストール
以下の拡張をインストール。
サブスクリプションとの紐づけを行っておく。azureに公開
無料プランで作成する。
VSCodeからデプロイ
公開後の確認
https://az-node-app.azurewebsites.net/api/hello
typescriptの導入
ミニマムに入れてみる。
package.json{ "name": "node-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node ./bin/www", + "build": "tsc", + "watch": "tsc --watch" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1", "uuid": "^8.3.1" }, "devDependencies": { + "@types/express": "^4.17.9", + "typescript": "^4.1.2" } }tsconfig.json{ "compilerOptions": { "target": "es2020", "module": "commonjs", "lib": ["es2020"], "outDir": "./dist", "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": [ "src/**/*" ], }src/app.tsimport express from 'express' var app = express(); app.get('/api/hello', (req, res) => { return res.json('hello world!!') }) export default appbin/www- var app = require('../app'); + var app = require('../dist/app').default; var http = require('http');手元でビルドしたものをデプロイするように修正
デプロイするだけのわりに時間がかかるなと思っていたら、buildスクリプトは自動で走るらしい。(参照:node.jsアプリの構成)
今回はミニマムで行きたいので、とりあえずその設定をオフにした。オフにしたといってもbuildスクリプトの名前変えただけ。
ついでに余分なディレクトリはデプロイされないようにした。
npm install --production
がされるようにしたいけれど、それをするにはまた別の手順が必要そう。今回はパス。package.json"scripts": { "start": "node ./bin/www", - "build:" "tsc", + "tsc": "tsc", "watch": "tsc --watch" },.vscode/settings.json{ "appService.zipIgnorePattern": [ "node_modules{,/**}", ".vscode{,/**}", "src{,/**}" ] }lintの設定
typescriptを入れるならついでに入れたくなるのがlinter。
package.json"devDependencies": { "@types/express": "^4.17.9", + "@typescript-eslint/eslint-plugin": "^4.8.2", + "@typescript-eslint/parser": "^4.8.2", + "eslint": "^7.14.0", + "eslint-config-prettier": "^6.15.0", + "eslint-plugin-prettier": "^3.1.4", + "prettier": "^2.2.0", "typescript": "^4.1.2" }
.vscode/extensions.json{ "recommendations": [ "ms-azuretools.vscode-azureappservice", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "editorconfig.editorconfig" ] }.vscode/settings.json{ "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.tabCompletion": "on", "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact" ], "typescript.format.enable": false, "javascript.format.enable": false, }.eslintrc.jsmodule.exports = { ignorePatterns: ['!.eslintrc.js'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:prettier/recommended', 'prettier/@typescript-eslint', ], plugins: ['@typescript-eslint'], parser: '@typescript-eslint/parser', env: { browser: true, node: true, es6: true, jest: true, }, parserOptions: { sourceType: 'module', }, rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', 'commma-dangle': 'off', }, }.prettierrc.jsmodule.exports = { semi: false, arrowParens: 'always', singleQuote: true, trailingComma: 'all', }.editorconfigroot = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false参考
Node.js 10,12,14のためのTypeScriptコンパイラ設定(targetとlib)
Node+TypeScript+ExpressでAPIサーバ構築
github
node.jsアプリの構成
- 投稿日:2020-11-27T18:41:45+09:00
Serverlessを使ってブラウザだけでJavaScriptを実行してみる
はじめに
Serverlessのクラウドサービスを利用して、JavaScriptを実行(ランタイムはNode.js)してみました。常時稼働しなくて良いが、リクエスト投げた時だけ動いて欲しい。定期的に実行したい。そんな時に使えそうです。
ここではIBM Cloud Functions(以下、ICF)を使いました。
当記事は「サーバーレスというものを試してみたい」「IBM Cloud Functionsを使ったことはない」という方向けの記事なので、より深い内容は別記事をあたっていただければと思います。
なお、厳密にはライト(無償)プランではなく、平均実行時間や実行回数などで課金されていくのですが、記事を書いている時点(2020/11)ではライトアカウント(無償)でも使えました。また無料枠があるので、ここに記載されている内容を試すくらいであれば無料で試せると思います。こちらのチュートリアルを基に少し手を加えたものを記事にしています。
他に参考にした記事
- マニュアル: ここにチュートリアルがあります。
- セミナー資料: ICFの紹介とハンズオン手順があります。
- なお、IBM Cloudを利用されたことがない方が始めるための手順の記事はこちら。では、早速試してみます。順調にいけば1時間足らずで全て試せると思います。
IBM Cloud Functionsのアクションを作成する
アクションとは、「サーバーレスで動かすコード」といったところでしょうか。単体の実行や、単体の実行結果を渡して次につなげる「シーケンス」というものもできます。
IBM Cloudにログイン
https://cloud.ibm.com/左上のナビゲーション・メニューをクリックし、「Functions」を選択
(この手順はやらなくても先に進めますが)名前空間(namespace)を作ります。
namespaceとは、ICFで作成・実行するアクションやトリガーなどの入れ物です。ユーザーごとにアクセス権限管理もできるようです。画面上部の「現在の名前空間」のプルダウンをクリックし、一番下の「名前空間の作成」をクリックします。
- 名前: 任意。ここではfunctions-test
- 以下はそのままで「作成」をクリック
現在の名前空間: functions-test がでてくれば成功です。
以降の手順はこの名前空間で作業していきます。
ちなみにこの名前空間、一旦作ると消すことができないみたいです・・・。4.「作成の開始」をクリック
5. 「作成」画面で一番左の「アクション」をクリック
6. 「アクションの作成」画面で
- アクション名: 任意。ここでは hello
- 以下はそのまま で「作成」をクリック
7.コードのエディターが開きます。Hello Worldのサンプルコードが入っています。右上の「起動」をクリックして試してみます。
8. 以下のように実行できました。
パラメータを使ってアクションを呼び出す
9.少しアプリを変えてみましょう。エディターで、コードから、function main(params) {...} 部分を削除するか、同じ部分をコメントアウト /* ... */で囲みます。その後、以下をコピペし、右上の「保存」をクリック。
このコードは、引数にnameとplaceがあり、これを渡すことで文を出力します。function main(params) { return { message: "Hello, " + params.name + " from " + params.place }; }10.「パラメータを付けて起動」をクリックするとポップアップが現れるので、以下のように{}で値を囲むようにコピペ(nameとplaceの値(sirotan, Japan)は適当なので、適宜変更してください)し、「適用」。
{ "name": "sirotan", "place": "Japan" }11.その後「起動」すると、以下のように結果が変わりました。
トリガーでアクションを呼び出す
トリガーとは、先ほど作成したアクションを定期的に実行したり、何らかのイベントをきっかけに実行するためのものです。
12.以下のように左上の「Actions」をクリックして元のメニューに戻ります。
※ このWebサイトのツリー構造を示すものをパンくずリストっていうらしい。筆者は初めて知りました(汗
13.もう一つアクションを作成します。手順5~6と同様です。アクション名は先ほどのもの(hello)とは違うものにしてください。
- アクション名: 任意。ここでは gettime
- 以下はそのまま で作成をクリック14.以下のコードをコピペします。エディターで、コードの中身の function main {...} 部分を削除するか、コメントアウト /* ... */で囲みます。その後、以下をコピペし、「保存」します。
このコードでは、実行時の日時と、何回目の実行かを表示します。var counter = 0; // global variable function main(msg) { var date = new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000)); var time = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds(); counter++; return { message: "It is " + time + ". This action has been called " + counter + " time(s)." }; }15.このgettimeアクションを繰り返し実行させたいと思います。コードエディターの画面左「接続されたトリガー」をクリック。その後、右上の「トリガーの追加」をクリック
16. 「Periodic」をクリック
17. 以下を設定し、「作成&接続」をクリック
- トリガー名: 任意。ここでは minute alarm
- タイマー設定: 一番下の「UTC分」の右上のパターン選択から「毎分」を選択
18.左上のパンくずリストから「Action」->左メニューの「モニター」を選択。トリガーに接続されたアクション(minute alarm)が、赤枠のように約1分おきに呼び出され、アクション「hello」を実行されていることが確認できるかと思います。
また、画面右の「アクティベーション・ログ」に実行ログが出ていますが、青枠のアクション名(helloやminute alarm)の下にある文字列は実行時のID(Activation ID)です。クリックするとブラウザの別ウィンドウが開き、実行内容が表示されます。19.作成したトリガーを停止します。
左メニューの「トリガー」-> 該当のトリガー(ここでは、minute alarm)をクリック ->右の「接続」列にある「有効」のチェックを外す。
これを忘れるとずっと実行され続け、意図せずに料金が発生する恐れがありますのでご注意ください。Webアクションを作成する
これまではICFの画面内での実行結果表示でしたが、HTMLで実行結果を表示したいと思います。
20.先ほど作成したアクション: helloのコードを以下のようにupdateし、右上の「保存」をクリック。
これをHTML形式でメッセージを表示します。function main(params) { let html = '<html style="color:red"><body><p>' + 'Hello, ' + params.name + ` from ` + params.place + '</p></body></html>' return { headers: { "Content-Type": "text/html" }, body: html }; }21.左メニューの「エンドポイント」をクリックし、「Webアクションとして有効化」にチェックして、「保存」。
22. 画面中程の「HTTP メソッド」にあるURLリンクを開くとメッセージが表示されます。
23. 引数を渡すには、ブラウザのURLの最後(/helloの後)に以下のように値を追加します?name=sirotan&place=Japan以上です。
同様に、gettimeアクションでも可能です。コードを以下のようにしてみてください。var counter = 0; // global variable function main(msg) { var date = new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000)); var time = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds(); counter++; let html = '<html style="color:red"><body><p>' + 'It is ' + time + '. This action has been called ' + counter + ' time(s).' '</p></body></html>' return { headers: { "Content-Type": "text/html" }, body: html }; }
まとめ
ここまで読んでいただきありがとうございます。
割と簡単に、ブラウザだけでJavascriptを実行し、コード変更や定期実行、実行結果をHTML表示できました。
ちょっとでも興味を持っていただければ幸いです。他の言語、CLIでの実行、アクションのAPI呼び出しや、複数のアクションの実行(シーケンス)も可能ですが、それはまた別の機会に試そうと思います。
- 投稿日:2020-11-27T17:00:25+09:00
npm -g install @wordpress/env install ERR on Ubuntu 20.04 via Windows 10 WSL2
環境
- ホストOS: Windows 10 Pro
- WSL2
- Ubuntu20.04
- Node.js 14.15.1
- npm 6.14.8
状況
wp-envを使うために,
$npm -g install @wordpress/envすると
ERR! configure error gyp ERR! stack Error: `gyp` failed with exit code: 1 gyp ERR! stack at ChildProcess.onCpExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:351:16) gyp ERR! stack at ChildProcess.emit (events.js:315:20) gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:277:12) gyp ERR! System Linux 4.19.128-microsoft-standard gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "configure" "--fallback-to-build" "--module=/usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit/build/Release/nodegit.node" "--module_name=nodegit" "--module_path=/usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit/build/Release" "--napi_version=7" "--node_abi_napi=napi" "--napi_build_version=0" "--node_napi_label=node-v83" gyp ERR! cwd /usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit gyp ERR! node -v v14.15.1 gyp ERR! node-gyp -v v5.1.0 gyp ERR! not ok node-pre-gyp ERR! build error node-pre-gyp ERR! stack Error: Failed to execute '/usr/local/bin/node /usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --module=/usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit/build/Release/nodegit.node --module_name=nodegit --module_path=/usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit/build/Release --napi_version=7 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v83' (1) node-pre-gyp ERR! stack at ChildProcess.<anonymous> (/usr/local/lib/node_modules/@wordpress/env/node_modules/node-pre-gyp/lib/util/compile.js:83:29) node-pre-gyp ERR! stack at ChildProcess.emit (events.js:315:20) node-pre-gyp ERR! stack at maybeClose (internal/child_process.js:1048:16) node-pre-gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:288:5) node-pre-gyp ERR! System Linux 4.19.128-microsoft-standard node-pre-gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/@wordpress/env/node_modules/.bin/node-pre-gyp" "install" "--fallback-to-build" node-pre-gyp ERR! cwd /usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit node-pre-gyp ERR! node -v v14.15.1 node-pre-gyp ERR! node-pre-gyp -v v0.13.0 node-pre-gyp ERR! not ok Failed to execute '/usr/local/bin/node /usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --module=/usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit/build/Release/nodegit.node --module_name=nodegit --module_path=/usr/local/lib/node_modules/@wordpress/env/node_modules/nodegit/build/Release --napi_version=7 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v83' (1) [nodegit] ERROR - Could not finish install [nodegit] ERROR - finished with error code: 1 npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! nodegit@0.26.5 install: `node lifecycleScripts/preinstall && node lifecycleScripts/install` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the nodegit@0.26.5 install script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /home/ii-140/.npm/_logs/2020-11-27T06_57_34_623Z-debug.log解決策
node-pre-gyp
が悪さしてそうなので, そのあたりを調べてると,WordPress gutenberg の github issueにそれっぽいのがありました.
https://github.com/WordPress/gutenberg/issues/22398
@watofundefined - Can confirm that I had the same issue, specifically with npm -g i @wordpress/env on a clean copy of Ubuntu 20.04 via Windows 10 WSL2.
sudo apt-get install libkrb5-dev resolved it for me and let the install complete as intended.
Once I closed and reopened terminal wp-env worked as expected.
なるほど,
$sudo apt install libkrb5-devしたあとに, 再度
$npm -g install @wordpress/env結果
いけました.
参考
- 投稿日:2020-11-27T15:50:43+09:00
Anyダメ絶対! axios編
ウェブクルー AdventCalendar 2日目の記事です。
今年もアドベントカレンダーの時期がやってきました!※タイトルはオーバーな表現が含まれておりますがこちらは演出によるものです予めご了承ください
About me
@kouchanne はウェブクルーに2017年に新卒で入社した、今年4年目になるエンジニアです。
新卒を売りにしてますが、4年も立つと新卒ブランドはすっかりなくなってしまうものですね...
それはさておき、今年も元気に記事を書いて行こうと思いますのでどうぞお付き合いくださいm(_ _)m序章
最近弊社でも、Vue.js(Nuxt.js)が導入されてきたので、RESTAPIを叩いてあーだこーだするみたいなケースが増えてきました。
※Nuxt.jsの導入で頑張った話はこちらでまとめてますのでよろしければご覧ください。基本的にVue.jsを書く際はTypeScriptを利用するようにしているんですが、TypeScriptをやっていく上で絶対ぶつかる壁がありますよね...
そう Any こいつです
TypeScript対応していない、JavaScript製のライブラリを使っている際に出てくるAnyはある程度しょうがない部分もありますが、自分で定義するものに関してはできる限りAnyを避けることでTypeScriptの恩恵を得たいですよね。
今回は、axiosにフォーカスしてTypeScriptの恩恵をできる限る受ける方法をご紹介したいと思います。
そもそも Any is 何
そもそもTypeScriptというのはJavaScriptに型情報を持たせることで、実装時に意図しない値になることを未然に防ぐものです。
JavaScriptはもともと予めすべての型情報を確定させなくてはならないという制約がないため、型情報を予め定義していないものに関しては、どうしてもどの型にも当てはめることができないという状態が発生してしまいます。
しかし、それでは実装時に型を確定させる必要があるTypeScriptでは実装ができなくなってしまいます、
そこで登場するのが 「とりあえず、何くるかわからんけどOK」 というのがAnyになります。axiosの場合
「でも叩くAPIは決まっているし、型は確定しているんじゃないの?」
って思う方もいらっしゃると思います、
しかし、RESTAPIでやり取りする値はJSONというフォーマットになります。JSONは
{ "hoge": "hogehoge", "fuga": [ { "fuga": "fugafuga" } ] }こんな見た目をしているので、一見JSONそのものがオブジェクトという錯覚をしてしまうのですが正確には
JSONフォーマットの文字列(string)をJavaScript側で変換してオブジェクトにするという流れを経ていますその際、ただの文字列情報に型情報を持つことができないので、最終的にやってくる値がAnyになってしまうというわけです。
しかし、axiosでは事前に型をセットすればResponseを特定の型にすることができるのでそちらをご紹介します。
やってみる
今回のサンプルプログラムはこちらのリポジトリにあります
想定するAPI
今回叩くAPIから返ってくるResponseとして以下のようなデータが返ってくることを想定して実装します
成功パターン
{ "status": "SUCCESS", "results": [ { "id": 1, "name": "サンプル タロウ", "tel": "電話番号が入ります", "address": "住所が入ります", "age": 18 }, { "id": 2, "name": "サンプル タロウ", "tel": "電話番号が入ります", "address": "住所が入ります", "age": 56 }, { "id": 3, "name": "サンプル タロウ", "tel": "電話番号が入ります", "address": "住所が入ります", "age": "ほげほげ" } ] }失敗パターン
{ "status": "FAILED" "message": "データの取得に失敗しました" }1. まずは必要な型を作成する
上で想定している成功パターンと失敗パターンに合わせて、型を作成します。
成功パターン
成功時は
SuccessResponse
という型を作成してみましょうinterface SuccessResponse { status: 'SUCCESS' results: User[] }
User
データは単体で使い回すことが想定されるので、別途でUser
型として定義するのが良いでしょう。次のような形で定義します。
type User = { id: number, name: string, tel: string, address: string, age: number }これで、User情報の部分の型を作成することができました。
この型をresults
の部分に配列としてセットします!ちなみに、今回
Interface
とType Alias
どちらも使っていますが、大体できることは一緒です。
この2つの使い分けに関しては、この方の記事がわかりやすかったので参考にしてみてください失敗パターン
次に失敗パターンの型を作成します
FaiedResopnse
という型で定義します。interface FaiedResopnse { status: 'FAILED' message: string }実際のAPIはもうちょっと情報が多いと思いますが、今回はサンプルなのでシンプルに書いてます。
ちなみに、
status
の部分がstringではなく文字列になっていますが、これはリテラル型といって、その文字列のみを許可する型を定義することができます。2.実際にaxiosに作成した型を当てはめる
先程作成した形をaxiosのResponseの成功パターンと失敗パターンそれぞれで使えるようにします。
やり方は簡単です、先程定義した型を
成功パターンをget
の型にセット
失敗パターンをAxiosError<FaiedResopnse>
としてerrの型にセットするだけです。import axios,{AxiosError} from 'axios' const getUsers = () => { return axios.get<SuccessResponse>('http://localhost:3000/users').then((res) => { const data: SuccessResponse = res.data /* dataの型がSuccessResponseになってる */ return data }).catch((err: AxiosError<FaiedResopnse>) => { const data: FaiedResopnse = err.response?.data ?? {status: 'FAILED', message: err.message} /* dataの型がFaiedResopnseになってる */ return data }) }なぜエラー時だけ
AxiosError
をがあるかというと、型をセットするタイミングが異なるためです
axiosのget
に渡すことで予めresponseデータに型をセットした状態で取得ができるようになります
なので、以下のようにも書けると思いますaxios.get('http://localhost:3000/users').then((res: AxiosResponse<SuccessResponse>)終了
必要な工程はこれだけです!
これで、axiosのResponseに型をセットすることができるようになりました。ちなみに、サンプルではこれから更にJSONのバリデーションをかけて失敗した場合エラーにするという処理も入れています。
まぁ意図しないところでエラーになっちゃうと困るので、あまり出番はないかもですが、こうすることでありえない値を呼び出そうとして実行時エラーになるということを未然に防ぐことができるようになるのでより型安全なTypeScriptにすることができるようになります。まとめ
今回はaxiosのResponseに型をセットする方法についてご紹介させていただきました。
今回のサンプルデータではuserデータがシンプルなので良いですが、実際に使うデータは似たような名前のプロパティーが多かったりして型推論が使えないと厳しいものが殆どになってくると思います。
しっかりと型をセットすることで、その辺りのTypeScriptの恩恵を受けることができるので積極的に活用していきたいですね。
いつもAnyでやってしまっているという方がいれば、参考になれば幸いです。
最後に
明日のカレンダーは @Hideto-Kiyoshima-wc さんになります。
よろしくおねがいします。
ウェブクルーでは一緒に働いてくれる方を絶賛募集中です!
興味のある方はぜひお問い合わせください
https://hrmos.co/pages/1004681658307198976/jobs/214
- 投稿日:2020-11-27T11:42:27+09:00
EJSでもconsole.logが使える。デバッグに便利。
EJSでも、JavaScriptでおなじみの
console.log()
を使うことで実行環境へログを出力することができます。<% const person = 'taro'; console.log(person); %>taro使用しているNode.jsがv10が以上であれば
console.table()
なども使うことができます。<% const people = [ {name: 'taro', age: 66}, {name: 'shota', age: 33}, {name: 'hiroto', age: 10}, ]; console.table(people); %>┌─────────┬──────────┬─────┐ │ (index) │ name │ age │ ├─────────┼──────────┼─────┤ │ 0 │ 'taro' │ 66 │ │ 1 │ 'shota' │ 33 │ │ 2 │ 'hiroto' │ 10 │ └─────────┴──────────┴─────┘混みいったコードを書く時にデバッグとして使うと便利です。
参考記事
Node.js v10.0.0でconsole.table()追加&console.log()アップデートに感動したので早速試してみる