Node.js アプリに TypeScript を導入する

既存の Node.js アプリケーションを徐々に TypeScript 化していく場合の導入方法のメモです。 Migrating from JavaScript | TypeScript オフィシャルサイトのこちらのぺージが参考になりますが、クライアントサイドを例書かれているので、サーバーサイドは勝手が違います。 typescript と ts-node のインストール まず typescript と ts-node しましょう。 ts-node はコンパイルせずに直接 ts ファイルを実行できます。 $ npm install -g typescript ts-node または -g ではなく --save-dev としてローカルにインストールします。 tsconfig.json の生成 $ tsc --init message TS6071: Successfully created a tsconfig.json file. tsconfig.json 生成されるので、 "target": "es2018", allowJs: true, "outDir": "./dist" にそれぞれ変更します。理由は Promise などを使えるようにするため、JavaScriptファイルはそのまま使うため、コンパイルしたものを dist ディレクトリに出力するためです。 tsconfig.json 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 { "compilerOptions": { /* Basic Options */ "target": "es2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "incremental": true, /* Enable incremental compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ } } main ファイルを .ts にする 例えばメインファイルの app.js を app.ts にリネームして、 tsc で実行します。 ...

2019年4月19日 · Toshimitsu Takahashi

Node.js Koa の RESTful API サーバに GraphQL を導入する

Node.js の HTTP サーバフレームワーク Koa で作られた API サーバに GraphQL 導入する方法です。 簡略化した例として、TodoリストのAPIサーバとして POST /api/todo で新規アイテムを登録し、 GET /api/todo でリストで取得できるもので説明します。同様の操作ができる機能を GraphQL で提供します。 RESTful API サーバの実装状態 koa-router はネストすることが可能なので、 /api は apiRouter として定義し、後で /graphql は別途 graphqlRouter として利用します。 package.json 依存する npm モジュールは以下の通りです。 1 2 3 4 5 6 7 8 9 { "dependencies": { "graphql": "^14.2.1", "koa": "^2.7.0", "koa-bodyparser": "^4.2.1", "koa-graphql": "^0.8.0", "koa-router": "^7.4.0" } } models/todo.js メモリ上の配列でTodoを管理する簡易的なものです。中身は同期処理ですが、より実践的にするため async (Promise) にしています。 ...

2019年4月12日 · Toshimitsu Takahashi

Ubuntu の Nginx に対して Let's Encrypt の certbot を設定するには

昔は SSL 証明書は購入することが必要でしたが、最近は Let’s Encrypt が普及したことで無料で https 対応できるようになりました。 ただこの証明書は有効期間が短いため、適切に設定しておかないとすぐに期限切れになってしまいます。 これは、 certbot を使って Ubuntu の Nginx の指定ドメインに対してセットアップするときの忘備録です。 certbot のリポジトリ追加とインストール $ sudo add-apt-repository ppa:certbot/certbot $ sudo apt-get update $ sudo apt-get install python-certbot-nginx 指定したドメインに certbot を設定する $ sudo certbot --nginx -d yourdomain.com 実行すると、下記のとおり出力されます。 途中訊かれるのは http のリクエストを https にリダイレクトするかどうかです。 Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator nginx, Installer nginx Obtaining a new certificate Performing the following challenges: http-01 challenge for yourdomain.com Waiting for verification... Cleaning up challenges Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/yourdomain.com Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2 Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/yourdomain.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations! You have successfully enabled https://yourdomain.com You should test your configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=yourdomain.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/yourdomain.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/yourdomain.com/privkey.pem Your cert will expire on 2019-07-07. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le 外部からの SSL 設定の確認 上記の You should test your configuration at: にある通り、 ブラウザで https://www.ssllabs.com/ssltest/analyze.html?d=_yourdomain.com_ を開いて適切かどうか確認しましょう。 ...

2019年4月8日 · Toshimitsu Takahashi

AWS Fargate 運用の Redash を Version 7.0.0 にアップグレード

AWS Fargate で運用中の Redash を Version 7.0.0 に上げました。 今回のバージョンからデータソースの暗号化されるため環境変数の設定に注意する必要があります。 環境変数 REDASH_SECRET_KEY の値で暗号化します。それが定義されていなければ REDASH_COOKIE_SECRET が使われます。さらになければデフォルト値が使われます。 従って今回から web サーバと worker のコンテナを分けている場合、 worker にも REDASH_SECRET_KEY が必要になります。 Task Definition の修正は、 docker イメージを redash/redash:7.0.0.b18042 に変えます。 環境変数 REDASH_SECRET_KEY, REDASH_COOKIE_SECRET に任意の値をセットします。 マイグレーションは、自分の場合は Task definition を編集して別にアップグレード用を作成します。そして web, worker, redis と3つのコンテナを web, redis のみにして web の実行コマンドを server 指定から manage,db,upgrade に変更します(["manage","db","upgrade"] となります)。 アップグレード用の Task Definition から [Run Task] してマイグレーションが成功してから、サービスを新しい Task Defintion に更新してコンテナを入れ替えましょう。

2019年4月4日 · Toshimitsu Takahashi

bashスクリプト内でパスから情報を取得する

シェルでは $0 で実行したスクリプトファイルのパスが取れる。ここでスクリプトのファイル名やスクリプトのあるディレクトリパスを切り出したときはどうすればよいか。basename というコマンドを使うとファイル名切り出せる。ただ bash だとパターンマッチ抽出たどコマンドに頼らずできる。 スクリプトのファイル名 1 2 3 4 5 6 7 #!/bin/bash basefilename=${0##*/} echo $basefilename basefilenamewithoutext ${basefilename%%.*} # 拡張子を除く echo $basefilenamewithoutext ## は先頭の最長マッチ部分を削除する %% は末尾の最長マッチ部分を削除する スクリプトのディレクトリパス 1 2 3 4 5 6 #!/bin/bash basedir=${0%/*} echo $basedir cd $basedir # スクリプトのあるディレクトリに移動する % は末尾の最短マッチ部分を削除する /foo.sh だと空文字列になってしまうのでがルートに置かれるスクリプトには使えない

2019年3月18日 · Toshimitsu Takahashi

JavaScript で HTML の参照文字をアンエスケープするには

HTML 文字列のアンエスケープ(unescape)とはタグに使われる文字をエスケープした文字実体参照などを実際の文字などに戻すことです。例えば &lt;b&gt;tilfin&apos;s note&lt;b&gt; を <b>tilfin's note<b> と変換します。RSS Feed などに HTML が埋め込まれるときに必要になったりします。 最近のブラウザでは、 DOMParser という HTML/XML/SVG パーサーが実装されているのでこれを使います。 サポート状況 https://caniuse.com/#search=DOMParser parseFromString メソッドの第2引数に MIME タイプを指定します。 1 2 3 4 function unescapeHTML(escapedHtml) { const doc = new DOMParser().parseFromString(escapedHtml, 'text/html'); return doc.documentElement.textContent; } 上記のようにユーティリティ関数を定義して試します。 > unescapeHTML("&lt;b&gt;tilfin&apos;s note&lt;b&gt;") "<b>tilfin's note<b>"

2019年3月7日 · Toshimitsu Takahashi

背景色に応じて文字色を白と黒で切り替える

ユーザーが背景色を指定できる要素の文字色をその背景色に応じて切り替えたいときの方法です。固定で白や黒にすると、背景色によっては見辛くなってしまうためです。そのためには背景色の輝度を算出します。RGBに対して R × 0.299 + G × 0.587 + B × 0.114 で算出できます。下記の例では背景色が暗ければ白を、明るければ黒となるように計算しています。 1 2 3 4 5 6 7 8 9 10 11 12 13 const backColor = '#0033ff'; let r = backColor.substr(1, 2), g = backColor.substr(3, 2), b = backColor.substr(5, 2); r = parseInt(r, 16); g = parseInt(g, 16); b = parseInt(b, 16); const y = 0.299 * r + 0.587 * g + 0.114 * b; // 輝度 const foreColor = v < 128 ? 'white' : 'black';

2019年1月15日 · Toshimitsu Takahashi

Ubuntu 18.04 上の GitLab CI で Docker イメージをビルドして GCP の Container Registry に登録するまで

環境 さくらVPS に Ubuntu 18.04 をOSカスタムインストールしていて、GitLab CE をインストールします。GitLab CI Runner も同ホストで動かします。 CI で Docker イメージをビルドして、Google Cloud Platform (GCP) の Container Registry にプッシュします。 GitLab のインストール 下記のページの通りです。EEとCEの差はライセンスを適用するかどうかで変わりますが、絶対にCEのままというならインストールスクリプトのURLを変えるとそちらでインストールされます。 about.gitlab.com GitLab CI 機能の設定 Docker CE をインストール 単に apt からインストールもできますが、バージョンが古いと嵌りやすいので最新の Docker CE をインストールします。 docs.docker.com GitLab Runner のインストール こちらは、Dockerへのインストールではなく、リポジトリからインストールしました。GitLab Runner はほぼ CI Runner のコマンド管理ツールで、実際ジョブ処理する Runner (ないし Executor) は次でインストールという認識です。 docs.gitlab.com 実際の GitLab Runner(Executor) として動作するDockerコンテナを登録 下記の Building Docker images with GitLab CI/CD ページ内の Use docker-in-docker executor* を実行します。 https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor 各リポジトリの CI 設定 GCPにストレージ管理者権限を持つサービスアカウントを作成し、JSONキーファイルを取得します。 https://cloud.google.com/container-registry/docs/advanced-authentication#json_key_file ...

2018年11月18日 · Toshimitsu Takahashi

さくらVPSで物理HDD障害後にUbuntuのFile SystemがRead onlyになったときの対応方法

さくらインターネットからVPSの障害報告があり、サービス復旧の連絡があり、SSHできたので気にしてなかった。 しかし後日 unable to open /var/xxx: Read-only file system みたいな感じで軒並みVPS内のサービスが止まってしまっていた。 問い合わせところ既に物理HDD障害は交換で対応済みだが、仮想OSにおいてディスクのファイルシステムに問題があったためだった。 CentOSのデフォルトの方法はサポートの方に紹介いただいたが、元々カスタムOSでUbuntuを入れていたので自力で直したのでメモしておく。 ブラウザでさくらのVPSコントロールパネルを開く。 対象のサーバのページを開く。 **シリアルコンソール(β版)**を開く 強制再起動する。即座にシリアルコンソールで Shift キーを押し続ける grub メニューで Ubuntu ~ recovery mode というのを選択する。 (initramfs) と入力待ちになったら e2fsck -f -y -v /dev/vda1 を行う。 vda1 のところは仮想環境によるかもしれないので5.でのログを見ておく。 完了したら reboot で再起動すると普通に起動してくれるはず。 ※シリアルコンソールの表示が反応しない時もあるので、手順4以外はVNCコンソールを同時に開いて使っても良い。 参考ページ VPS の再起動の後に読み込み専用ファイルシステムと言われました。 本の虫: Ubuntuがブート時にmanual fsckが必要だとしてinitramfsで止まった時 Ubuntu 12.04 リカバリーモードで起動する - kledgeb [SOLVED] UNEXPECTED INCONSISTENCY - run fsck manually

2018年10月1日 · Toshimitsu Takahashi

RubyでYARD定義を使って実行時にメソッド引数と戻り値の型チェックを試みる

(Ruby のカンファレンスの度に、毎度話題になる?) Ruby に型が欲しい件ですが、個人的な見解を書いておこうと思います。ちなみに私は RubyKaigi 2018 に参加しておりません。Twitterのタイムラインでの賑わいを見ていただけです。 最近、Swift を触っててそのコンパイルの重さにうんざりしているので、 型推論 は現代のマシンスペックでは基本的に辛いと思っています(カフェでノマドコーディングしたいので)。またメタプログラミングし放題の Ruby に導入するのは困難という認識です。 では、どういうときに 型定義 が欲しくなるのか考えてみます。 コードを書いて実行して確認というトライアンドエラーを減らしたい、実行前にエラーをなるべく洗い出したい。 型定義起因による補完を効かせながらコーディングしたい。これはエディタやIDEのサポートも必要です。 1 について、Rubyの場合テストでカバレッジを稼いで、なるべくエラーの芽を潰しておこうとします。しかしユニットテストにおいて end-to-end テストが存在しない場合、単純なユーティリティ関数でなければ、モックやスタブに頼るようになりチェックが甘くなります。そのためテストは通過するものの、実際通しで動かしたとき想定とは異なる型のデータ受渡しが発生してしまうことがあります。 2 について、メソッド名や引数名から型をコードの読解で推測することは可能ですが、それなりの規模のアプリケーションやライブラリではコードコメントでドキュメント定義していないと(昔の自分や)他人の書いたコードを扱うのが困難になることが多いと思います。 そして例えば YARD で引数や戻り値の型をコメントに定義して、ドキュメントを生成したりIDEでコード補完に用いることが多いでしょう。 さてここから本題ですが、1 で型チェックしないで検証から漏れてしまう問題は、テストでメソッド呼び出しの引数と戻り値をよりチェックするアサーションを明示的に書いていく必要があります。一方 2 でYARDによるドキュメントの型定義は必ずしも実際のソースコードで走る処理と一致してるか保証されない問題もあります。 そこで『YARD定義によるメソッド引数と戻り値の型チェック』を実行時に行ってみたらどうかと考えてみました。 テストで型チェックのアサーションを減らせる。 YARD定義とソースコードが一致してるか検証できる。 Rubyのソースコードに余計な定義を挿し込むことはない。 YARDはテンプレートで書きだす直前の解析データを、YARD::Registry から参照する機能を提供しています。またあらゆるメソッド呼び出し(:call)と戻り(:return)は、TracePoint 機構を使ってフックすることができます。これを組み合わせれば、割と簡単に、実行時に定義どおりに適切な型の引数が指定されてメソッド呼び出され、適切な戻り値が返っているかをチェックすることできると思います。 ということで、試してみましょう。 lib/dog.rb これが YARD 定義を書いた検証対象のクラスになります。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 module Animal # # This class is Dog # class Dog # @param name [String] a name # @param weight [Numeric] weight def initialize(name, weight) @name = name @weight = weight @children = [] end # Add a child dog # # @param dog [Animal::Dog] a child dog def add_child(dog) @children.push(dog) end # Run. # # @param distance [Integer] # @return [String] message def run(distance) "#{@name} runs #{distance}." end # dummy method returns wrong type value # # @return [Integer] def dummy "a string" end end end definition.rb YARD::Registry から ClassObject と属する MethodObject を解析してチェックしやすい定義クラス MethodDefinition の集合 DefinitionStore に変換します。 ...

2018年6月3日 · Toshimitsu Takahashi