Expand description
GraphQL support (feature graphql).
Built on async-graphql: routes serves
POST /graphql and GraphiQL on GET /graphql; ocre g api Post ... --graphql generates posts(limit, offset), post(id), createPost,
updatePost and deletePost resolvers that call the model, so they share
its rules with the JSON API.
Opt-in because it costs on the free plan: the WebAssembly binary grows by
about 1.1 MB and each new Worker instance spends 20-60 ms of CPU loading
it and building the schema (measured with wrangler tail; the free plan
allows 10 ms per request, with tolerance for infrequent overruns). Build
the schema once per instance and keep resolvers thin; each resolver’s D1
queries cost the same rows read and written as in a JSON handler.
Resolvers get the request context with ctx.data::<ocre::Ctx>()?, and
Ocre errors convert with ?: clients see Not found or the bad-request
message with extensions.status (and extensions.fields for validation
errors), never internal details, which are logged.
use std::sync::LazyLock;
use async_graphql::{Context, EmptyMutation, EmptySubscription, Object, Schema};
use axum::Router;
use ocre::{Ctx, Error};
struct Query;
#[Object]
impl Query {
async fn post_count(&self, ctx: &Context<'_>) -> async_graphql::Result<i64> {
let db = ctx.data::<Ctx>()?.db()?;
// ... SELECT COUNT(*) FROM posts
Err(Error::NotFound.into())
}
}
type AppSchema = Schema<Query, EmptyMutation, EmptySubscription>;
static SCHEMA: LazyLock<AppSchema> = LazyLock::new(|| Schema::new(Query, EmptyMutation, EmptySubscription));
fn routes() -> Router<Ctx> {
Router::new().merge(ocre::graphql::routes(|| &*SCHEMA))
}Re-exports§
pub use async_graphql;