My App
BoardKitExamples

Full-Stack Example

Complete backend and frontend working together

This example shows how to wire up the NestJS backend and React frontend as a complete application.

Architecture

React App (port 5173)
  ├── HTTP requests → NestJS API (port 3000)
  └── WebSocket → NestJS Gateway (port 3000/board)

NestJS API (port 3000)
  ├── Prisma → PostgreSQL
  ├── Local filesystem → ./uploads/
  └── In-memory event log

Backend: app.module.ts

import { Module } from '@nestjs/common';
import { BoardModule, InMemoryEventLogStorage } from '@hfu.digital/boardkit-nestjs';
import { PrismaBoardAdapter } from '@hfu.digital/boardkit-nestjs';
import { PrismaService } from './prisma.service';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
import { LocalAssetStorage } from './assets/local-asset-storage';

const prisma = new PrismaService();

@Module({
    imports: [
        BoardModule.register({
            storage: new PrismaBoardAdapter({
                board: prisma.board,
                page: prisma.page,
                element: prisma.element,
                boardMember: prisma.boardMember,
                shareLink: prisma.shareLink,
            }),
            assetStorage: new LocalAssetStorage('./uploads'),
            eventLogStorage: new InMemoryEventLogStorage(),
            authGuard: new JwtAuthGuard(process.env.JWT_SECRET ?? 'dev-secret'),
        }),
    ],
})
export class AppModule {}

Backend: main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    // Enable CORS for the React dev server
    app.enableCors({
        origin: 'http://localhost:5173',
        credentials: true,
    });

    await app.listen(3000);
    console.log('API: http://localhost:3000');
    console.log('WebSocket: ws://localhost:3000/board');
}
bootstrap();

Frontend: main.tsx

import { createRoot } from 'react-dom/client';
import { BoardKitProvider } from '@hfu.digital/boardkit-react';
import { Whiteboard } from './Whiteboard';

const token = 'your-jwt-token'; // from your auth flow

createRoot(document.getElementById('root')!).render(
    <BoardKitProvider
        config={{
            apiUrl: 'http://localhost:3000',
            wsUrl: 'ws://localhost:3000/board',
            authToken: token,
        }}
    >
        <Whiteboard boardId="my-board-id" />
    </BoardKitProvider>,
);

Frontend: Whiteboard.tsx

import {
    BoardCanvas,
    Toolbar,
    useBoard,
    useCollaboration,
    useTool,
    useKeyboardShortcuts,
} from '@hfu.digital/boardkit-react';

export function Whiteboard({ boardId }: { boardId: string }) {
    const { board, loading, error } = useBoard(boardId);
    const { connectionState } = useCollaboration(boardId);
    const { activeTool, setTool } = useTool();
    useKeyboardShortcuts();

    if (loading) return <p>Loading...</p>;
    if (error) return <p>Error: {error}</p>;

    return (
        <div style={{ width: '100vw', height: '100vh' }}>
            <header style={{ padding: 8, borderBottom: '1px solid #ccc', display: 'flex', gap: 16 }}>
                <strong>{board?.name}</strong>
                <span>Connection: {connectionState}</span>
            </header>
            <Toolbar activeTool={activeTool} onToolChange={setTool} />
            <BoardCanvas />
        </div>
    );
}

Database Setup

1. Initialize Prisma

bunx prisma init

2. Add the BoardKit schema

Copy the schema from the Prisma Adapter docs into your prisma/schema.prisma.

3. Run migrations

bunx prisma migrate dev --name init

Running the Full Stack

# Terminal 1: Backend
cd backend && bun run start:dev

# Terminal 2: Frontend
cd frontend && bun run dev

Creating a Board (via API)

curl -X POST http://localhost:3000/boards \
  -H "Authorization: Bearer your-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Whiteboard", "ownerId": "user-1"}'

Use the returned board ID in your frontend's boardId prop.

Testing with In-Memory Storage

For quick local development without a database:

import {
    BoardModule,
    InMemoryBoardStorage,
    InMemoryAssetStorage,
    InMemoryEventLogStorage,
    MockAuthGuard,
} from '@hfu.digital/boardkit-nestjs';

@Module({
    imports: [
        BoardModule.register({
            storage: new InMemoryBoardStorage(),
            assetStorage: new InMemoryAssetStorage(),
            eventLogStorage: new InMemoryEventLogStorage(),
            authGuard: new MockAuthGuard(),
        }),
    ],
})
export class AppModule {}

This setup requires no database and accepts any authentication token — ideal for prototyping.

On this page