-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Star-wars-api sample code is wrong #470
Comments
Typically this is done using an So we can change type Human =
{ Id : string
Name : string option
Friends : string list
AppearsIn : Episode list
HomePlanetId : string option } Then the resolver for Define.AsyncField(
"homePlanet",
Nullable PlanetType,
resolve =
fun context human ->
async {
match human.HomePlanetId with
| Some planetId ->
return! tryFetchPlanet planetId
| None ->
return None
}) Note that batching and caching is likely a good idea to avoid the n-selects problem. One approach: #255 (comment) |
@njlr can you submit a PR that adds your caching approach to StarWars sample? |
@njlr may I ask another question. I'm trying to expose a database with some 250+ tables and would like to provide custom So I thought that scalar AnyScalar
union KeyType = ID | String
type AnyDictionary {
key: KeyType!
value: AnyOrListOfAny!
}
union AnyOrListOfAny = Any | [Any!]!
union Any = AnyScalar | AnyDictionary
type QueryFilter {
filter: AnyDictionary
} Is it possible to express the above I don't know how to...
|
Edit: Will circle back with a better answer, but I think you should do the unpacking in the resolver logic |
@njlr your original answer was actually on point. GraphQL enforces explicit types. The only way to have something like a "dynamic" type in GraphQL is by encoding the dynamic objects as strings. |
Is it possible to somehow define nested Input types? E.g.: for each field in the table, I'd like to render a set of possible queries depending on its type (Int, String, Float, etc). So for Int field, I'd have an object of
Example code (that can't make it work): let renderInputObjectForColumnType typeName (colTyp:ScalarDefinition<'T,'T>) =
let basicOps =
Define.InputObject($"Query_%s{typeName}", [
for op in ["_eq"; "_ne"; "_gt"; "_gte"; "_lt"; "_lte"] ->
Define.Input(op, SchemaDefinitions.Nullable colTyp)
for op in ["_in"; "_notIn"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf colTyp)
])
let nestedOps =
Define.InputObject($"Query_%s{typeName}", [
for op in ["_and"; "_or"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf basicOps)
])
SchemaDefinitions.Nullable nestedOps Another option would be to be able to mutate and add extra fields to an existing object (if possible). |
Hi @pkese . In your example, I don't really get why you'd want to have the definitions of let renderInputObjectForColumnType typeName (colTyp:ScalarDefinition<'T, 'T>) =
let basicOps =
Define.InputObject($"Query_condition_%s{typeName}", [
for op in ["_eq"; "_ne"; "_gt"; "_gte"; "_lt"; "_lte"] ->
Define.Input(op, SchemaDefinitions.Nullable colTyp)
for op in ["_in"; "_notIn"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf colTyp)
])
let nestedOps =
Define.InputObject($"Query_conditions_%s{typeName}", [
for op in ["_and"; "_or"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf basicOps)
])
SchemaDefinitions.Nullable nestedOps ? |
@valbers |
I'm not even sure if recursive input type definitions are allowed in GraphQL... let renderInputObjectForColumnType typeName (colTyp:ScalarDefinition<'T, 'T>) =
let basicOps =
Define.InputObject($"Query_condition_%s{typeName}", [
for op in ["_eq"; "_ne"; "_gt"; "_gte"; "_lt"; "_lte"] ->
Define.Input(op, SchemaDefinitions.Nullable colTyp)
for op in ["_in"; "_notIn"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf colTyp)
])
let nestedOps =
Define.InputObject($"Query_conditions_%s{typeName}",
Define.Input("_is", SchemaDefinitions.Nullable basicOps) // <-- here
:: [
for op in ["_and"; "_or"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf basicOps)
]
)
SchemaDefinitions.Nullable nestedOps |
Regarding your initial questions:
No. This is for pagination logic. In this context, "connection" is something like a "dormant query" (I just found out this term myself) and "edge" is something like a page.
Probably because it's an approach that is not part of the GraphQL specification. It does have an specification of its own, though: GraphQL Cursor Connections Specification |
GraphQL is perfectly fine with referencing types that are declared somewhere below in the document.
Yeah, but that another layer of I'm wondering if union types would work for Inputs, maybe that could solve it. |
Union input types are not implemented here yet |
Ha, this works! let renderFilterChoicesForType typeName (colTyp:ScalarDefinition<'T,'T>) =
let __basicOps =
Define.InputObject($"Query_%s{typeName}", [])
let basicOps =
Define.InputObject($"Query_%s{typeName}", [ // <-- same name as above
for op in ["_eq"; "_ne"; "_gt"; "_gte"; "_lt"; "_lte"] ->
Define.Input(op, SchemaDefinitions.Nullable colTyp)
for op in ["_in"; "_notIn"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf colTyp)
for op in ["_and"; "_or"] ->
Define.Input(op, SchemaDefinitions.Nullable <| ListOf __basicOps) // <-- reference to empty InputObject from above
])
SchemaDefinitions.Nullable basicOps |
Then comes a bit of a facepalm moment: let renderFilterChoicesForType typeName (colTyp:ScalarDefinition<'T,'T>) =
let rec basicOps =
Define.InputObject($"Query_%s{typeName}", fun () -> [ // <-- adding fun ()
for op in ["_eq"; "_neq"; "_gt"; "_gte"; "_lt"; "_lte"] do
yield Define.Input(op, SchemaDefinitions.Nullable colTyp)
for op in ["_in"; "_notIn"] do
yield Define.Input(op, SchemaDefinitions.Nullable <| ListOf colTyp)
for op in ["_and"; "_or"] do
yield Define.Input(op, SchemaDefinitions.Nullable <| ListOf basicOps)
])
SchemaDefinitions.Nullable basicOps Thanks everyone for support and sorry for having wasted your time. 🙏 Now that this is solved... I've tried to extract a minimal working example in #472. |
@pkese |
@valbers Technically the initial post in the ticket still makes sense (I think it would be nice to fix the sample code to use planet's ids instead of planet's names as references). But I'm fine with closing the ticket, so feel free to close it. |
Alright. I agree that it would indeed be of help to have a sample showcasing a possible approach for joins or caching results. |
I'm looking at star-wars-api/Schema
Note that the Human.HomePlanet, as defined in data in the last line contains Planet.Name rather than Planet.Id.
I guess the idea here was that a Human's
HomePlanet
was supposed to containPlanet.Id
, e.g"1"
for Tatooine rather than planet's name (Some "Tatooine"
) as defined in the list ofhumans
.This omission is really unpleasant. I started reading this sample code because I wanted to figure out how foreign-key relationships are supposed to be modeled with this library and this kind of leaves it unexplained.
In my real world case, where I would like to use FSharp.Data.GraphQL, I'd like to do joins across database tables and based on the documentation here I don't really know how to model that and where to start from.
Do I need to include FSharp.Data.GraphQL.Server.Relay to achieve that (there are some
Edge
andConnection
defined there - is that for relationships)? Why is this Relay in a separate namespace, i.e. not part of the main library?Thanks.
The text was updated successfully, but these errors were encountered: