Functions
Run a worker on a Spacefast space, beside its static files and on the same hostname.
Functions adds one Web-standard fetch handler to a space. Spacefast
serves static files first. Requests that no file answers go to the worker,
on the paths that the worker’s compiled route table claims. Hand-written
handlers claim the whole site, and Next builds derive a narrower table.
Unclaimed paths return 404 without waking the worker. The worker and the
files publish as one version on one hostname, and they roll back together.
Quick start
Two commands scaffold and publish a worker:
sf init my-api --runtime functions
cd my-api
sf publish
The worker uses a default export of an object with a fetch method:
// handler.ts
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/api/hello") {
return Response.json({ hello: "world" });
}
return new Response("Not found", { status: 404 });
},
};
Detection and configuration
Spacefast detects the first matching worker entry:
| Path | Typical source |
|---|---|
.open-next/worker.js |
Next.js built with OpenNext |
handler.ts, handler.js, or handler.mjs |
Standalone worker |
Any route module under functions/ |
File router. Spacefast synthesizes the dispatcher |
A .php file under functions/ takes a different lane. It executes at
its derived route with the sf_* prelude, with no worker and no runtime
declaration. See Functions for PHP.
Declare an entry when the file is in another location, or when you want to pin the runtime settings:
{
"$schema": "https://spacefast.com/schemas/sf.json",
"runtime": {
"kind": "functions",
"entry": "build/worker.js",
"database": true,
"compatibilityDate": "2026-07-01",
},
}
If you declare runtime.kind: "functions" and Spacefast finds no entry,
the publish fails with
config_runtime_entry_missing.
A directory with no declaration and no detected entry remains a static
space.
Runtime contract
Handlers use Request, Response, streams, and crypto. Hand-written
handlers cannot import Node built-ins. The build fails rather than the
request. Next and OpenNext builds get nodejs_compat automatically.
Outbound network access is fail-closed. Spacefast denies it unless the
published version holds the fetch capability, so a fetch() call in your
code does not silently add network authority. No Functions publish
grants that capability. Outbound fetch is available only to
Zero actions. To check which capabilities a published version
holds, run sf runtime status.
Variables
Put server-only values in .env.server. The publish syncs them into the
space as secret variables and never uploads the
file. The worker reads them from env:
STRIPE_SECRET_KEY=your_stripe_secret_key
Spacefast withholds database connection strings. Use the database binding instead.
Storage
Every worker receives env.STORAGE, the same space
object store that Zero uses. The binding has
three methods:
upload(file)takes aBloband returns an object withid,contentType,size, andurl.get(id)returns aResponse.delete(id)removes the object.
Database
Set "database": true under runtime in sf.jsonc, and the worker
receives env.DB, a D1-shaped binding:
export default {
async fetch(_request: Request, env: Env): Promise<Response> {
const { results } = await env.DB.prepare(
"SELECT id, email FROM signups ORDER BY created_at DESC LIMIT ?",
)
.bind(10)
.all();
return Response.json(results);
},
};
Database has the full binding contract, the migration
commands, and sf db console.
Next.js
Build Next.js with OpenNext’s Cloudflare adapter, then publish the
directory that contains .open-next/worker.js. Spacefast serves static
assets from disk and routes SSR and route handlers to the worker. See
Next.js for the build recipe.
Develop and inspect
Functions does not run inside sf dev yet. Use your framework’s dev server,
then publish a version to test the Spacefast runtime.
sf runtime status
sf logs runtime --follow
sf logs runtime --request-id req_9f21
sf runtime status reports the published entry, bundle digest and size,
compatibility date, capabilities, and dispatch state.
Limits
- Worker bundle: 8 MiB maximum.
- Request execution: 30 seconds maximum.
Zero or Functions
Functions gives you direct control over request handling with Web APIs and npm packages. If the database, authentication, and storage are the app, Zero builds all three in:
| Zero | Functions | |
|---|---|---|
| You write | Capsule: schema and named handlers | Worker: one fetch handler |
| Database | Built in | Optional env.DB binding |
| Authentication | Built in | Bring your own flow |
| Storage | Built in | Built in (env.STORAGE) |
| Framework | Zero client and server APIs | Web APIs and npm packages |
| Local development | sf dev |
Your framework’s dev server |