【Node.js/TypeScript】Express × Supabase(Postgres)で認証付き自作APIを構築してRenderに無料デプロイする全手順

Node.js

「フロントエンドの開発をしていて、自分だけのオリジナル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での設定手順

  1. Renderにログインし、「New +」 -> 「Web Service」 を選択。
  2. 対象のGitHubリポジトリ(PrivateでOK)を Connect する。
  3. 設定項目を以下のように入力:
    • Build Command: npm install && npm run build
    • Start Command: npm run start
  4. 「Environment Variables」 に、Supabaseの接続情報(DATABASE_URL)や、自分で決めた合言葉(MY_SECRET_TOKEN)を登録する。
  5. 「Deploy Web Service」 をクリックして数分待てば公開完了!

💡 無料プランのプチ注意点 Renderの無料プランは、しばらくアクセスがないとサーバーが自動でスリープします。そのため、しばらく経ってからの最初の1回目のリクエストだけ、サーバーが起動するまでに50秒〜1分ほど時間がかかりますが、これは仕様なので安心してください。

まとめ:自作API×外部DBの組み合わせは無限大!

Expressでのファイル分割による綺麗な設計、シンプルなトークン認証、そしてSupabaseとの連携を行うことで、非常にセキュアで拡張性の高いAPIを爆速で構築できました。

一度この仕組みを作ってしまえば、タスク管理アプリや冷蔵庫の在庫管理アプリなど、あらゆるWebアプリケーションに応用が可能です。ぜひ皆さんも自分だけのAPIを作って、色々なサービスと連携させてみてください!

コメント

タイトルとURLをコピーしました