概要
ElysiaJS を使って、3 秒で API サーバを実装する方法を紹介します。
以下は ElysiaJS 公式ドキュメントの Cheat Sheet の引用です。
import { Elysia } from 'elysia'
new Elysia()
.get('/', () => 'Hello World')
.listen(3000)
これだけで http://localhost:3000 に GET /
のエンドポイントができました。
簡単ですね。
Cheat Sheet 笑えるくらいシンプルです。
この記事は読まなくても良いのでこれ読みましょう。
ElysiaJS とは
ElysiaJS は初心者でも簡単に使用できるよう設計された Bun フレームワークです。
TypeScript の型安全性と Bun の高速性を備えており、明瞭な実装を行うことが出来ます。
新進気鋭な Javascript Runtime である Bun のフレームワークということで、新しめなライブラリです。
まだ情報は少ないですが、その分モダンな開発体験を味わえるでしょう。
実際のユースケース
Discord の Webhook で Google カレンダーの予定を通知します。
FFXIV の新しい高難度コンテンツが実装されたからです。
- bun@1.1.37
- elysia@1.1.25
import { Elysia } from "elysia";
const app = new Elysia()
.use(swagger({ path: "/docs" }))
.get("/auth", getAuth)
.get("/oauth2callback", (context) => getOauth2callback(context))
.get("/today", getToday)
.post("/today", postToday)
.listen(process.env.API_PORT ?? 3000);
これでエンドポイントが 4 つできました。
ついでに Scalar UI による OpenAPI ドキュメントも生成されています。
.use(swagger({ path: "/docs" }))
ここです。
path
を指定しなかった場合は /swagger
に、
この例では /docs
にアクセスすることで、ドキュメントを確認することが出来ます。
それぞれのエンドポイントで関数呼び出しを行っています。
(全部を中に書いたら見辛いので)
実際の関数はこのようになっています。
import { google } from "googleapis";
const oauth2Client = new google.auth.OAuth2({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
redirectUri: process.env.GOOGLE_REDIRECT_URI,
});
const scopes = ["https://www.googleapis.com/auth/calendar.readonly"];
function getAuth() {
const authUrl = oauth2Client.generateAuthUrl({
access_type: "offline",
scope: scopes,
});
if (oauth2Client.credentials.access_token) {
const expired = oauth2Client.credentials.expiry_date
? new Date(oauth2Client.credentials.expiry_date)
: null;
return {
message: "Already authenticated!",
expired: expired?.toLocaleString(),
refreshTokenExists: !!refreshToken,
authUrl,
};
} else {
return {
message: "Authorize this app by visiting this URL",
authUrl,
};
}
}
GET /auth
で実行される getAuth()
では、
Google API の認証状態の確認と認証 URL の発行を行っています。
認証 URL にアクセスすると、Google アカウントからアプリに対するアクセス権を付与する画面に遷移します。
サードパーティのアプリで何かとログインを求められるあの画面です。
注目してほしいのはこの return
部分。
return {
message: "Already authenticated!",
expired: expired?.toLocaleString(),
refreshTokenExists: !!refreshToken,
authUrl,
};
適当なオブジェクトを投げ返すだけで、Elysia がいい感じの JSON に変換して Response してくれます。ありがとう。
もうひとつ見てみましょう。
import { Elysia, type Context } from "elysia";
async function getOauth2callback(context: Context) {
if (!context.query.code) {
return context.error(400, "No code provided");
}
const { tokens } = await oauth2Client.getToken(context.query.code);
if (tokens.refresh_token) {
refreshToken = tokens.refresh_token;
saveRefreshToken(tokens.refresh_token);
}
oauth2Client.setCredentials({ ...tokens, refresh_token: refreshToken });
const expired = tokens.expiry_date ? new Date(tokens.expiry_date) : null;
return {
message: "Authenticated!",
expired: expired?.toLocaleString(),
refreshTokenExists: !!refreshToken,
};
}
GET /oauth2callback
で呼び出される getOauth2callback()
では、
Google API の AccessToken の取得を行っています。
上述の GET /auth
で取得した認証 URL で認証をすると、
上でちゃっかり設定していた redirectUri
に code
のクエリパラメータ付きで飛ばされます。
その飛び先がこちらの GET /oauth2callback
となっており、受け取った code
から AccessToken
の取得を行っているというわけですが、大変めんどくさかったので詳細は割愛します。
見てほしいのはここ。
async function getOauth2callback(context: Context) {
if (!context.query.code) {
return context.error(400, "No code provided");
}
const { tokens } = await oauth2Client.getToken(context.query.code);
code
が渡されなかった場合はエラー、渡された場合はトークンの取得に進んでいます。
このように context
からリクエストを取得したり、レスポンスをカスタマイズして投げ返すことが出来ます。
この辺は Hono なども同様かと思いますので、とっつきやすさもあると思います。
GET /auth
と GET /oauth2callback
で認証したところで準備は完了。
あとは GET /today
で Google カレンダーから今日の予定を取得、
POST /today
で Discord の Webhook にメッセージを送りつけるだけです。
ところで、予定のリマインドをするのに、まさか手動でなんてやりませんよね。
パーティーメンバーへのリマインドのし忘れが多々あったため、なんとかして自動化したいと考えたのが今回のアプリです。
今回は毎日 18 時にリマインドを行います。
このような定時実行には cron がよく利用されると思いますが、なんとこれ、一緒に出来ちゃいます。しかも型安全で。
そう、Elysia ならね。
import { cron } from "@elysiajs/cron";
import { treaty } from "@elysiajs/eden";
const api = treaty<typeof app>(process.env.API_URL ?? "");
new Elysia().use(
cron({
name: "reminder-event",
pattern: "0 18 * * *",
timezone: "Asia/Tokyo",
async run() {
console.log(new Date(), "Cron job started");
await api.today.post();
console.log(new Date(), "Cron job finished");
},
})
);
Eden は Elysia インスタンスへ簡単に接続できるクライアントです。
TypeScript の型推論を利用しており、型安全に Request/Response を利用できます。
以下のように、簡単に POST /today
を呼び出す事ができます。
const api = treaty<typeof app>(process.env.API_URL ?? "");
await api.today.post();
Cron Plugin については言わずもがな、
指定した pattern
のタイミングで、run()
の処理を実行してくれます。
所感
行き当たりばったりで上述の機能を急造したため、まだまだ理解しきれていない部分も多いですが、
とにかくわかりやすく使いやすい、学習コストの低いフレームワークだなと感じました。
WebSocket や UnitTest も作りやすいよう用意されているようなので、機会があればまた触ってみたいと思います。
ということで、最近気になっている ElysiaJS のオススメでした。