Closed jkarni closed 7 years ago
Idea:
Move GraphQL Schema definitions into the Servant DSL .
var userType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString },
}
});
would be moved to the Servant type-level. Next we add a MimeType for GraphQL that allows us to parse GraphQL queries and use the typing information to be able to build a correct handler type that returns a graphql object.
@arianvp with TH?
This is the result we got at the Munihac hackathon towards this goal: https://github.com/kcsongor/generic-records
@arianvp I also have an SOP-based version of record subtyping here: https://github.com/kosmikus/records-sop Not exactly sure what the requirements are, though, and it still needs an unreleased version of generics-sop. Should probably look at Csongor's version now and figure out the differences.
generic-records
and records-sop
both look amazing!
I'm not entirely sure I get @arianvp 's original proposal.
(Without knowing very much about GraphQL) my thought and hope, which may be what Arian was suggesting, was to not change the Servant DSL at all, but instead just generate the graphQL schema from the API definition. Each endpoint being one query in the schema:
type API = "user" :> Capture "id" String :> Get '[JSON] User
data User = User { id :: String, name :: String}
Leads to the same behavior as:
var schema = new graphql.GraphQLSchema({
query: new graphql.GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
args: {
id: { type: graphql.GraphQLString }
},
resolve: function (_, args) {
return data[args.id];
}
}
}
})
});
var userType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString },
}
});
With a combinator "WithGraphQL" over the API to also server as GraphQL. Which (again, without knowing much about GraphQL) would be dope.
Hmm. the problem is that REST and GraphQL are really a totally different thing. in GraphQL you only have 1 HTTP endpoint you talk to, and the actual graph queries are sent over the network. In this its more similar to SQL than REST.
I think we can do similar type-safe stuff for GraphQL HTTP APIs as we do for REST HTTP APIs, but it would lead to something new in my opinion, that doesn't really fit into current servant API descriptions.
Hmm. the problem is that REST and GraphQL are really a totally different thing. in GraphQL you only have 1 HTTP endpoint you talk to, and the actual graph queries are sent over the network. In this its more similar to SQL than REST.
Maybe I could have been clearer about what I'm proposing. It's that we can generate that one GraphQL endpoint from the existing API description. Just take a look at the servant
code and javascript code (from the GraphQL tutorial) above. They contain roughly the same data. So:
type API = "user" :> Capture "id" String :> Get '[JSON] User
data User = User { id :: String, name :: String}
server :: Server API
server = ...
type WholeAPI = API :<|> GraphQLFor API
wholeServer :: Server WholeAPI
wholeServer = server :<|> graphQLFor server
Should in theory be possible, no?
Ok, here's a sketch.
I haven't shown how to implement the instance for :<|>
. There are nuances there that I'd like to sleep on, but it's pretty evident to me that it should work.
In the comment is how we'd run a query. There's the graphql package to help with parsing.
I used the 'HasName' class to pick field names so that we could operate only at the (servant-server
) value-level, but maybe in practice we'd like to use paths for that? Similarly, I ignored argument names in functions, but perhaps in practice we'd like to use Capture
and/or QueryParam
names.
Closing this, as it'll be a separate package if it ever happens. Feel free to open issues in servant-graphql
@cpennington on IRC had the idea of integrating
servant
with GraphQL. That is, an interpretation that from the application developers perspective would feel like servant-server, but would handle GraphQL queries. This sounds pretty cool, asservant
applications would then all be able to handle GraphQL as well.