Advanced Types
Provided Types
- PageInfo: Following the relay connections spec.
Cyclic Types
Types which use themselves in their definition have to reuse previously created instances. The type's field lists are mutable, which allow you to instantiate the type and then modify the fields of the type. For example, an User with friends:
class User {
const User(this.friends);
final List<User> friends;
}
GraphQLObjectType<User>? _type;
GraphQLObjectType<User> get userGraphQLType {
if (_type != null) return _type; // return a previous instance
final type = objectType<User>(
'User',
// leave fields empty (or don't pass them)
fields: [],
);
_type = type; // set the cached value
type.fields.addAll([ // add the fields
listOf(userGraphQLType.nonNull()).nonNull().field(
'friends',
resolve: (obj, _) => obj.friends,
),
]);
return type;
}
Code generation already does it, so you don't have to worry about it when using it.
Custom Scalars
You can extend the GraphQLScalarType
or create an instance directly with GraphQLScalarTypeValue
. For example, to support the Decimal
type from https://github.com/a14n/dart-decimal you can use the following code:
import 'package:decimal/decimal.dart';
import 'package:leto_schema/leto_schema.dart';
export 'package:decimal/decimal.dart';
final decimalGraphQLType = GraphQLScalarTypeValue<Decimal, String>(
name: 'Decimal',
deserialize: (_, serialized) => decimalFromJson(serialized)!,
serialize: (value) => decimalToJson(value)!,
validate: (key, input) => (input is num || input is String) &&
Decimal.tryParse(input.toString()) != null
? ValidationResult.ok(input.toString())
: ValidationResult.failure(
['Expected $key to be a number or a numeric String.'],
),
description: 'A number that allows computation without losing precision.',
specifiedByURL: null,
);
Decimal? decimalFromJson(Object? value) =>
value == null ? null : Decimal.parse(value as String);
String? decimalToJson(Decimal? value) => value?.toString();
For code generation you need to provide customTypes
in the build.yaml file of you project:
target:
default:
builders:
leto_generator:graphql_types:
options:
customTypes:
- name: "Decimal"
import: "package:<your_package_name>/<path_to_implementation>.dart"
getter: "decimalGraphQLType"
leto_generator:graphql_resolvers:
options:
customTypes:
- name: "Decimal"
import: "package:<your_package_name>/<path_to_implementation>.dart"
getter: "decimalGraphQLType"
Generic Types
Work in progress
class ErrC<T> {
final String? message;
final T value;
const ErrC(this.value, [this.message]);
}
GraphQLObjectType<ErrC<T?>> errCGraphQlType<T extends Object>(
GraphQLType<T, Object> tGraphQlType, {
String? name,
}
) {
return objectType(
name ?? 'ErrC${tGraphQlType is GraphQLTypeWrapper ? (tGraphQlType as GraphQLTypeWrapper).ofType : tGraphQlType}',
isInterface: false,
interfaces: [],
description: null,
fields: [
field('message', graphQLString,
resolve: (obj, ctx) => obj.message,),
field('value', tGraphQlType,
resolve: (obj, ctx) => obj.value,)
],
);
}
- With code generation
import 'package:leto/leto.dart';
part 'errc.g.dart';
()
class ErrC<T> {
final String? message;
final T value;
const ErrC(this.value, [this.message]);
}
Which generates in 'errc.g.dart':
Map<String, GraphQLObjectType<ErrC>> _errCGraphQlType = {};
/// Auto-generated from [ErrC].
GraphQLObjectType<ErrC<T>> errCGraphQlType<T extends Object>(
GraphQLType<T, Object> tGraphQlType,
) {
final __name =
'ErrC${tGraphQlType is GraphQLTypeWrapper ? (tGraphQlType as GraphQLTypeWrapper).ofType : tGraphQlType}';
if (_errCGraphQlType[__name] != null)
return _errCGraphQlType[__name]! as GraphQLObjectType<ErrC<T>>;
final __errCGraphQlType = objectType<ErrC<T>>(
'ErrC${tGraphQlType is GraphQLTypeWrapper ? (tGraphQlType as GraphQLTypeWrapper).ofType : tGraphQlType}',
isInterface: false,
interfaces: [],
description: null);
_errCGraphQlType[__name] = __errCGraphQlType;
__errCGraphQlType.fields.addAll(
[
field('message', graphQLString,
resolve: (obj, ctx) => obj.message,
inputs: [],
description: null,
deprecationReason: null),
field('value', tGraphQlType.nonNull(),
resolve: (obj, ctx) => obj.value,
inputs: [],
description: null,
deprecationReason: null)
],
);
return __errCGraphQlType;
}