Open8

Rust , Headless CMS作成メモ Workers-rs

knaka Tech-Blogknaka Tech-Blog

概要

  • Rust , Headless CMS試作メモになります。
  • cloudflare Workers-rs で、実装
  • データは、 D1 database 保存
  • 管理画面は、 React フルスタック
  • APIは、ほぼ 100% Rust言語
  • 小規模アプリ等などの、バックエンドにしたい。
  • フロントは 生成AI, バックは Headless CMS使用で、時短できれば

[ 公開 2025/09/04 ]


環境

  • cloudflare Workers-rs
  • D1 database
  • rustc 1.88.0 , cargo 1.88.0
  • node 22

書いたコード

https://github.com/kuc-arc-f/headless-2025/tree/main/headless-2025

  • dev-start
npm run build
npm run dev

設定方法

  • headless-2025/wrangler.toml
  • db 接続設定
  • API_KEY認証の、KEYを決める
  • 管理画面ログイン: USER_NAME , PASSWORD
[vars]
USER_NAME = "user1@example.com"
PASSWORD = "1234"
API_KEY = "123"

https://github.com/kuc-arc-f/headless-2025/blob/main/headless-2025/wrangler.toml


API使い方


  • 例: List
  • node.js
  • your-key: API_KEY , wrangler.toml に設定した API_KEY
  • content: データ種類
const start = async function() {
  try{
    const response = await fetch("http://localhost:8787/api/data/list?content=todo", {
      method: 'GET',
      headers: {
        'Authorization': 'your-key',
      }
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text)
      throw new Error('Failed to item');
    }
    const json = await response.json();
    console.log(json)
  }catch(e){console.log(e)}
}
start();

  • Create

  • your-key: API_KEY , wrangler.toml に設定した API_KEY

  • content: データ種類

  • data: json データ


const start = async function() {
  try{
      const item = {
        content: "test1",
        data: JSON.stringify({
          "title": "tit-1",
          "body": "body-1",
        })
      }
      const response = await fetch("http://localhost:8787/api/data/create", {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'your-key',
      },
      body: JSON.stringify(item),
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text);
      throw new Error('Failed to create item');
    }
    return response.json();
  }catch(e){console.log(e)}
}
start();

knaka Tech-Blogknaka Tech-Blog

Rust Axum , Headless CMSの例

  • Axum使用、Headless CMS作成メモになります。
  • 今回は、サバーレスではなく。セルフホスト等でデプロイ想定です。

環境

  • Axum
  • SQLite database
  • rustc 1.88.0 , cargo 1.88.0
  • node 20
  • react

書いたコード

https://github.com/kuc-arc-f/headless-2025/tree/main/headless-2025-rs


  • dev-start
npm run build
npm run dev

設定方法

  • .env
  • API_KEY認証の、KEYを決める
  • 管理画面ログイン: USER_NAME , PASSWORD
API_KEY=123
USER_NAME = "user1@example.com"
PASSWORD = "1234"

  • table
CREATE TABLE IF NOT EXISTS hcm_data (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    content  TEXT NOT NULL,
    data  TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)

  • sqlite3で、 db作成
  • Dbname: cms.db
sqlite3 cms.db

API使い方


  • 例: List
  • node.js
  • your-key: API_KEY , .env に設定した API_KEY
  • content: データ種類
const start = async function() {
  try{
    const response = await fetch("http://localhost:3000/api/data/list?content=test1", {
      method: 'GET',
      headers: {
        'Authorization': 'your-key',
      }
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text)
      throw new Error('Failed to create item');
    }
    const json = await response.json();
    console.log(json)
  }catch(e){console.log(e)}
}
start();

  • Create

  • your-key: API_KEY , .env に設定した API_KEY

  • content: データ種類

  • data: json データ


const start = async function() {
  try{
      const item = {
        content: "test2",
        data: JSON.stringify({
          "title": "tit-22",
          "body": "body-22",
        })
      }
      const response = await fetch("http://localhost:3000/api/data/create", {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'your-key',
      },
      body: JSON.stringify(item),
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text);
      throw new Error('Failed to create item');
    }
    return response.json();
  }catch(e){console.log(e)}
}
start();


knaka Tech-Blogknaka Tech-Blog

PGLite WASM + Bun , Headless CMSの例

  • PGLite WASM、Headless CMS作成メモになります。
  • 今回は、サバーレスではなく。セルフホスト等でデプロイ想定です。

関連

  • 前のPGLite 記事 インストールなど

https://zenn.dev/knaka0209/scraps/b077514499e644


環境

  • PGLite WASM , popstgres 17.x
  • bun 1.2.20
  • react

書いたコード

https://github.com/kuc-arc-f/bun_41ex/tree/main/headless-2025-pg


  • db create
bun run db_init.ts

  • dev-start
npm run build
npm run dev

設定方法

  • .env
  • DATA_DIR: PGLite data folder
  • API_KEY認証の、KEYを決める
  • 管理画面ログイン: USER_NAME , PASSWORD
DATA_DIR="/path/cmsdata"
API_KEY=1234
USER_NAME = "user1@example.com"
PASSWORD = "123"

API使い方


  • 例: List
  • node.js
  • your-key: API_KEY , .env に設定した API_KEY
  • content: データ種類
const start = async function() {
  try{
    const response = await fetch("http://localhost:3000/api/data/list?content=test1", {
      method: 'GET',
      headers: {
        'Authorization': 'your-key',
      }
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text)
      throw new Error('Failed to item');
    }
    const json = await response.json();
    console.log(json)
  }catch(e){console.log(e)}
}
start();

  • Create

  • your-key: API_KEY , .env に設定した API_KEY

  • content: データ種類

  • data: json データ


const start = async function() {
  try{
      const item = {
        content: "test2",
        data: JSON.stringify({
          "title": "tit-22",
          "body": "body-22",
        })
      }
      const response = await fetch("http://localhost:3000/api/data/create", {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'your-key',
      },
      body: JSON.stringify(item),
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text);
      throw new Error('Failed to create item');
    }
    return response.json();
  }catch(e){console.log(e)}
}
start();


knaka Tech-Blogknaka Tech-Blog

Headless CMS , 接続アプリの例

  • Headless CMS API使用して、連携アプリ作成
  • bun + Reactで作成。

環境

  • bun 1.2.20
  • React

書いたコード

https://github.com/kuc-arc-f/bun_41ex/tree/main/headless-app-1


  • .env
  • EXTERNAL_API_URL: 接続API URRL
  • API_KEY: Headless CMS に設定した API_KEY
EXTERNAL_API_URL="http://localhost:8787"
API_KEY=123

  • dev-start
npm run build
npm run dev

  • 一覧
  • headless-app-1/src/client/Home.tsx

https://github.com/kuc-arc-f/bun_41ex/blob/main/headless-app-1/src/client/Home.tsx

  • 起動処理、リストデータ取得
  const fetchItems = async () => {
    try {
      setLoading(true);
      const data = await itemsApi.getAll(CONTENT);
      console.log(data);
      setItems(data);
    } catch (err) {
      setError('アイテムの取得に失敗しました');
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchItems();
  }, []);

  • API連携
  • headless-app-1/src/routes/todos.ts

https://github.com/kuc-arc-f/bun_41ex/blob/main/headless-app-1/src/routes/todos.ts

  • 外部API連携、レスポンスをフロント側に返す。
router.post('/list', async function(req: any, res: any) {
  try {
    const body = req.body;
    console.log(body)
    const url = process.env.EXTERNAL_API_URL;	
    const apikey = process.env.API_KEY;	
    const path = "/api/data/list?content=" + CONTENT 
    console.log("url=", url + path)
    const response = await fetch(url + path, {
      method: 'GET',
      headers: {
        'Authorization': apikey,
      }
    });    
    if(response.ok === false){
      console.error("Error, res.ok = NG");
      throw new Error("Error, res.ok = NG");
    }
    const json = await response.json();
    res.send({ret: 200, data: json.data});
  } catch (error) {
    console.error(error);
    res.sendStatus(500);
  }
});


knaka Tech-Blogknaka Tech-Blog

SQLite WASM , フロント側 項目ソートなど

  • フロント側内容になり。SQLite WASM + Headless CMS 構成
  • bun + Reactで作成。
  • 画面一覧で、項目指定ソートが難航したので、SQLiteにデータ追加し ソート実装
  • データは、メモリ保存。
  • 起動時に、APIから SQLiteにデータ登録( 項目は、一覧に見える項目のみ )

関連

https://www.npmjs.com/package/sql.js/v/1.13.0


環境

  • SQLite WASM (sql.js 1.13.0)
  • bun 1.2.20
  • React

書いたコード

https://github.com/kuc-arc-f/bun_41ex/tree/main/headless-app-2


  • .env
  • EXTERNAL_API_URL: 接続API URRL
  • API_KEY: Headless CMS に設定した API_KEY
EXTERNAL_API_URL="http://localhost:8787"
API_KEY=123

  • dev-start
npm run build
npm run dev

  • SQLite 初期化: 画面起動時
  • headless-app-2/src/client/Sort/db.ts

https://github.com/kuc-arc-f/bun_41ex/blob/main/headless-app-2/src/client/Sort/db.ts

  init: async function() {
    try{
      const sql = await initSqlJs({
          locateFile: file => `https://cdn.jsdelivr.net/npm/sql.js@1.13.0/dist/${file}`
      });
      const sqldb = new sql.Database();
      sqldb.run(`CREATE TABLE IF NOT EXISTS items (
          id TEXT,
          name TEXT,
          age INTEGER,
          weight INTEGER
        );`);
      return sqldb;
    }catch(e){
      console.log(e);
      throw new Error("error, init")
    }

  },


  • APIからデータ追加

  addItem: async function(db: any, values: any[]) {
    try{
      values.forEach((element) => {
        //console.log("id",  element.id);
        //console.log(element.data);
        let sql = `INSERT INTO items (id, name, age ,weight)
         VALUES 
         (
         '${element.id}', 
         '${element.data.name}' , 
         ${element.data.age} ,
         ${element.data.weight}
         );
        `;
        //console.log(sql);
        const res = db.run(sql);
      });
    }catch(e){
      console.log(e);
      throw new Error("error, addItem")
    }

  },


  • sort: 画面イベント処理から、指定列のソート
  sortItem: async function(db: any, colname: string,  order: string) {
    try{
      let order_sql = "ASC";
      if(order !== "asc"){
        order_sql = "DESC"
      }
      const sql = `SELECT id, name, age ,weight FROM items
      ORDER BY ${colname} ${order_sql}
      ;`;
      console.log(sql);

      const res = await db.exec(sql);
      console.log(res);
      if(!res[0]){
        return [];
      }
      const out = [];
      res[0].values.forEach((row) => {
        //console.log(row);
        let target = {
          id: row[0], 
          name: row[1], 
          age: row[2] ,
          weight: row[3]
        }
        out.push(target)
      })

      return out;
    }catch(e){
      console.log(e);
      throw new Error("error, sortItem")
    }

  },

knaka Tech-Blogknaka Tech-Blog

Postgres Rust Axum , Headless CMS

  • Postgres + Axum、Headless CMS作成メモになります。
  • 今回は、サバーレスではなく。セルフホスト等でデプロイ想定です。

環境

  • Axum
  • Postgres
  • rustc 1.88.0 , cargo 1.88.0
  • node 20

書いたコード

https://github.com/kuc-arc-f/headless-2025/tree/main/headless-pg


API使い方


  • .env
  • API_KEY: Headless CMS に設定した API_KEY
API_KEY=123

  • dev-start
npm run build
npm run dev

  • table

https://github.com/kuc-arc-f/headless-2025/blob/main/headless-pg/table.sql

CREATE TABLE IF NOT EXISTS hcm_data (
  id SERIAL NOT NULL,
  content TEXT NOT NULL,
  data TEXT NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT now(),
  updated_at TIMESTAMP NOT NULL DEFAULT now()
);

例: node.js: List
node.js
your-key: API_KEY , .env に設定した API_KEY
content: データ種類

const start = async function() {
  try{
    const response = await fetch("http://localhost:3000/api/data/list?content=test2&order=desc", {
      method: 'GET',
      headers: {
        'Authorization': 'your-key',
      }
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text)
      throw new Error('Failed to create item');
    }
    const json = await response.json();
    console.log(json)
  }catch(e){console.log(e)}
}
start();


  • create
  • Create
  • your-key: API_KEY , .env に設定した API_KEY
  • content: データ種類

const start = async function() {
  try{
      const item = {
        content: "test2",
        data: JSON.stringify({
          "title": "tit-22",
          "body": "body-22",
        })
      }
      const response = await fetch("http://localhost:3000/api/data/create", {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'your-key',
      },
      body: JSON.stringify(item),
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text);
      throw new Error('Failed to create item');
    }
    return response.json();
  }catch(e){console.log(e)}
}
start();


knaka Tech-Blogknaka Tech-Blog

GoLang + Postgres , Headless CMS

  • GoLang + Postgres、Headless CMS作成メモになります。
  • 今回は、サバーレスではなく。セルフホスト等でデプロイ想定です。

環境

  • go 1.24.4
  • Postgres
  • node 20

書いたコード

https://github.com/kuc-arc-f/golang_2ex/tree/main/headless_pg


API使い方


  • .env
  • API_KEY: Headless CMS に設定した API_KEY
API_KEY=123

  • dev-start
npm run build
go run .


  • 例: node.js: List
  • node.js
  • your-key: API_KEY , .env に設定した API_KEY
  • content: データ種類

const start = async function() {
  try{
    const response = await fetch("http://localhost:8080/api/data/list?content=test2&order=desc", {
      method: 'GET',
      headers: {
        'Authorization': 'your-key',
      }
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text)
      throw new Error('Failed to create item');
    }else{
      const json = await response.json();
      console.log(json)
    }
  }catch(e){console.log(e)}
}
start();



  • create
  • Create
  • your-key: API_KEY , .env に設定した API_KEY
  • content: データ種類

const start = async function() {
  try{
      const item = {
        content: "test2",
        data: JSON.stringify({
          "title": "tit-12d1",
          "body": "body-12d1",
        })
      }
      const response = await fetch("http://localhost:8080/api/data/create", {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'your-key',
      },
      body: JSON.stringify(item),
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text);
      throw new Error('Failed to create item');
    } else{
      console.log("OK");
    }
  }catch(e){console.log(e)}
}
start();



knaka Tech-Blogknaka Tech-Blog

Rust , Turso databse , Headless CMS

  • rust Turso databse , headlessCMS作成メモになります。
  • 今回は、サバーレスではなく。セルフホスト等でデプロイ想定です。

環境

  • Rust Axum
  • cargo 1.90.0
  • Turso SDK Rust
  • node 22

書いたコード

https://github.com/kuc-arc-f/headless-2025/tree/main/headless-turso


API使い方


  • .env
  • API_KEY: Headless CMS に設定した API_KEY
API_KEY=123
TURSO_DATABASE_URL=""
TURSO_AUTH_TOKEN=

  • dev-start
cargo run  --release

  • table , todoに書き込む場合
CREATE TABLE IF NOT EXISTS todo (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  data TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

  • 例: node.js: List
  • node.js
  • your-key: API_KEY , .env に設定した API_KEY
  • content: データ種類
const start = async function() {
  try{
    const response = await fetch("http://localhost:3000/api/data/list?content=todo&order=asc", {
      method: 'GET',
      headers: {
        'Authorization': 'your-key',
      }
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text)
      throw new Error('Failed to create item');
    }
    const json = await response.json();
    console.log(json)
  }catch(e){console.log(e)}
}
start();



  • Create
  • your-key: API_KEY , .env に設定した API_KEY
  • content: データ種類

const start = async function() {
  try{
      const item = {
        content: "todo",
        data: JSON.stringify({
          "title": "tit24",
          "body": "body24",
        })

      }
      const response = await fetch("http://localhost:3000/api/data/create", {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'your-key',
      },
      body: JSON.stringify(item),
    });
    if (!response.ok) {
      const text = await response.text();
      console.log(text);
      throw new Error('Failed to create item');
    }else{
      console.log("OK");
      const json = await response.json();
      console.log(json);
    }
  }catch(e){console.log(e)}
}
start();