「フロントエンドの開発をしていて、自分だけのオリジナルAPIが欲しくなったことはありませんか?」
本記事では、Node.js(TypeScript)とExpressを使い、データの保存先にはSupabase(PostgreSQL)を採用した、独自のAPI(/search や /add)を構築する方法を解説します。
さらに、セキュリティのためのシンプルなAPIキー認証の実装から、フロントエンドからのfetch関数での呼び出し方、そして最後にRender(レンダー)を使って無料で世界に公開(デプロイ)する手順までを網羅しました。
この記事を読めば、バックエンドの知識が少なくても、自分だけの認証付きAPIサーバーを公開できるようになります!
1. 全体の構成とフォルダ設計
今回は、APIの規模が大きくなってもコードが散らからないよう、機能(処理)ごとにファイルを分割する設計を採用します。
📁 フォルダ構成
📁 my-api-project
├── 📁 routes
│ ├── 📄 search.ts (検索系APIの処理)
│ └── 📄 addProduct.ts (商品追加APIの処理)
├── 📄 db.ts (Supabase/PostgreSQLとの接続設定)
└── 📄 server.ts (すべてのルートをまとめる親玉ファイル)
このようにファイルを分けておくことで、「検索のバグを直したい時は search.ts だけを見ればいい」という、メンテナンス性の高いコードになります。
2. 【サーバー側】Expressでファイル分割とAPIの実装
まずは、商品を追加するAPI(/api/add)を実装します。今回はセキュリティを考慮し、「あらかじめ決めておいた合言葉(APIキー)が一致しているか」をチェックするシンプルな認証も組み込みます。
📄 routes/addProduct.ts
TypeScript
import express, { type Request, type Response } from "express";
import { pool } from "../db.ts";
const router = express.Router();
// 自分で決めた秘密の合言葉(Renderの環境変数から読み込む)
const MY_SECRET_TOKEN = process.env.MY_SECRET_TOKEN || "your-local-dummy-token";
router.post("/", async (req: Request, res: Response): Promise<any> => {
// 1. リクエストのヘッダーからトークンを取り出す
const clientToken = req.headers["x-api-key"];
// 2. トークンの一致チェック(簡易認証)
if (clientToken !== MY_SECRET_TOKEN) {
res.status(401).json({ error: "認証エラー:トークンが一致しません" });
return;
}
// 3. 一致していればデータベース(Supabase)へ挿入
const { jancode, name } = req.body;
try {
// Supabase側でidやcreated_atのデフォルト値(gen_random_uuid()やnow())を
// 設定していれば、ここではjancodeとnameを送るだけで自動生成されます!
const dbResponse = await pool.query(
"INSERT INTO products (jancode, name) VALUES ($1, $2) RETURNING *",
[jancode, name],
);
res.json(dbResponse.rows[0]);
} catch (error) {
console.error("Error inserting product:", error);
res.status(500).json({ error: "Failed to insert product" });
}
});
export default router;
📄 server.ts(親玉ファイル)
ここで、送られてきたJSONを解析するための express.json() と、分割したルーターを紐付けます。
TypeScript
import express from "express";
import cors from "cors";
import addProductRouter from "./routes/addProduct.ts";
const app = express();
app.use(cors()); // フロントエンドからのCORS制限を回避
app.use(express.json()); // ⚠️重要: req.bodyのJSONをパースする設定
// 各ルーターの登録
app.use("/api/add", addProductRouter);
const PORT = process.env.PORT || 3000;
app.use('/api/add', addProductRouter);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
3. 【フロントエンド側】標準の fetch 関数でAPIを叩く
フロントエンドからAxiosなどの外部ライブラリを使わず、ブラウザ標準の fetch 関数を使ってデータを送信する方法です。
fetch を使う際は、「body を文字列化すること」と「401などのエラーを手動でハンドリングすること」がポイントです。
Typescrypt
async function addProduct() {
try {
const response = await fetch("https://あなたのAPIのURL.onrender.com/api/add", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "my-super-secret-pass-123" // サーバーと一致する合言葉
},
body: JSON.stringify({
jancode: 1234567890,
name: "テスト商品"
})
});
// fetchはHTTPエラー(401や500)でもcatchに飛ばないため、okプロパティでチェック
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `HTTPエラー: ${response.status}`);
}
const data = await response.json();
console.log("追加成功:", data); // Supabaseで自動生成されたidやcreated_atも含まれます
} catch (error) {
console.error("エラーが発生しました:", error.message);
}
}
4. Render(レンダー)へ安全にデプロイする手順
作成したAPIリポジトリを、無料で利用できるホスティングサービス「Render」にデプロイします。
🔑 最大のポイント:リポジトリは「非公開(Private)」でOK!
GitHubのリポジトリは非公開(Private)のままで全く問題ありません。RenderとGitHubを連携すれば、安全に非公開コードを読み込んでデプロイしてくれます。パスワード等の漏洩を防ぐためにも非公開を強く推奨します。
🚀 Renderでの設定手順
- Renderにログインし、「New +」 -> 「Web Service」 を選択。
- 対象のGitHubリポジトリ(PrivateでOK)を Connect する。
- 設定項目を以下のように入力:
- Build Command:
npm install && npm run build - Start Command:
npm run start
- Build Command:
- 「Environment Variables」 に、Supabaseの接続情報(
DATABASE_URL)や、自分で決めた合言葉(MY_SECRET_TOKEN)を登録する。 - 「Deploy Web Service」 をクリックして数分待てば公開完了!
💡 無料プランのプチ注意点 Renderの無料プランは、しばらくアクセスがないとサーバーが自動でスリープします。そのため、しばらく経ってからの最初の1回目のリクエストだけ、サーバーが起動するまでに50秒〜1分ほど時間がかかりますが、これは仕様なので安心してください。
まとめ:自作API×外部DBの組み合わせは無限大!
Expressでのファイル分割による綺麗な設計、シンプルなトークン認証、そしてSupabaseとの連携を行うことで、非常にセキュアで拡張性の高いAPIを爆速で構築できました。
一度この仕組みを作ってしまえば、タスク管理アプリや冷蔵庫の在庫管理アプリなど、あらゆるWebアプリケーションに応用が可能です。ぜひ皆さんも自分だけのAPIを作って、色々なサービスと連携させてみてください!


コメント