組み込み
Next.js に avacast を組み込む手順
サーバーでセッションを作り、ブラウザで SDK に渡すまでを、App Router の構成で順に説明します。
用意するもの
- API キー: 管理画面の「API キー」で発行します。
- アバターの ID:
GET /v1/avatarsで一覧を取れます。 - SDK:
npm install avacast
アバターの一覧
curl https://api.avacast.jp/v1/avatars \
-H "Authorization: Bearer $AVACAST_API_KEY"API キーは .env.local に置き、NEXT_PUBLIC_ を付けません。付けるとブラウザに渡ってしまいます。
.env.local
AVACAST_API_KEY=sk_live_...
AVACAST_AVATAR_ID=avt_xxxxxxxxセッションを作る Route Handler
サーバー側で POST /v1/sessions を呼び、ブラウザに必要な値だけを返します。ブラウザに渡すのは client_token と webrtc で、API キーは返しません。
app/api/avacast/session/route.ts
export async function POST() {
// 実際には、ログイン中の利用者だけが呼べるように認証を挟む
const res = await fetch('https://api.avacast.jp/v1/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.AVACAST_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ avatar_id: process.env.AVACAST_AVATAR_ID }),
});
const body = await res.json();
if (!res.ok) {
return Response.json({ error: body.error.code }, { status: res.status });
}
const { session_id, client_token, webrtc } = body;
return Response.json({ session_id, client_token, webrtc });
}課金はセッションを開いていた時間です。使い終わったら閉じられるように、終了用の Route Handler も作っておきます。Next.js 16 では params が Promise なので await します。
app/api/avacast/session/[id]/route.ts
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
await fetch(`https://api.avacast.jp/v1/sessions/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.AVACAST_API_KEY}` },
});
return new Response(null, { status: 204 });
}終了を呼ばなかった場合も、プランの最大長か、話さない時間が続いたとき (既定 180 秒) に自動で閉じます。
映像を出すクライアントコンポーネント
SDK はブラウザの WebRTC を使うので、'use client' のコンポーネントの useEffect の中で動かします。attach は start の前でも後でも呼べます。
app/components/avatar.tsx
'use client';
import { AvacastSession } from 'avacast';
import { useEffect, useRef, useState } from 'react';
export function Avatar() {
const videoRef = useRef<HTMLVideoElement>(null);
const [session, setSession] = useState<AvacastSession | null>(null);
useEffect(() => {
let s: AvacastSession | null = null;
let sessionId: string | null = null;
let cancelled = false;
(async () => {
const res = await fetch('/api/avacast/session', { method: 'POST' });
const { session_id, client_token, webrtc } = await res.json();
if (cancelled) {
// 作り終える前に画面を離れた (StrictMode の 1 回目もここ)
void fetch(`/api/avacast/session/${session_id}`, { method: 'DELETE' });
return;
}
sessionId = session_id;
s = new AvacastSession(client_token, {
signalingUrl: webrtc.signaling_url,
iceServers: webrtc.ice_servers,
eventsUrl: webrtc.events_url,
});
if (videoRef.current) s.attach(videoRef.current);
s.on('session.ready', () => setSession(s));
await s.start();
})();
return () => {
cancelled = true;
void s?.stop();
// 画面を離れたらセッションを閉じる (keepalive でページ遷移中も送る)
if (sessionId) {
void fetch(`/api/avacast/session/${sessionId}`, { method: 'DELETE', keepalive: true });
}
};
}, []);
return (
<div>
<video ref={videoRef} autoPlay playsInline />
<button
type="button"
disabled={!session}
onClick={() => session?.speak('ご相談ありがとうございます。')}
>
話させる
</button>
</div>
);
}speakは映像が届いてから (session.readyの後) 呼びます。その前に呼ぶとsession_not_connectedになります。- 開発中は React の StrictMode で
useEffectが 2 回走ります。1 回目のセッションは cleanup で閉じるので、残りません。 - SDK の
speakはclient_tokenで認証するので、ブラウザから呼んでも API キーは出ません。
ページに置く
app/page.tsx
import { Avatar } from './components/avatar';
export default function Page() {
return <Avatar />;
}ここまでで、ページを開くとアバターの映像が表示され、ボタンで話します。話させる文章をサーバーで作る場合は、ブラウザの speak の代わりにサーバーから POST /v1/sessions/:id/speak を呼びます。詳しくはドキュメントを参照してください。
月 30 分まで無料
カード登録なしで始められます。