Skip to main content

Advanced Types

Provided Types

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;
}