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_jaweather_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 APIDummyJSON Quotes APINumbers 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