🧯
Cloud Functions for Firebase上のHonoでBasic/Bearer Authするだけの記事
Cloudflare Workers上で動かないライブラリがあったので、急遽Cloud Functionsを使って簡単なAPIを作成しました。
普通にできたのですが、元々Honoを利用して開発しているのでできれば合わせたいです。
調べるとこの方の記事にあるように他のLambda等のadapterを真似て、それをCF用に書き直すことでHonoが動いたとのことで、自分も参考にして動かしてみました。
まずHonoをCloud Functionsで動かす
使い方は簡単で、以下のhander.tsをindex.tsの隣に置き、index.tsでHonoを普段通りに書き、最後にhanderでラップするだけです。
hander.ts
import { Request as FunctionRequest, Response } from "firebase-functions";
import { Hono } from "hono";
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
export const handler = (app: Hono<any>) => {
return async (req: FunctionRequest, resp: Response) => {
const url = new URL(`${req.protocol}://${req.hostname}${req.url}`);
const headers = new Headers();
// biome-ignore lint/complexity/noForEach: <explanation>
Object.keys(req.headers).forEach((k) => {
headers.set(k, req.headers[k] as string);
});
const body = req.body;
const newRequest = ["GET", "HEAD"].includes(req.method)
? new Request(url, {
headers,
method: req.method,
})
: new Request(url, {
headers,
method: req.method,
body: Buffer.from(
typeof body === "string" ? body : JSON.stringify(body || {}),
),
});
const res = await app.fetch(newRequest);
const contentType = res.headers.get("content-type");
if (contentType?.includes("application/json")) {
resp.json(await res.json());
} else {
resp.send(await res.text());
}
};
};
index.ts
import * as functions from "firebase-functions";
import { Hono } from "hono";
import { handler } from "./handler";
const app = new Hono();
app.get("/", (c) => c.json({ message: "Hono!" }));
// この`api`がfunction名になる
export const api = functions.https.onRequest(handler(app));
そして実行してみると…
$ npm run serve
...
✔ functions: Loaded functions definitions from source: api.
✔ functions[us-central1-api]: http function initialized (http://127.0.0.1:5001/<YOUR_APP_NAME>/us-central1/api).
...
のようにURLが表示され、
$ curl http://127.0.0.1:5001//<YOUR_APP_NAME>/us-central1/api
{"message":"Hono!"}
と実行できました!
Basic/Bearer Authを施す
ここで、このAPIに対して簡単な認証を施したいと思ったときは、Hono側のmiddlewareを用いて簡単に実現できます。
Basic Authの場合
以下のように2箇所追加するだけです。
index.ts
import * as functions from "firebase-functions";
import { Hono } from "hono";
import { handler } from "./handler";
import { basicAuth } from "hono/basic-auth"; // 追加
const app = new Hono();
// 追加
app.use(
"*",
basicAuth({
username: "hono",
password: "hono",
}),
);
app.get("/", (c) => c.json({ message: "Hono!" }));
export const api = functions.https.onRequest(handler(app));
以下のように認証が効いていることがわかります。
# 素で叩く
$ curl http://127.0.0.1:5001/hoelai-13ecf/us-central1/api
Unauthorized
# user/passを指定して叩く
$ curl -u hono:hono http://127.0.0.1:5001/hoelai-13ecf/us-central1/api
{"message":"Hono!"}
Bearer Authの場合
ほぼ一緒です。tokenは hoge
にしてます。
index.ts
import * as functions from "firebase-functions";
import { Hono } from "hono";
import { handler } from "./handler";
import { bearerAuth } from "hono/bearer-auth"; // 追加
const app = new Hono();
app.use("*", bearerAuth({ token: "hoge" })); // 追加
app.get("/", (c) => c.json({ message: "Hono!" }));
export const api = functions.https.onRequest(handler(app));
こちらも以下のように認証が効いていることがわかります。
# 素で叩く
$ curl http://127.0.0.1:5001/hoelai-13ecf/us-central1/api
Unauthorized
# headerを追加して叩く
$ curl -H 'Authorization: Bearer hoge' http://127.0.0.1:5001/hoelai-13ecf/us-central1/api
{"message":"Hono!"}
おわり
つまりCloud FunctionsでもHonoで色々できそうですね!
以上です。
Discussion