Getting Started
Application Modules
Application Modules — Getting Started documentation for BCP Framework.
Last updated: 2026-08-29T17:13:55
Application Modules
BCP applications can import project files through the project-root @/ alias.
import {
db,
} from "@/lib/database";The generated tsconfig.json maps @/* to the project root. BCP also installs a Node.js resolver during development so the same alias works in SSR, loader and API route modules. Production builds resolve the alias through the application bundler.
Client modules
A page or shared module that uses React client hooks must begin with the "use client" directive.
"use client";
import {
useEffect,
useState,
} from "react";
export default function UsersPage() {
const [users, setUsers] =
useState([]);
useEffect(
() => {
void fetch("/api/users")
.then(
(response) =>
response.json()
)
.then(
setUsers
);
},
[]
);
return (
<pre>
{JSON.stringify(users, null, 2)}
</pre>
);
}BCP checks client-reachable page/layout module graphs at development startup and production build time. Using React client hooks without "use client" causes a boundary error.
Client island files (*.island.ts and *.island.tsx) are already explicit client entry points and keep their existing island semantics.
Server-only modules
Use bcp/server-only in modules that contain database clients, secrets, filesystem access, or other code that must never enter a browser bundle.
import "bcp/server-only";
import mysql from "mysql2/promise";
export const db =
mysql.createPool(
process.env.DATABASE_URL ??
""
);Database helpers generated by create-bcp-app include this marker automatically.
Do not import a server-only module directly from a page or client component:
// Not allowed in a page/client graph
import {
db,
} from "@/lib/database";For browser-driven data operations, put the database operation behind an API route:
// app/api/users/route.ts
import {
db,
} from "@/lib/database";
export async function GET() {
const [rows] =
await db.query(
"SELECT * FROM users"
);
return Response.json({
data: rows,
});
}Then call the API from the client:
const response =
await fetch(
"/api/users"
);
const result =
await response.json();Server data loaders
BCP 0.1.9 adds a server-only page data path. A loader.ts next to page.tsx is not part of the hydrated page graph and may import server-only database helpers directly.
app/users/
├─ loader.ts
└─ page.tsx// app/users/loader.ts
import {
db,
} from "@/lib/database";
export async function loader() {
const [rows] =
await db.query(
"SELECT id, name FROM users"
);
return {
users: rows,
};
}The page consumes only the serializable result:
"use client";
import {
useLoaderData,
} from "bcp";
export default function UsersPage() {
const data =
useLoaderData<{
users: Array<{
id: number;
name: string;
}>;
}>();
return (
<pre>
{JSON.stringify(
data.users,
null,
2
)}
</pre>
);
}The loader itself stays server-only; only its validated JSON-safe result is serialized for hydration. See Server Data Loaders for request context, authentication, redirects and cache behavior.
Current boundary model
BCP 0.1.x is not a React Server Components implementation. Pages and layouts can participate in browser hydration, so server-only dependencies must remain behind explicit server execution paths: API routes, route-level server data loaders, middleware, or other framework server runtimes. The boundary checks are designed to prevent accidental database or secret-bearing code from entering client bundles.