Your data, modeled.
Models, migrations, relationships.
THE BACKEND FRAMEWORK FOR NEXT.JS, NUXT & SVELTEKIT
A complete backend for Next.js, Nuxt, and SvelteKit.
Keep
your framework. Get your backend together.
From permission to persistence to background work.
Define an ability once. Reuse the same decision in routes, jobs, and services.
Explore authorizationimport { defineAbility, allow, deny }
from '@holo-js/authorization'
export default defineAbility('posts.publish',
context => context.user?.role === 'editor'
? allow()
: deny('Editor access required.')
)
Same backend conventions. The route syntax you already know.
import Post from '@/server/models/Post'
export async function GET() {
const posts = await Post
.where('status', 'published')
.latest()
.paginate(15)
return Response.json(posts)
}
One contract. Different drivers.
Explore what each module
does.
Store files locally, serve public uploads, or use S3-compatible storage through the same API. Your media library builds on these disks with model collections and conversions.
defaultDisk: 'local'
await Storage.put('avatars/ava.png', file)
const avatar = await Storage.get('avatars/ava.png')
avatar.png → public disk
Storage uses the configured default disk. S3 credentials and disk configuration belong in your environment.
Explore storage →Explore the media library →Start with SQLite, move to PostgreSQL or MySQL. Keep your model calls and portable schema migrations while the configured connection changes.
driver: 'sqlite'
await schema.createTable('posts', table => {
table.id()
table.string('title')
table.timestamps()
})
Portable schema → configured database
Install the chosen driver and configure its connection. Existing data must be migrated separately; database-specific SQL is not portable.
Explore database →Local login, sessions and API tokens, plus Clerk and WorkOS. Hosted providers sync into your local user model and create a normal Holo session.
import { user } from '@holo-js/auth'
const currentUser = await user()
Your sign-in → local user → Holo session
Keep identity choice inside one authentication module.
Explore authentication →Define typed server queries and mutations. When matching Holo database writes commit, live query results refresh in connected browsers.
query(...) // read typed data
mutation(...) // write typed data
Draft → query result
Run the broadcast WebSocket worker to deliver live browser updates.
Explore realtime →Define a notification once, then deliver it through email, database and broadcast channels. Queue or delay delivery when your product needs it.
defineNotification({
via: () => ['email', 'database', 'broadcast'],
// channel-specific builders
})
Invoice paid → one notification
Email uses the mail sender. Live broadcast uses the configured transport.
Explore notifications →Define a typed email, preview it in the browser, and send it through a configured driver. Add Markdown, attachments or queued delivery.
await sendMail(welcomeMail({
to: 'ava@example.com', name: 'Ava',
})).using('preview')
Welcome, Ava. Your account is ready.
Your account is ready.
Open your dashboard →Preview, log and SMTP drivers share the mail delivery contract.
Explore mail →Write a typed job once. Run it inline with sync, or dispatch it through database and Redis connections. Attempts, backoff and failed jobs stay explicit.
default: 'sync'
await GenerateReport.dispatch({ reportId })
Report requested → queued
Sync runs inline. Database and Redis need workers; database queues also need their queue-table migration.
Explore queues →A TOOLKIT, NOT A TAKEOVER
Pick your pieces. Your choices become your starter command.
Models, migrations, relationships.
Auth, sessions, and permissions.
Explore identityTyped jobs. Explicit retries.
Explore background workLocal and cloud disks. One API.
Explore storageThe stack you chose. One command to start.
npm create holo-js@latest my-app -- --framework next --database
sqlite --package auth,queue,storage
Orange modules are included in your app.