Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
TypeScript and JavaScript functions.
A function written in Rust, C, Zig or Go arrives as a shared library. A
function written in TypeScript cannot: there is nothing to link. So this
crate provides the other half of what a .so gives the host — a manifest to
read at boot and something to call per request — backed by V8 isolates
instead of dlopen.
functions/greet.ts ← what you write
functions/greet.js ← `apiplant build` strips the types (swc, at build time)
and the server loads *this*, like it loads libgreet.so
Two stages, on purpose
Types are stripped at build time, so the server never parses TypeScript
and a syntax error is a build failure rather than a boot failure. What runs at
request time is plain JavaScript in a V8 isolate — the same split Deno and Bun
make internally, just with the first half hoisted into apiplant build.
No type checking happens: swc strips annotations without consulting them,
exactly like deno run --no-check or bun. apiplant build writes an
apiplant.d.ts beside your sources so your editor (and tsc --noEmit, if you
want it in CI) does the checking with real types.
What a module looks like
import { defineFunctions, db, s } from "apiplant";
export default defineFunctions({
greet: {
permission: "public",
input: s.object({ name: s.string() }),
handler(input) {
const notes = db.value("SELECT count(*)::int AS n FROM apiplant_note");
return { message: `Hello, ${input.name}!`, notes };
},
},
});
One module may declare any number of functions, like one .so may export
any number. apiplant is the only module a function can import; it is
compiled into this crate from typescript/ at the repository root and served
to the isolate by [module], so nothing is installed and nothing can be out
of step with the host. A module that would rather import nothing declares
export const manifest = [...] and one export per entry instead; both forms
arrive here the same way.
Concurrency
An isolate is single-threaded, so a module is loaded into a small pool of them
([workers], APIPLANT_JS_WORKERS) that share one job queue. Requests run
concurrently up to the pool size and queue beyond it. Isolates share nothing:
module-level state in a function is per-worker and must not be treated as
shared state — use the database or the cache for that.
An invocation that runs longer than APIPLANT_JS_TIMEOUT_MS (30s by default)
has its isolate terminated and fails that one request; the worker recovers.