このブログは更新を終了しました。移転先はこちらです。

2023-12-12

サイトの作り方③Dynalist APIを理解する

 Noratetsu Houseの作り方の続きです。

 今回はDynalist APIの利用について。

経緯と断り書き

 ここまでの記事はこちら。

 記事タイトルは「サイトの作り方」となっていますが、これはあくまで私の個人サイトの「Noratetsu House」の作り方です。長いので省略して書いています。

 この一連の記事はあくまで「のらてつのサイトはどうやってできているのか」を書いておくものであり、勉強の取っ掛かりになるためにやっているだけなので、正確で十分な知識を提供するものではありません。


 

 普通個人サイトを作るにあたってDynalist APIが登場することはありませんが、私はDynalistのデータを使ってサイトを構築しているので、サイトの作り方の一環として今回Dynalist APIの話をします。

 

書き方の基本

 APIにはそれぞれ仕様書がありますが、各プログラミング言語で何をどうしたらいいのかまでは仕様書には書かれていません。なので徒手空拳で挑んでも咀嚼は難しいと思います。

 そのためJavaScriptにおけるAPIの使い方については前回おおよそのところを説明しました。正確ではなかったかもしれませんが、「結局何をすればどうなるのか」は多少見えやすくなったのではないかと思います。

 Dynalist APIの仕様書を見てみましょう。

 APIを利用する場合はDeveloper - DynalistでAPI secret tokenを発行してもらいます。

 APIの利用方法について以下のように書いてあります。

All of our endpoints expect POST requests, and the Content-Type is application/json.

 endpoint(エンドポイント)とはリクエストを送るURLのことです。提供されている複数のエンドポイント(=URL)はいずれもPOSTリクエストを必要とし、こちらが送信するデータはjson形式にする必要があるよということです。

 JavaScriptでは以下のような意味になります。fetch(URL, オプション)のオプション部分にオブジェクトとして色々書く必要があるわけです。

fetch('https://dynalist.io/api/v1/doc/read', {
  method: 'POST', // methodにPOSTを指定する
  body: JSON.stringify({ // bodyにjson形式で情報を記述する
    token: '<secret token>',
    'file_id': '<file id>',
  }),
})

 https://dynalist.io/api/v1/doc/readがエンドポイントで、オプションのオブジェクトのmethodプロパティにPOSTリクエストである旨を記述しています。

 なおJSON.stringify(object)はオブジェクトや配列をjson形式の文字列に変換するメソッドです。必要な情報を含むオブジェクトをこのメソッドでjsonにしています。そのjsonを、オプションのオブジェクトのbodyプロパティの値にすることで、APIに情報を渡すことができます。

 

Dynalist APIでできること

 Dynalist APIはどんな機能を提供してくれているのか、具体的に見ていきましょう。なお、「ファイル」は「ドキュメント」と「フォルダ」を併せたものとします。

  • File-level API

    • https://dynalist.io/api/v1/file/list(ファイルのリストを取得)

    • https://dynalist.io/api/v1/file/edit(ファイルのタイトル変更・移動・新規作成)

  • Document-level API

    • https://dynalist.io/api/v1/doc/read(指定したドキュメント内の全ノードを取得)

    • https://dynalist.io/api/v1/doc/check_for_updates(指定したドキュメントのバージョンを取得?)

    • https://dynalist.io/api/v1/doc/edit(指定したドキュメント内の特定のノードの編集・移動・削除・子項目作成)

  • Account Level API

    • https://dynalist.io/api/v1/inbox/add(Inboxに指定してあるドキュメント/ノードに新規ノードを追加)

    • (PRO限定)https://dynalist.io/api/v1/upload(テキストファイル、画像ファイル、PDFファイルをアップロード)

    • https://dynalist.io/api/v1/pref/get(Inboxに指定したドキュメント/ノードの位置またはInboxへの新規ノードの追加位置の設定(topかbottomか)を取得)

    • https://dynalist.io/api/v1/pref/set(ドキュメント/ノードをInboxに指定、またはInboxへの新規ノードの追加位置を設定)

 大きく分けて「File-level API」「Document-level API」「Account Level API」の三つに分類されており、全部で9種類のAPIがあるようです。

 データを変更する系のAPIは慎重に利用した方が良いでしょう。今この記事を読んで「へえ~」と思う段階で手を出すのは多分危ないので、ここでは解説しません。私自身、現時点で必要を感じていないので使ったことはありません。

 

ノードのデータを取得する

 私がサイト構築にあたり使っているのはドキュメント内のノードを取得するAPIだけです(仕様書内では「Get content of a document」の項)。既にこの記事の上の方や前回記事で例に出しているものです。

const token = 'hoge'; // secret tokenをここに入れる(うっかり公開しないように注意)
const fileId = 'fuga'; // ファイルのURL(https://dynalist.io/d/fuga)のfugaの部分を入れる 
fetch('https://dynalist.io/api/v1/doc/read', {
    method: 'POST',
    body: JSON.stringify({
        token: token,
        'file_id': fileId,
    }),
})
    .then(response => response.json())
    .then(data => {
        console.log(data);
        // dataを使って任意の処理
    })

 仕様書を見てみましょう。

 「Request parameters」の欄に、どういう情報を送信しなければならないかが書いてあります。「token」と「file_id」が必要なことがわかります。なので、それらをキーとしたプロパティを含むオブジェクトを作り、それをjsonにして送信することになります。

 上の例では一度に書いていますが、作業の各段階を変数に入れながらやってみるとこんな感じになります。

// 「Request parameters」にしたがって送信する情報のオブジェクトを作る
const param = {
  token: '<発行されたsecret token>',
  'file_id': '<取得したいドキュメントのID>',
}
 
// それをjson形式の文字列に変換する
const json = JSON.stringify(param);
 
// fetchに渡すオプションのオブジェクトを用意する
const option = {
 method: 'POST',
 body: json,
}
 
// fetchでAPIに通信する
fetch('https://dynalist.io/api/v1/doc/read', option)
  .then(response => response.json()) // 受け取った文字列をオブジェクトに変換する呪文
  .then(data => {
    console.log(data);
    // dataを使って任意の処理
 })

 

 さて、これでデータを取得することはできるようになりました。では、取得したデータはどんな形をしているのでしょうか。

 仕様書の、背景が黒い欄に「Example response」と書いてあるところがあります。その下に書かれているオブジェクトが取得できるデータの例です。json形式で書かれています。

{
  "_code": "OK",
  "_msg": "",
  "file_id": "<document id>",
  "title": "Todo list",
  "version": 15,
  "nodes": [
    {
      "id": "<node #1 id>",
      "content": "Buy milk",
      "note": "2L whole milk",
      "created": 1552959309804, // timestamp in milliseconds of the creation time
      "modified": 1552959335182, // timestamp in milliseconds of the last modified time
      "children": []
    },
    {
      "id": "<node #2 id>",
      "content": "Send Jason photos",
      "note": "",
      "checked": true,
      "checkbox": true, // whether this item has checkbox
      "color": 4, // color label, 0~6
      "heading": 3, // heading level, 0~3
      "created": 1552959309804,
      "modified": 1552959335182,
      "collapsed": true,
      "children": [
        "<node #3 id>",
      ]
    },
    {
      "id": "<node #3 id>",
      "content": "Picnic photos",
      "note": "",
      "created": 1552959309904,
      "modified": 1552959359783
      "children": []
    }
  ]
}

 ドキュメント自体の情報がいくつかあり、ノードのデータはnodesプロパティの中に入っていることがわかります。

 試しに、指定したドキュメントの中にノードがいくつ含まれているのかをコンソールに表示してみましょう。

fetch('https://dynalist.io/api/v1/doc/read', option)
  .then(response => response.json())
  .then(data => {
    const nodes = data.nodes;
    console.log(nodes.length);
 })

 

 では、各ノードはどのようなデータによってできているのでしょうか。TypeScriptで使われるような「型」で表現してみます。stringは文字列、numberは数、booleanはtrueかfalseかです。

{
    id: string, // ノードリンク https://dynalist.io/d/<file id>#z=<id> の<id>の部分
    content: string, // 本文
    note: string, // ノート欄
    checked?: boolean, // チェックされているかどうか
    checkbox?: boolean, // チェックボックスが設定されているかどうか
    color?: number, // カラー設定は何番目か(0~6)
    heading?: number, // 見出し設定は何番目か(0~3)
    created: number, // 作成日時(ミリ秒)
    modified: number, // 更新日時(ミリ秒)
    collapsed?: boolean, // 子項目を折り畳んでいるかどうか
    children: string[], // 子要素のidの配列
}

 キー名に「?」がついているプロパティは、設定がある時だけデータに含まれ、デフォルト値の場合は省略されます。

 ノード毎の設定はこうして全て取得できるので、このデータを使って条件分岐などを駆使して、自分に必要な記事データを改めて作っていくことになります。

 当たり前のことですが、個々のノードがそれぞれ私の中でどのような意味を持っているのかをDynalist APIは考慮してくれません。全てのノードの情報を機械的に提供してくれるだけです。なので、Dynalistに記事データを作るという場合は、どのノードが記事タイトルでどこからどこまでが記事本文にあたるのかといったことを自分でうまく解釈していく必要があります。

 これが結構骨の折れる作業で、自分がやりたいことを実現できるかどうかは自分の工夫にかかっています。次回はこの作業について書きます。