Skip to main content

Module graphql

Module graphql 

Source
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;

Functions§

graphiql
Renders GraphiQL, the in-browser query editor, pointed at /graphql.
respond
Executes a POST /graphql JSON body against schema and returns the JSON response.
routes
Returns a router serving GraphiQL on GET /graphql and queries on POST /graphql.