The backend unframework.
Ship a backend from the bundler you already use. Call the server from the client like a function.
// notes.server.tsimport{action}from"oxidejs";exportconstcreate=action(async(title: string)=>{return{id: crypto.randomUUID(), title };});// client.tsimport{create}from"./notes.server";constnote=awaitcreate("Ship Oxide");Looks local. Runs on the server.
// vite.config.tsimport{defineConfig}from"vite";importoxidefrom"oxidejs/vite";exportdefaultdefineConfig({plugins: [oxide()],});Vite or Rsbuild. Switch the import. Keep the rest.
bun add oxidejsIt guides you. It doesn't frame you.
- Single bundle — One folder to deploy. A server, and a client if you have a page.
- Server actions — Wrap a
*.server.tsexport inaction(), then import and call it from the client over Effect RPC. - Fetch or celld — Run the generated server on Node-compatible hosts, or emit a celld worker.
| Package | Purpose |
|---|---|
| oxidejs | Turns Vite or Rsbuild into a backend. One plugin, one folder to deploy. |
Server actions ride Effect RPC (application/json-rpc). The templates/simple and templates/tasks apps show fetch and celld deployments.
MIT © Ryuz