grapher_annotation 0.7.0
grapher_annotation: ^0.7.0 copied to clipboard
Annotation package for 'grapher_builder' package. For generating GraphQL based on dart code models.
Grapher #
Grapher is a helper for generating client-side GraphQL from models written in Dart!
Support all base features:
- Type, Input, Enum
- Query, Mutation, Subscription
- Union (including list fields)
- Subquery (query on field)
- Fragments and recursive types
Not implemented, but plan:
- Multiple queries in one
Usage #
See example project
Types #
Define class with unnamed or _ constructor, all fields in this constructor used in code generation
Below are examples of the Dart model for the GraphQL entity example.
Object #
Objects validation only occurs if a name is specified or object used in actions If name is not specified, it search in parent objects
@GrapherObject(name: 'Item')
class Item {
final ID id;
final DateTime createdAt;
final String name;
final String? description;
final int count;
final ItemStatus? status; // Enumeration values ββare optional to ensure greater compatibility.
const Item(
this.id,
this.createdAt,
this.name,
this.description,
this.count,
this.status,
);
}
type Item {
id: ID!
createdAt: Timestamp!
name: String!
description: String
count: Int!
price: Double!
status: ItemStatus!
}
Input #
@GrapherInput(name: "SelectItemInput")
class SelectItemInput {
final ID? id;
const SelectItemInput({this.id});
}
input SelectItemInput {
id: ID
}
For greater compatibility, you can replace enum values with a string.
Fields #
All fields maybe annotated for change behavior
@GrapherObject(name: 'Item')
class Item {
@GrapherField(name: 'code')
final ID id;
const Item(
this.id,
);
}
Fragments and recursive types #
@GrapherObject(fragment: true) selects the type through a named fragment
<Type>Fields. For a union field, @GrapherField(fragment: 'Type') does the
same with a fragment on the union or interface Type.
A field that leads back to a type it is selected from is recursive and needs
maxDepth: how many times it nests. Fragments cannot contain such fields
(fragment spreads must not form cycles), so they are selected next to the spread.
const _widgets = {'Paragraph': Paragraph, 'Block': Block};
@GrapherObject(name: 'Block')
class Block extends Widget {
@GrapherField(union: _widgets, fragment: 'Widget', maxDepth: 1)
final List<Widget> children;
const Block(this.children);
}
widgets {
...WidgetFields
... on Block { children { ...WidgetFields } }
}
fragment WidgetFields on Widget {
__typename
... on Paragraph { text }
... on Block { title }
}
Enum #
@GrapherEnum(name: "Status")
enum Status {
@GrapherEnumValue(name: "new")
open,
closed,
}
enum Status {
new,
closed
}
Actions #
The library uses wrappers that you must use result for further use
Use in
- static function in object or input
@GrapherObject()
class Item {
// ...
@GrapherQuery(name: 'items')
static Query<List<Item>> query(SelectItemInput input) => _itemQuery(input);
}
extend type Query {
items(input: SelectItemInput!): [Item!]!
}
- top level function
@GrapherQuery(name: 'items')
Query<List<Item>> query(SelectItemInput input) => _query(input);
- getter in input
@GrapherObject()
class UpdateItemInput {
// ...
@GrapherMutation(name: 'updateItem')
Mutation<Item> get mutation => _updateItemInputMutation(this);
}
Query #
Mutation #
Subscription #
Options #
Resolvers #
For custom types use resolvers and create custom annotation
Custom resolver maybe const class!
@GrapherResolver(name: 'Timestamp')
class TimestampResolver with GrapherResolverMixin<DateTime> {
const TimestampResolver();
@override
DateTime fromMap(dynamic json) => DateTime.parse(json as String);
@override
dynamic toMap(DateTime value) {
return value.toUtc().toIso8601String();
}
}
// To avoid import issues, define the resolver class or instance in the same file as the new annotations.
const timestampResolver = TimestampResolver();
// Example for GrapherObject
class ProjectObject extends GrapherObject {
const ProjectObject({super.name})
: super(resolvers: const [timestampResolver]);
}
// And use @ProjectObject(name:)