not/onNOT DONE, ALWAYS ON

not/onの開発メモ:FunkApiCore編

ver.5.3

not/onの公開記事をより、止まらせず(NOT DO NE)、動く(ALWAYS ON)ものにするため、ダイナミックな記事生成のパーツを検討する。

funkApiCoreは、これを実現するにあたり、not/onエンジンが呼び出すライブラリとして作成する。

not/onのコンセプトはこちらから。

ちなみにfunkApiCoreはCORSフリー、誰でもどのAPPでも自由に使える。
下記にそのコンセプト、想定利用内容、および呼び出し方法についてまとめていく。



funkApiCore 開発ドキュメント


funkApiCore は、記事やブログの投稿サービスにおいて、テキスト内に埋め込まれたカスタム構文(予約語)を検知し、動的なコンテンツ(お天気情報、祝日、最寄り駅、名言など)をパブリックAPIから取得してJSON形式で返却するための完全独立型JavaScript共通APIラッパーライブラリ。

1. コンセプト

⚙️ CORSフリー & ANTI-STACK

本ライブラリは、ブラウザのDOM(画面描画)や他のライブラリやフレームワークに依存しないピュアJavaScript(ES6)で記述。

極力開発環境や実行環境をスタックしない、下記、ANTI-STACKポリシーに準拠

anti-stack — 積まない、という開発スタイルZenn

また、利用するすべてのパブリックAPI(Open-Meteo, HeartRails, DummyJSON等)はCORSポリシーに対応しており、HTTPS環境下(https://noton.youside.me 等)からブラウザの fetch で直接呼び出してもブロックされない安全なアーキテクチャを採用する。

📊 レスポンスの標準化

各APIのバラバラなデータ構造を吸収し、常に以下の統一されたJSON形式で結果を返却する。

🌐 多言語への柔軟性(日英併記)

海外のAPIから返却される英語の気象ステータスなどは、ライブラリ内部で日本語(weather_text_ja)と英語(weather_text_en)の両方にマッピングして応答。


アーキテクチャ

FunkApiCore アーキテクチャ構成図 記事投稿サービス (UI/エンジン) 1. 構文パーサー 記事中の [WEATHER] などを検知 プレースホルダーHTMLを先行配置 2. UI・デバイス制御 ・位置情報要求ボタンの描画 ・navigator.geolocation の実行 ・確定座標をコアに渡し関数実行 ・返却された標準JSONを画面反映 環境依存処理 (ブラウザ等) ・DOM操作 (画面書き換え) ・位置情報パーミッション確認 ・メディア固有のUXデザイン FunkApiCore (純粋ロジック) 環境に非依存 / Pure JS getWeather() Open-Meteoラップ (日英変換) getHolidays() Holidays JPラップ (期間抽出) getNearestStation() HeartRailsラップ (徒歩計算) getTrivia() 3ジャンルの多重分岐ラッパー JSON共通フォーマッタ パブリックAPI群 (CORSフリー) Open-Meteo 天気データ(登録不要/制限緩) Holidays JP API 日本の祝日CSVベースJSONデータ HeartRails Express 日本の路線・最寄り駅検索 Trivia系エンドポイント ・Advice Slip (生活アドバイス) ・DummyJSON Quotes (名言 / CORS対応) ・Numbers API (日付の歴史雑学) 引数(lat, lng等) 標準化JSON fetch() JSON



2. 想定利用内容(使われ方)

本ライブラリは、not/on(記事投稿サービス)における「動的ブログパーツ(埋め込みコンポーネント)」のバックエンドロジックとしての利用を想定。
ただし、誰でもどのAPPでも使える。


記事内の記述例(疑似タグスタイル):

今日の世田谷はとても過ごしやすい気候です。
[WEATHER lat="35.65" lng="139.54" date="today"]

ここが最寄りになります。 [NEAREST_STATION]

今日の名言: [TRIVIA type="zen"]

レンダリングの流れ:

  1. エンジン側のパーサーが記事内の予約語を検知し、UIの「枠(プレースホルダー)」を画面に描画します。
  2. UI層のJavaScriptが、タグから抽出した属性値(lat, lngなど)を引数に渡し、本ライブラリ(FunkApiCore)を実行します。
  3. 返却された標準化JSONのデータを、UI層がHTMLに流し込んで装飾します。

3. 呼び出し方法(使い方と関数の紹介)

import { FunkApiCore } from './funkApiCore.js';

① 天気情報の取得(WEATHER)

特定地点の特定時間の天気を取得できる。

const response = await FunkApiCore.getWeather(35.65, 139.54, "today");
if (response.status === "success") {
  console.log(response.data.weather_text_ja); // 例: "霧雨"
  console.log(`${response.data.temp_max}℃`);  // 例: "24.9℃"
}

② 祝日情報の取得(HOLIDAY)

日本の祝日情報を取得できる。
"next"(直近)、"this_month"(今月)、"next_month"(来月)を指定。

const response = await FunkApiCore.getHolidays("next");
if (response.status === "success") {
  console.log(response.data.holidays[0].name); // 例: "海の日"
}

③ 最寄り駅情報の取得(NEAREST_STATION)

特定の位置情報を渡すと、最寄駅の情報を返す。
特定の位置情報は、クライアントから送ってもらうことを想定。

const response = await FunkApiCore.getNearestStation(35.65, 139.54);
if (response.status === "success") {
  const st = response.data;
  console.log(`${st.line_name} ${st.station_name}駅 (徒歩約${st.travel_time_minutes}分)`);
}

④ 読み物・雑学の取得(TRIVIA)

ちょっと記事に遊び心を、この場合はTRIVIAをコールする。
"advice"(アドバイス)、"zen"(偉人の名言・DummyJSON経由)、"number"を指定。

const response = await FunkApiCore.getTrivia("zen");
if (response.status === "success") {
  console.log(`"${response.data.content}" — ${response.data.author}`);
  // 例: "Life isn’t about getting and having..." — Kevin Kruse
}

4.詳しい関数仕様

1. 天気情報の取得 getWeather()

指定された座標の天気予報(Open-Meteo APIを利用)を取得。

【引数】

引数名 型 必須 説明/ 指定できる値
lat 数値 必須 緯度(例: 35.65)
lng 数値 必須 経度(例: 139.54)
date 文字列 任意 対象日を指定

dateのデフォルトは "today"。
指定可能: "today", "tomorrow", または "YYYY-MM-DD" 形式


【応答値 (data オブジェクト)】

プロパティ名 型 意味・解釈のしかた
date 文字 実際にデータが取得された日付(例: "2026-07-04")。
weather_code 数値 WMO(世界気象機関)の基準に基づく天気のコード番号
weather_text_ja 文字列 weather_code を日本語のテキストに変換した固定値。(例: "快晴", "晴れ(時に薄曇り/曇り)", "雨" など全9パターン)
weather_text_en 文字列 weather_code を英語のテキストに変換した固定値。(例: "Clear Sky", "Rain" など全9パターン)
temp_max 数値 その日の最高気温(摂氏 ℃)。(例: 28.5)
temp_min 数値 その日の最低気温(摂氏 ℃)。(例: 19.0)
rain_probability 数値 その日の最大降水確率(%)。0〜100の整数。(例: 20)

ちなみに、weather_code(天気のコード番号)は、以下のマッピング仕様にてweather_text_ja とweather_text_en を応答。
weather_codeを直接参照すれば、独自の判定基準でラベリングしたい場合に利用可能。

weather_code weather_text_ja weather_text_en
0 快晴 Clear Sky
1, 2, 3 晴れ(時に薄曇り/曇り) Mainly Clear / Partly Cloudy
45, 48 霧 Fog
51, 53, 55 霧雨 Drizzle
61, 63, 65 雨 Rain
71, 73, 75 雪 Snow
80, 81, 82 にわか雨 Rain Showers
95以上 雷雨 Thunderstorm
上記以外 不明 Unknown

2. 祝日情報の取得 getHolidays()

日本の祝日データ(Holidays JP API)を取得。

【引数】

引数名 型 必須 説明 / 指定できる値
period 文字列 必須 取得したい期間を指定。
・"next": 今日以降で最も近い次の祝日(1件のみ)
・"this_month": 今月あるすべての祝日
・"next_month": 来月あるすべての祝日

【応答値 (data オブジェクト)】

プロパティ名 型 意味・解釈のしかた
period 文字列 引数で指定した検索期間がそのまま返す(エコーバック)。
holidays 配列 該当する祝日データのリスト。該当する祝日がない月は空の配列 [] 。
└ date 文字列 祝日の日付。(例: "2026-07-20")
└ name 文字列 日本の公式な祝日名。(例: "海の日")

3. 最寄り駅情報の取得 getNearestStation()

指定された座標から最も近い駅情報(HeartRails Express API)を取得。

【引数】

引数名 型 必須 説明 / 指定できる値
lat 数値 必須 現在地などの緯度(例: 35.65)
lng 数値 必須 現在地などの経度(例: 139.54)

【応答値 (data オブジェクト)】

プロパティ名 型 意味・解釈のしかた
station_name 文字列 最寄り駅の名前。「駅」という文字は含まない。(例: "新宿")
line_name 文字列 その駅が属する路線名。(例: "京王線")
distance_meters 数値 指定された座標から駅までの直線距離(メートル)。
計算しやすい整数値で返す。(例: 430)
travel_time_minutes 数値 不動産表示などで使われる「徒歩1分=80m」の基準を用いて、
ライブラリ内部で distance_meters ÷ 80 を切り上げ計算した徒歩所要時間の目安(分)。(例: 6)

4. 読み物・雑学の取得 getTrivia()

指定されたジャンルのランダムなテキストデータ(Advice Slip API、DummyJSON Quotes API、Numbers API)を取得。

【引数】

引数名 型 必須 説明 / 指定できる値
type 文字列 必須 欲しい雑学のジャンルを指定。
・"advice": 日常のちょっとしたアドバイス
・"zen": 哲学者や歴史上の偉人の名言
・"number": 今日の日付にまつわる歴史の出来事(数字雑学)

【応答値 (data オブジェクト)】

プロパティ名 型 意味・解釈のしかた
type 文字列 引数で指定したジャンルをそのまま返す。
category 文字列 UI側でアイコンや枠の色を変えたい場合に使えるカテゴリ分類。
・"life": advice を指定した場合
・"philosophy": zen を指定した場合
・"date": number を指定した場合
content 文字列 取得された名言や雑学の本文テキスト(英語)。
author 文字列 この言葉の「発言者」や「出典」です。ジャンルによって性質が変わる。
・"advice" の場合: 常に "Unknown"(発言者不明のアドバイス)
・"zen" の場合: 実際の発言者の名前(例: "Kevin Kruse" 等)
・"number" の場合: 常に "History"(歴史上の事実のため)

5.ライブラリ情報

OpenAPI 3.0.0 仕様書(JSON)

  "openapi": "3.0.0",
  "info": {
    "title": "FunkApiCore (JavaScript/ES6)",
    "description": "記事埋め込みカスタム構文 [予約語] 用の独立パブリックAPIラッパーライブラリ共通仕様",
    "version": "1.1.0"
  },
  "paths": {
    "/weather": {
      "get": {
        "summary": "天気情報の取得 (Open-Meteo ラッパー)",
        "description": "緯度・経度と日付を指定し、日英の固定文言を含む標準化された天気データを取得します。",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number"
            },
            "description": "緯度"
          },
          {
            "name": "lng",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number"
            },
            "description": "経度"
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "today"
            },
            "description": "日付指定 (today, tomorrow, YYYY-MM-DD)"
          }
        ],
        "responses": {
          "200": {
            "description": "正常応答",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "success"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "date": {
                          "type": "string",
                          "example": "2026-07-04"
                        },
                        "weather_code": {
                          "type": "integer",
                          "example": 53
                        },
                        "weather_text_en": {
                          "type": "string",
                          "example": "Drizzle"
                        },
                        "weather_text_ja": {
                          "type": "string",
                          "example": "霧雨"
                        },
                        "temp_max": {
                          "type": "number",
                          "example": 24.9
                        },
                        "temp_min": {
                          "type": "number",
                          "example": 19.0
                        },
                        "rain_probability": {
                          "type": "integer",
                          "example": 20
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/holiday": {
      "get": {
        "summary": "祝日情報の取得 (Holidays JP API ラッパー)",
        "description": "指定された期間の日本の祝日データを取得します。",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "next",
                "this_month",
                "next_month"
              ]
            },
            "description": "取得対象期間"
          }
        ],
        "responses": {
          "200": {
            "description": "正常応答",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "success"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "period": {
                          "type": "string",
                          "example": "next"
                        },
                        "holidays": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": "string",
                                "example": "2026-07-20"
                              },
                              "name": {
                                "type": "string",
                                "example": "海の日"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/nearest-station": {
      "get": {
        "summary": "最寄り駅情報の取得 (HeartRails Express API ラッパー)",
        "description": "指定された座標から最も近い駅情報を取得します。",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number"
            },
            "description": "緯度"
          },
          {
            "name": "lng",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number"
            },
            "description": "経度"
          }
        ],
        "responses": {
          "200": {
            "description": "正常応答",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "success"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "station_name": {
                          "type": "string",
                          "example": "調布"
                        },
                        "line_name": {
                          "type": "string",
                          "example": "京王線"
                        },
                        "distance_meters": {
                          "type": "integer",
                          "example": 430
                        },
                        "travel_time_minutes": {
                          "type": "integer",
                          "example": 6
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/trivia": {
      "get": {
        "summary": "読み物・雑学の取得 (Advice/DummyJSON Quotes/Numbers)",
        "description": "タイプに応じたテキストデータを取得します。CORSに完全対応したエンドポイントを利用します。",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "advice",
                "zen",
                "number"
              ]
            },
            "description": "API種別 (zen=一般名言)"
          }
        ],
        "responses": {
          "200": {
            "description": "正常応答",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "success"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "example": "zen"
                        },
                        "content": {
                          "type": "string",
                          "example": "Life isn’t about getting and having, it’s about giving and being."
                        },
                        "author": {
                          "type": "string",
                          "example": "Kevin Kruse"
                        },
                        "category": {
                          "type": "string",
                          "example": "philosophy"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "ErrorResponse": {
        "description": "エラー応答共通モデル",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "string",
                  "example": "error"
                },
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "example": "TRIVIA_API_ERROR"
                    },
                    "message": {
                      "type": "string",
                      "example": "トリビア情報の取得に失敗しました。"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

ライブラリ実物

PAGEhttps://noton.youside.me/funkApiCore.js



「NotionClient編」につゞく

not/onの開発メモ:NotionClient編not/on の公開記事をより、 止まらせず(NOT DONE)、自走する(ALWAYS ON) ものにするため、ダイナミックな記事生成のパーツを検討する。その第2弾。 第1弾はこちら NotionデータベースのデータをAWS Lambda(Python)でプレーンなJSONに変…not/on
not/on