necrassrs 0.1.2

SDL-first GraphQL execution and resolver contracts for Rust.
Documentation

necrassrs is the execution core for SDL-first GraphQL applications in Rust. Define the public API in GraphQL SDL, let Cargo generate Rust contracts and resolver scaffolding, and fill in the resolver bodies with application logic.

Resolver contracts and dispatch code are generated by necrassrs-build. HTTP integration is provided by necrassrs-axum and necrassrs-actix. See the API documentation for the execution API.

[!WARNING] NecrassRs is pre-1.0 and under active development. The generated API supports built-in and application-owned custom scalars, enum results, ordinary and OneOf input objects, owned Object results, Interface and Union results, nullable and list forms, recursive input and Object relationships, and query, mutation, and subscription routing. Borrowed Object results remain unsupported.

Quick start

With Rust and Cargo installed, install the CLI from crates.io:

cargo install necrassrs-cli --locked

The package is named necrassrs-cli; the installed command is necrass. Create and run a project:

necrass init my-api
cd my-api
cargo run

The target directory must be new or empty. After initialization, ordinary builds and runs only require Cargo; no separate generation command is needed.

Run necrass without arguments in a terminal to choose the project directory, package name, and HTTP framework interactively. Explicit init commands do not prompt: Axum is the default, and necrass init my-api --framework actix selects Actix Web. Use necrass --help or necrass init --help for usage. Without a terminal, supply an explicit command.

Open GraphiQL and run { hello(name: "Sheri") }. The response is {"data":{"hello":"Hello, Sheri"}}.

Follow the installation and first-request guide for prerequisites, interactive setup, framework selection, and curl examples. See Introspection and GraphiQL for deployment settings.

From schema to resolver

The starter defines its API in schema/schema.graphql:

type Query {
  hello(name: String!): String!
}

Cargo runs build.rs, which calls necrassrs_build::build("schema"). The build library validates the SDL, generates argument types and resolver contracts, and synchronizes editable resolver declarations reachable from src/resolvers.rs.

[!IMPORTANT] Cargo builds write disposable generated code to OUT_DIR and update resolver source files. Retained fields keep their resolver bodies. Deleting or renaming a field removes an untouched generated stub, but a resolver with an edited body, attribute, or comment stops synchronization until you remove or migrate it.

The starter includes this working resolver:

pub struct Query;

impl<C: Sync> crate::generated::resolvers::QueryResolver<C> for Query {}

impl<C: Sync> necrassrs::Resolver<crate::generated::fields::Query::hello, C> for Query {
    type Output = String;

    async fn resolve(
        &self,
        _context: &C,
        args: crate::generated::types::Query::hello::Args,
    ) -> Result<Self::Output, necrassrs::ResolverError> {
        Ok(format!("Hello, {}", args.name))
    }
}

The application owns routing and request Context construction. At runtime, NecrassRs validates the request, dispatches selected fields to resolvers, and builds the GraphQL response. Return ResolverError for ordinary application errors.

Follow the tutorial to define a schema, implement resolvers, and query a running web server. Continue with the Personnel Management System for Object, Interface, and Union results. See Organizing Resolver Files to split implementations across Rust modules, and Types for Rust type mappings, input defaults and presence, OneOf, and recursive inputs.

Everyday development

  1. Edit SDL under schema/ to change the public API.
  2. Run cargo build to regenerate contracts and synchronize resolver declarations.
  3. Implement new resolver bodies in src/resolvers.rs.
  4. Run cargo run and query the server.

Generated contracts in OUT_DIR are disposable. In the resolver source tree, SDL owns resolver declarations, while the application owns retained resolver bodies and unrelated code.

File Purpose
schema/**/*.graphql Public GraphQL contract
build.rs Calls the build library during Cargo builds
src/main.rs Routing, request Context construction, and server setup
src/resolvers.rs, src/resolvers/ Build-managed resolver declarations with application-owned bodies and state
src/generated.rs Includes generated code from OUT_DIR
OUT_DIR/necrassrs.rs Disposable contracts, argument types, embedded SDL, and dispatch; do not edit

Builds update resolver source files. Retained fields keep their resolver bodies, and new fields receive unimplemented!() stubs in src/resolvers.rs. A deleted or renamed field loses its old declaration only when it remains an untouched generated stub. An edited body, comment, or attribute stops synchronization so it can be removed or migrated explicitly. Unrelated application code is preserved; retained bodies may need edits when arguments or result types change.

The starter configures panic = "abort" for development and release builds. Calling an unimplemented resolver terminates the process; unselected fields are not called. This setting affects all panics. Ordinary resolver errors return GraphQL responses.

Further reading

  • Tutorial example: inspect the completed Axum project from the step-by-step tutorial, including its in-memory mutation.
  • Axum server example: run a workspace example and explore requests, errors, and manual setup details.
  • Actix server example: run generated resolvers through Actix Web with built-in GraphiQL.
  • Custom Scalars: define scalar storage, coercion, and serialization.
  • Integration: integrate the execution core and HTTP adapters manually.

License

Licensed under the MIT License.