apiplant-function
Write an apiplant function without the ABI boilerplate.
Instead of hand-implementing the [apiplant_abi] traits, exporting a root
module, and shuttling JSON in and out by hand, you write one ordinary typed
function and call [function!]:
use apiplant_function::prelude::*;
#[derive(serde::Deserialize, Default)]
struct Config { #[serde(default)] greeting: String }
#[derive(serde::Deserialize, JsonSchema)]
struct Input { name: String }
#[derive(serde::Serialize, JsonSchema)]
struct Output { message: String }
fn greet(ctx: &Context<Config>, input: Input) -> Result<Output, String> {
Ok(Output { message: format!("{}, {}!", ctx.config().greeting, input.name) })
}
apiplant_function::function! {
name: "greet",
description: "Greets a person",
method: Post,
visibility: Public,
handler: greet,
}
# fn main() {}
The macro generates the root module, reads/writes JSON, resolves typed config
and input, and turns your Err(_) into a 400. Types are inferred from the
handler's signature — you never name them twice. With the default schema
feature the input and output types must also derive JsonSchema
so the endpoint shows up typed in the OpenAPI docs.
Use [functions!] to export several from one library — each with its own
name, manifest and handler.
Functions as lifecycle hooks
A function can also be attached to a resource's lifecycle from
models/<name>.toml, in which case [Context::hook] carries the operation's
context — the row created or fetched, the rows a list returned, the request
URL, the caller's auth status — and the [reply] helpers say what should
happen next. One function per event, so a handler never has to work out why
it was called:
# use apiplant_function::prelude::*;
fn post_after_create(ctx: &Context<()>, row: serde_json::Value) -> Result<serde_json::Value, String> {
let actor = ctx.hook().and_then(|hook| hook.principal_id.clone());
ctx.info(&format!("post {} created by {actor:?}", row["id"]));
Ok(reply::proceed())
}
apiplant_function::functions! {
{
name: "post_after_create",
description: "Records a newly created post",
method: Post,
visibility: Private,
handler: post_after_create,
},
}
# fn main() {}