Integration guide — React
End-to-end walkthrough: from getting your API key to a live multiplayer game in your React app.
Get your API key
Register at beta-gamer.com/register. You'll receive a live key (bg_live_…) and a test key (bg_test_…). Use the test key during development — sessions are in-memory only and never hit the database.
Install the SDK
npm install @beta-gamer/reactCreate a session (your backend)
Call POST /v1/sessions from your server. Pass your player's ID and display name. Return the sessionToken to your frontend.
// e.g. Next.js API route, Express, etc.
const res = await fetch('https://api.beta-gamer.com/v1/sessions', {
method: 'POST',
headers: {
'Authorization': 'Bearer bg_live_xxxx', // your secret key — server only
'Content-Type': 'application/json',
},
body: JSON.stringify({
game: 'chess', // 'chess' | 'checkers' | 'connect4' | 'tictactoe'
matchType: 'matchmaking', // 'matchmaking' | 'bot' | 'private'
players: [{ id: user.id, displayName: user.name }],
}),
});
const { sessionToken, sessionId } = await res.json();
// send sessionToken to your React frontendWrap your game UI in BetaGamerProvider
Pass the sessionToken you received from your backend. The provider decodes the token, connects the socket, and makes everything available via hooks.
import { BetaGamerProvider } from '@beta-gamer/react';
import { ChessGame } from './ChessGame';
export default function GamePage({ sessionToken }: { sessionToken: string }) {
return (
<BetaGamerProvider
token={sessionToken}
serverUrl="https://api.beta-gamer.com"
>
<ChessGame />
</BetaGamerProvider>
);
}Join matchmaking and listen to events
Use useSocket() and useSession() inside the provider. Emit matchmaking:join once the socket is ready, then listen for game:started.
import { useEffect, useRef, useState } from 'react';
import { useSocket, useSession } from '@beta-gamer/react';
export function ChessGame() {
const socket = useSocket();
const session = useSession();
const myPlayerId = session.players[0]?.id ?? '';
const roomIdRef = useRef('');
const [status, setStatus] = useState('Waiting for opponent…');
useEffect(() => {
if (!socket) return;
// Join matchmaking as soon as socket is ready
const join = () => socket.emit('matchmaking:join', {
username: session.players[0]?.displayName,
playerId: myPlayerId,
});
if (socket.connected) join(); else socket.once('connect', join);
socket.on('game:started', ({ roomId, players }) => {
roomIdRef.current = roomId;
setStatus('Game started!');
});
socket.on('game:over', ({ winner, reason }) => {
setStatus(winner === myPlayerId ? 'You won!' : winner ? 'You lost' : 'Draw');
});
return () => {
socket.off('connect', join);
socket.off('game:started');
socket.off('game:over');
};
}, [socket]);
return <p>{status}</p>;
}Handle session token on page load
Session tokens are single-use. Before rendering the provider, validate the token so refreshing a finished game shows the result instead of trying to reconnect.
import { useEffect, useState } from 'react';
import { BetaGamerProvider } from '@beta-gamer/react';
export default function GamePage({ token, sessionId }: { token: string; sessionId: string }) {
const [status, setStatus] = useState<'checking' | 'ok' | 'ended'>('checking');
useEffect(() => {
fetch(`/api/v1/sessions/validate?token=${token}`)
.then(r => r.json())
.then(d => setStatus(d.status === 'ended' ? 'ended' : 'ok'))
.catch(() => setStatus('ok'));
}, [token]);
if (status === 'checking') return <p>Loading…</p>;
if (status === 'ended') return (
<div>
<p>This session has already ended.</p>
<a href="/play">Play again</a>
</div>
);
return (
<BetaGamerProvider token={token} serverUrl="https://api.beta-gamer.com">
<ChessGame />
</BetaGamerProvider>
);
}Receive webhooks (optional)
When a game ends, Beta Gamer POSTs a signed payload to your webhookUrl. Set it in your dashboard settings or via PATCH /api/settings.
// Your webhook endpoint
app.post('/webhooks/beta-gamer', (req, res) => {
const { event, sessionId, winner, reason } = req.body;
// event: 'game.ended'
// winner: your tenant userId (players[0].id) or null for draw
res.sendStatus(200);
});