Custom Input and Output Types
Relevant source files
The following files were used as context for generating this wiki page:
Custom Input and Output Types in Seaography allow developers to extend the automatically generated GraphQL schema with user-defined structures. While Seaography primarily focuses on mapping SeaORM entities to GraphQL nodes, these custom types provide the flexibility to define specialized data transfer objects (DTOs), complex query parameters, and custom mutation results that are not directly tied to a single database table.
This system leverages Rust derive macros to bridge plain Rust structs and enums with the async-graphql dynamic schema engine. By implementing specific traits—either manually or via macros—these types can be registered within the Builder and used seamlessly alongside standard entity-based types in queries and mutations.
Core Traits and Architecture
The custom type system is built on several key traits that define how a Rust type should be represented in the GraphQL schema and how its values are processed during execution.
Output Type Traits
Output types are used for data returned by the GraphQL server.
CustomOutputType: The fundamental trait for any type that can be returned in a GraphQL field. It defines how to get a type reference and how to convert the Rust value into aFieldValue.CustomOutputObject: Specifically for Rust structs intended to be GraphQL Objects. It provides thebasic_objectmethod to construct theasync_graphql::dynamic::Objectdefinition.CustomUnion: Used for enums with multiple data-carrying variants to represent GraphQL Unions.
Sources: src/custom/output.rs:27-35, src/custom/types.rs:55-57
Input Type Traits
Input types are used for data sent by the client to the server (e.g., arguments).
CustomInputType: The base trait for types used as arguments. It includes logic for parsing aValueAccessorinto the Rust type.CustomInputObject: Specifically for Rust structs intended to be GraphQL Input Objects. It provides theinput_objectmethod to define fields and types for the schema.
Sources: src/custom/input.rs:24-33, src/custom/input.rs:35-37
Relationship Diagram
The following diagram illustrates the relationship between the developer-defined types, the traits they implement, and the Seaography Builder.
Macros and Automation
Seaography provides procedural macros to automatically generate trait implementations, reducing boilerplate for developers.
#[derive(CustomInputType)]
When applied to a struct, this macro generates code to:
- Map each struct field to a GraphQL Input field.
- Implement
parse_valueto extract and validate data from a GraphQLObject. - Implement
gql_input_type_refto point to the named input type in the schema.
For unit enums, it generates a GraphQL Enum mapping. For container enums (variants with data), it generates a specialized Input Object where each field represents a variant.
Sources: macros/src/custom_input_type.rs:13-35, macros/src/custom_input_type.rs:163-188
#[derive(CustomOutputType)]
When applied to a struct, this macro:
- Constructs a GraphQL
Objectwith fields corresponding to struct fields. - Handles field resolution by downcasting the parent value to the Rust type and extracting the field.
- Supports basic primitive types and nested custom types.
Sources: macros/src/custom_output_type.rs:36-70
Macro Logic Flow
The following sequence diagram shows how the CustomInputType macro processes a Rust struct to create a GraphQL parser.
Type Registration and Schema Integration
Once defined, custom types must be registered with the Builder to be included in the final GraphQL schema.
Registration Methods
| Method | Description |
|---|---|
register_custom_input::<T>() | Registers a type T that implements CustomInputObject. |
register_custom_output::<T>() | Registers a type T that implements CustomOutputObject. |
register_custom_object::<T>() | Registers a type as both input and output. |
register_custom_union::<T>() | Registers a type as a GraphQL Union. |
register_custom_enum::<T>() | Registers a type as a GraphQL Enum. |
Sources: src/builder.rs:241-265
Schema Builder Integration
The Builder maintains internal vectors for outputs, inputs, enumerations, and unions. During the schema_builder() call, it iterates through these collections and registers every object with the async_graphql::dynamic::SchemaBuilder.
Sources: src/builder.rs:40-65, src/builder.rs:328-360
Primitive and Built-in Implementations
Seaography provides default implementations of CustomInputType and CustomOutputType for standard Rust types and common database-related types.
Supported Base Types
- Primitives:
bool,u8,u16,u32,u64,i8,i16,i32,i64,f32,f64. - Standard Types:
String,Option<T>,Vec<T>,Box<M>. - External Types:
serde_json::Value(mapped toJsonscalar),Upload. - Chrono/Time: Support for various date and time types when enabled via features.
Sources: src/custom/impls.rs:10-44, src/custom/impls.rs:114-135, src/custom/output.rs:80-120
JSON Handling
The system explicitly maps serde_json::Value to a GraphQL Json scalar. The CustomInputType implementation uses deserialize() to parse the incoming value, while CustomOutputType uses async_graphql::Value::from_json to return it.
Sources: src/custom/impls.rs:114-135
Summary
Custom Input and Output Types provide the extensibility layer for Seaography. By using the CustomInputType and CustomOutputType traits and their associated macros, developers can define complex structures that the Builder integrates into the GraphQL schema. This mechanism allows the framework to handle everything from simple primitives to nested custom objects and unions, ensuring that the resulting API can support complex business logic beyond simple CRUD operations on database entities.