File layout
A schema can live in one file or be split across modules. gurafi is designed for the split
case: object, interface and root declarations reopen the same type, so a type's fields can be
co-located with the code that owns them.
A small application
main.rs owns the server and calls schema::build():
schema.rs defines the context type and registers every module:
users/mod.rs registers the type and its root fields:
User::id and User::name are declared in users/model.rs; the query in users/queries.rs;
the related field can live in posts because it references the same User type. Duplicate
field names are build errors, so the split cannot silently overwrite a field.
Naming conventions
model.rs— plain Rust data and#[derive(Object)]/Input/Enumdefinitions.queries.rs,mutations.rs— root field registration functions.mod.rs—pub fn register(schema: &mut Schema<Ctx>).- Keep the context type in one module and import it as
crate::schema::Ctx.
There is no rule that a GraphQL type must be registered where its model is defined. Register it once and reopen it from the modules that add fields.
Reopening versus duplicate definitions
object, object_fields and object_field all add to the same type. The same is true for
interface, query, mutation and subscription. A duplicate field name is a build error, not
a last-writer-wins merge.
#[derive(Object)] should appear once per Rust type. Two different Rust models with the same
GraphQL name are a build error unless you explicitly use object_ref for a runtime name.
Feature modules
Put cross-cutting extensions in a plugins.rs module:
Call it after registering types and before build(). Plugin order is registration order, so
install plugins before any module that depends on their metadata.
Tests
Build a fresh schema per test with the same registration functions. Schema is Clone, so a
test can keep a declaration and build variants with different BuildOptions or plugins.
