diff --git a/docs/scalars.md b/docs/scalars.md index d1ffac0..aad1a75 100644 --- a/docs/scalars.md +++ b/docs/scalars.md @@ -3,7 +3,7 @@ ```Nim type NodeResult* = Result[Node, string] - CoerceProc* = proc(ctx: Graphql, node: Node): NodeResult + CoerceProc* = proc(ctx: Graphql, typeNode, node: Node): NodeResult ``` Unlike other graphql implementations, nim-graphql using a simpler approach to implement scalars. @@ -20,3 +20,76 @@ You can refer to above types definition when writing your own custom scalars. Custom scalars main purpose is validating the user param input and validating scalar response object returned by resolver. Custom scalars can accepts and returns whatever response object needeed by the service, but the serialization is done by execution engine. For this reason, always keep in mind that final [response object](resolver.md#response-object) should contains only limited set of `Node` types. + +```Nim +proc scalarMyScalar(ctx: GraphqlRef, typeNode, node: Node): NodeResult {.cdecl, gcsafe, nosideEffect.} = + if node.kind == nkString: + ok(node) + else: + err("expect string, but got '$1'" % [$node.kind]) + +proc scalarLong(ctx: GraphqlRef, typeNode, node: Node): NodeResult {.cdecl, gcsafe, nosideEffect.} = + ## Long is a 64 bit unsigned integer. + ## TODO: validate max-int 64 bit + if node.kind == nkInt: + # convert it into nkString node + ok(Node(kind: nkString, stringVal: node.intVal, pos: node.pos)) + else: + err("expect int, but got '$1'" % [$node.kind]) + +proc initMyApi(ctx: GraphqlRef) = + ctx.customScalar("myScalar", scalarMyScalar) + ctx.customScalar("Long", scalarLong) +``` + +### Variable coercion + Variable coercion will use scalar proc to validate/convert the value. + But sometimes the variables serialization format doesn't provide compatible type with + scalar type. Common example is when a http graphql server receive variables in json format, + there is no way to distinguish between string and enum. + + Then we need a type conversion from json string to enum node using custom coercion. + +```graphql +enum Fruits { + Banana + Apple + Orange +} +enum Vehicles { + Car + Motorcycle + Bus + Truck +} +scalar UUID +``` + +```Nim +proc coerceEnum(ctx: GraphqlRef, typeNode, node: Node): NodeResult {.cdecl, gcsafe, nosideEffect.} = + if node.kind == nkString: + ok(Node(kind: nkEnum, name: ctx.createName(node.stringVal), pos: node.pos)) + else: + err("cannot coerce '$1' to $2" % [$node.kind, $typeNode]) + +proc coerceUUID(ctx: GraphqlRef, typeNode, node: Node): NodeResult {.cdecl, gcsafe, nosideEffect.} = + if node.kind == nkString: + ok(node) + elif node.kind == nkInt: + ok(Node(kind: nkString, stringVal: node.intVal, pos: node.pos)) + else: + err("cannot coerce '$1' to $2" % [$node.kind, $typeNode]) + +proc initMyApi(ctx: GraphqlRef) = + ctx.customCoercion("Fruits", coerceEnum) + ctx.customCoercion("Vehicles", coerceEnum) + ctx.customCoercion("UUID", coerceUUID) +``` + + Custom coercion will be executed first, then the custom scalar or builtin scalar proc will be executed. + When a custom scalar proc is not provided it will trigger error, but custom coercion is optional. + If custom coercion proc is not provided for custom scalar or enum, it will be skipped and go to the scalar + validation. + + Because after coercion there is still scalar/enum validation, usually coercion proc only do conversion and + doesn't need to do complex validation. diff --git a/docs/toc.md b/docs/toc.md index a9dffe8..0b1951c 100644 --- a/docs/toc.md +++ b/docs/toc.md @@ -17,6 +17,7 @@ - [Scalars](scalars.md) - [Builtin scalars](scalars.md#builtin-scalars) - [Custom scalars](scalars.md#custom-scalars) + - [Variable coercion](scalars.md#variable-coercion) - [Directives](directives.md) - [Builtin directives](directives.md#)