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

構造化ログのススメとRuby向けロガーOugaiを作った理由

構造化ログ 構造化ログ とは、機械的に処理しやすいログのことであり、その機構(ロギング)である。 英語圏では、 Structured Logging と表記される。たとえば Google Cloud の Stackdriver のドキュメントには下記の説明ページがあります。(残念ながら執筆時点で、これの日本語ページがまだできてないので、Google がどう訳すか興味深い) Structured Logging | Stackdriver Logging | Google Cloud 普通のログと構造化ログの比較 普通のログは、基本的に タイムスタンプ 、レベル 、そして メッセージ の文字列だけである。ログとして残す事象(イベント)のコンテキストになる情報はメッセージに適当に埋め込む。コンソール等で人が読みやすいものである。 構造化ログは、メッセージに埋め込んでいたコンテキストになる情報をそれぞれログ構造のフィールドに独立して持たせる。そのため後から解析がしやすい。そして出力するログはテキストベースで JSON にすることが多い。 では、見比べてみましょう。通常のRubyのLoggerと自作のOugaiでのログは次のようになります。『ユーザが記事を作成した』というログです(冠詞、削ってます)。 logger.info "User created article (user_id=#{user.id} article_id=#{article.id}" I, [2018-05-13T17:51:08.772542 #6253] INFO -- : User created article (user_id=123 article_id=45) logger.info "User created article", user_id: user.id, article_id: article.id 1 {"pid":6253,"level":20,"time":"2018-05-13T17:52:25.147+09:00","msg":"User created article","user_id":123,"article_id":45} ※デフォルトフォーマットとは異なります 見てわかる通り、普通のログは埋め込んで付帯情報を文字列化しつつ書かなくてはなりません。一方、構造化ログの方が JSON にした影響で長くなるのでコンソールでは読みづらいです。しかし読みづらいことはフォーマッタの動作環境での切替やparse機構を持つログビューワを使えば問題になりません。 構造化ログの方が解析しやすいというのは、例えば普通のログでは「あるユーザのログだけ抽出したいとき」に単に grep "user_id=10" とすると user_id が 101 など他のものまで引っかけてしまいます。構造化ログでは(主に JSONPath を使って)フィルタが $.user_id = 10 のように簡単に確実に絞り込めます。 ...

2018年5月13日 · Toshimitsu Takahashi