bosia
v0.1.10
Published
A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing inspired by SvelteKit. No Node.js, no Vite, no adapters.
Maintainers
Readme
Bosia
Full documentation: bosia.bosapi.com
A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS.
File-based routing inspired by SvelteKit, built on top of the Bun runtime and ElysiaJS HTTP server. No Node.js, no Vite, no adapters.
Features
- File-based routing —
+page.svelte,+layout.svelte,+server.ts, route groups, dynamic segments, catch-all routes - Server-side rendering — every page is rendered on the server with full hydration
- Server loaders —
+page.server.tsand+layout.server.tswithparent()data threading - API routes —
+server.tsexports HTTP verbs (GET,POST,PUT,PATCH,DELETE,OPTIONS) - Middleware hooks —
hooks.server.tswithsequence()for auth, logging, locals - Dev server with HMR — file watcher + SSE browser reload, no page blink
- Tailwind CSS v4 — compiled at build time, shadcn-inspired design tokens out of the box
- CLI —
bosia create,bosia dev,bosia build,bosia add,bosia feat
Quick Start
# Scaffold a new project
bun x bosia create my-app
cd my-app
# Start development
bun run dev
# Build for production
bun run build
bun run startTech Stack
| Layer | Technology | |-------|------------| | Runtime | Bun | | HTTP Server | ElysiaJS | | UI | Svelte 5 (Runes) | | CSS | Tailwind CSS v4 | | Bundler | Bun.build |
Routing Conventions
Files in src/routes/ map to URLs automatically.
| File | Purpose |
|------|---------|
| +page.svelte | Page component |
| +layout.svelte | Layout that wraps child pages |
| +page.server.ts | Server loader for a page |
| +layout.server.ts | Server loader for a layout |
| +server.ts | API endpoint (export HTTP verbs) |
Dynamic Routes
| Pattern | Matches |
|---------|---------|
| [param] | /blog/hello → params.param = "hello" |
| [...rest] | /a/b/c → params.rest = "a/b/c" |
Route Groups
Wrap a directory in parentheses to share a layout without affecting the URL:
src/routes/
└── (marketing)/
├── +layout.svelte # shared layout
├── +page.svelte # /
└── about/
└── +page.svelte # /aboutServer Loaders
// src/routes/blog/[slug]/+page.server.ts
import type { LoadEvent } from "bosia";
export async function load({ params, url, locals, fetch, parent }: LoadEvent) {
const parentData = await parent(); // data from layout loaders above
return {
post: await getPost(params.slug),
};
}Data returned is passed as the data prop to +page.svelte:
<script lang="ts">
let { data } = $props();
// data.post, data.params ...
</script>API Routes
Export named HTTP verb functions from +server.ts:
// src/routes/api/items/+server.ts
import type { RequestEvent } from "bosia";
export function GET({ params, url, locals }: RequestEvent) {
return Response.json({ items: [] });
}
export async function POST({ request }: RequestEvent) {
const body = await request.json();
return Response.json({ created: body }, { status: 201 });
}Middleware Hooks
Create src/hooks.server.ts to intercept every request:
import { sequence } from "bosia";
import type { Handle } from "bosia";
const authHandle: Handle = async ({ event, resolve }) => {
event.locals.user = await getUser(event.request);
return resolve(event);
};
const loggingHandle: Handle = async ({ event, resolve }) => {
const res = await resolve(event);
console.log(`${event.request.method} ${event.url.pathname} ${res.status}`);
return res;
};
export const handle = sequence(authHandle, loggingHandle);locals set here are available in every loader and API handler.
Public API
import { cn, sequence } from "bosia";
import type { RequestEvent, LoadEvent, Handle } from "bosia";| Export | Description |
|--------|-------------|
| cn(...classes) | Tailwind class merge utility (clsx + tailwind-merge) |
| sequence(...handlers) | Compose multiple Handle middleware functions |
| RequestEvent | Type for API route and hook handlers |
| LoadEvent | Type for load() in +page.server.ts / +layout.server.ts |
| Handle | Type for a middleware function in hooks.server.ts |
Path Alias
$lib maps to src/lib/ out of the box:
import { myUtil } from "$lib/utils";Project Structure
my-app/
├── src/
│ ├── app.css # Global styles + Tailwind config
│ ├── hooks.server.ts # Optional request middleware
│ ├── lib/ # Shared utilities ($lib alias)
│ └── routes/ # File-based routes
├── public/ # Static assets (served as-is)
├── dist/ # Build output (git-ignored)
└── package.jsonLicense
MIT
