ブックマークレットで使う設定値の置き場所をHeadless CMS にしてみたら快適だった

#この記事は
久しぶりにブックマークレットです。
Headless CMSというのが面白そうなので、あえて、ブックマークレットで使う方法を考えてみました。積年の悩み事が解決しそうな感じです。

##ブックマークレットの悩み事
SaaS系のインターネットサービスを交えて、ちょっと凝ったブックマークレットを作るときに困るのは、APIのキーやパスワードに近いものを何処に書くかということです。センシティブな情報をソースコードに直接書きたくないのですね、編集が面倒だし。
ベストプラクティスを探そうと、過去に[**この記事**](https://qiita.com/kanaxx/items/d5a0591336cc2cc55783)で考察したのですが、いまいち答えは見つかっていません。編集しやすくするための工夫でしたし。

ということで、今日のメインテーマは、「**ブックマークレットで使う設定値の置き場をHeadless CMSにしてみよう**」、ということですね。

#Headless CMSとは
Micro CMSというHeadlessCMSを作っている人が書いた記事があるので、こちらを参照ください。

アプリエンジニアにこそHeadless CMSが必要だと思う話
https://qiita.com/shoma2da/items/23f38e25496d0bb1675f

簡単にいうと、Webデータベース＋REST APIという感じでしょうか。テーブル定義もデータ定義もCMSの管理画面でやるけど、データはAPIでとるだけ。ブックマークレット向きですね。


#やってみよう

##microCMSの準備
https://microcms.io へ行って右上の新規登録から必要事項を入力してアカウントを作ります。
プランにもよりますが、小規模な使い方であれば、無料で使えます。ありがたや。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/7151d755-2c86-abeb-8541-466b1623b5d0.png)


アカウントを作ったあとは、microCMSの設定をしていきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/440aa0e9-6d01-d519-574e-362e60b9c0eb.png)

microCMSでは、アカウントの下にサービスを作り、サービスの下にコンテンツを作るデータ構造になっています。サービス単位でサブドメインが1つ振られ、コンテンツはドメイン下にぶら下がるURL体系です。

こんな感じです
https\://**service-name**.microcms.io/api/v1/**contents-name**

サブドメインは早いもの勝ちですね。`bookmarklet`は頂きました。

##設定の作成
具体的にデータを作っていきます。

###コンテンツの設定
bookmarkletサービスの下にコンテンツを追加します。URLの一部になるAPIのエンドポイントもここで決めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/09a55059-aa4a-8401-bc85-2ced064ba621.png)

コンテンツの形式を選びます。リスト形式を選択しました。同じスキーマで複数のレコードを作る場合はリスト形式です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/4a379e83-4adc-b513-ad67-a8360607a673.png)

その後は、コンテンツのスキーマですね。データベースでいえばテーブル定義です。今回は適当に4つのフィールド（カラム）を作りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/af4996bd-6f9e-57d2-38af-449b06b954d4.png)

###データ登録
スキーマにデータを作ります。GUIでさくさくっと。入力が終わったら「公開」を押します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/ac2008e6-1d3a-4a43-6eea-262b953f25d1.png)

**ここ重要**
データを作った直後は、コンテンツIDがランダムの文字列になります。これもURLの一部になるので、分かりやすい文字に変えておきます。今回は**production**としました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/94359deb-5f1b-ef0a-83cf-c412b7d9cd9c.png)

データ登録をもう繰り返して、**staging**用のデータを作りました。2行見えてますね。（できればコンテンツIDも見たいですね）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/55fdcc9b-2eb9-669f-563c-6752d2b80a4c.png)

###APIを試す
microCMSにあるAPIのテスターでデータをとってみます。このURLだと、JSONの配列でとれます。コンテンツの中に2件のデータがあるからですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/a1ed4427-cada-ebc6-43c1-2a71d547a7ce.png)


URLの後ろにコンテンツIDを追加してみます。オブジェクト形式でとれました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/91f26a54-7079-e508-e123-15dbf45393ea.png)

ここまでで、準備完了です。

##ブックマークレット側
ブックマークレットの本体は、こんな感じになります。```fetch```でmicroCMSのAPIを叩いてデータを取り出しています。

```javascript:micro-cms-bookmarklet.js
// ==ClosureCompiler==
// @output_file_name default.js
// @compilation_level SIMPLE_OPTIMIZATIONS
// @language_out ECMASCRIPT_2017
// ==/ClosureCompiler==
javascript:(
    async function(service, content, apikey, undefined){
        let url = 'https://' + service + '.microcms.io/api/v1/' + content;
        let config = await microcms(url, apikey);
        
        console.log('microCMS URL=%s', url);
        console.log(config.project_id);
        console.log(config.api_key);
        console.log(config.login_id);
        console.log(config.password);

        //取得した値を使ってプログラムする
        //
        //

        async function microcms(url, api){
            let opt = {
                method: "GET", // *GET, POST, PUT, DELETE, etc.
                mode: "cors", // no-cors, cors, *same-origin
                cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
                headers: {
                    "Content-Type": "application/json; charset=utf-8",
                    "X-API-KEY": api,
                },
            }
            let response = await fetch(url,opt);
            return response.json();
        }
    }
)
//各人ごとに変わる値は、ここに集約
//左から、microCMSのサービスID、APIのID、コンテンツID、APIのキー
('bookmarklet','sample-config/staging', 'microCMSのAPIキー');

```

実行結果はこんな風になります。ログが出るだけですけど。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/65d28216-a21a-33a3-7158-6d8e1daf08a5.png)


このように書くと、microCMSの接続情報（サービス名やAPIのキー）がスクリプトの呼び出し部分に集中され、URL部分にしか残らなくなります。ブックマークとして保存したあとに、利用者が変更する形ですね。
ブックマークレットの中は、```config.xxx```の値を使って、他のサービスのAPIを叩くなり、SlackのWebhook呼び出すなり、自由にできますね。コードの中に変数名しか出てこないのでスッキリします。

##この方法のメリット
CDNにJSONファイルを置くのと同じじゃん。と言われると同じです。
一番の違いは、スキーマの拡張や値の設定がmicroCMSのGUIでできることですね。設定の変更と配置はエンジニアでなくてもできます。キーに有効期限があり、定期的に変えないといけないサービスのときは、有効に働きそうです。
microCMS付属のアクセス制御が働くのもうれしいです。不正利用などの怪しい状況になった場合、microCMSのAPIキーをリセットすればOKです。大事なデータを置きすぎないように気を付けてください。

コードと設定値の切り離しができることが一番のメリットです。

#まとめ
ブックマークレットで使う「できるだけ秘密にしたい情報」をどうやって扱うか、ということを書きました。自分用なのか、グループに配る用なのかによってやり方は変わりますが、いまのところ、一番いい方法見つけた！と思っています。

##おまけ
~~microCMSにコンテンツ（スキーマ）をコピーする機能か、1つのスキーマを環境ごとに使い分ける機能があるといいなぁ。。値が10個超えてくると設定するのが面倒だろうなぁ。~~

microCMSを作っている人からコメントをいただき、オブジェクト形式の使い方からリスト形式に書き換えました。