Can you spread a GraphQL field? - graphql

Basically I'm trying to spread a GraphQL field so I don't have as much nesting.
For instance:
query {
user(id: aasdfasdf) {
...address {
street
}
}
}
output
{
user: {
street: '111 Cool Street'
}
}

No. From the spec:
When querying an Object, the resulting mapping of fields are conceptually ordered in the same order in which they were encountered during query execution, excluding fragments for which the type does not apply and fields or fragments that are skipped via #skip or #include directives.
The structure of the response will always match the structure of the request. GraphQL does not inherently support any syntax to manipulate the structure of the returned response. In other words, you get what you request, and you can only request what is in the schema.
Although it's experimental, if you're using a javascript client, you could take a look at graphql-lodash. It plugs into existing clients and enables you to use custom directives to apply arbitrary transformations to your response.

Related

Ability to extend all Apollo/GraphQL mutations with same fields. "Generic fragments" wanted

Problem definition
In the project I'm currently working on we're using React with Apollo Client.
On all our mutations, we have the following fields in our response:
ok
errors {
field
messages
}
The back-end extends all mutations with these fields, hence it would be nice to have a good, short way to include these fields on all mutations in the front-end, as well as be able to change this "fragment" in the future.
Hence I'm interested in shortening these same 4 lines into 1 across all my mutations.
What I've tried so far:
I've tried looking into Apollo fragments, however they seem to require a type that the fields are on or "relating to", eg.
fragment NameParts on Person {
firstName
lastName
}
Here, the frament NameParts is created using Person. However, I'm interested in extending ALL mutations.
It would be nice if I could make a generic fragment like so:
fragment OkAndErrors {
ok
errors {
field
messages
}
}
This does not seem to be possible.
I've also tried making a string, and importing it into my mutation like so:
export const OK_AND_ERRORS: string = `
ok
errors {
field
messages
}
`;
import { gql } from "apollo-boost";
import { OK_AND_ERRORS } from "./OK_AND_ERRORS";
export const CREATE_API = gql`
mutation CreateApi($newApi: ApiCreateGenericType!) {
createDrugapi(newDrugapi: $newDrugapi) {
${OK_AND_ERRORS}
(...rest of mutation is omitted for brevity)
}
}
`;
Again, it did not work.
I'm not sure if I can use gql function in a smart way with strings or JSON?
There's also inline fragments but I'm in doubt if it can be used for what I need, and the documentation of inline-fragments in Apollo is scarce.
In essence: Is there a smart way to extend Apollo mutations? Does generic fragments exist?
First of all, fragments aren't limited to Apollo but are part of just regular GraphQL queries. The GraphQL site itself actually has a nice explanation of them: https://graphql.org/learn/queries/#fragments
Essentially we can put fragments onto any query to extract data dependencies, but they're also useful for matching types using their on X type conditions.
In your case, you're saying that each mutation returns a kind of result type that has a common errors field. This tells me that you may have a MutationError kind of type already. However these MutationResult types that all have an errors fields should all implement an interface, if they don't already.
Interfaces are a great tool in the schema language to define explicitly that a type implementing it must always include a certain set of fields, in this case the errors fields. This means that we'd write our results like so:
interface MutationResult {
errors: [MutationError!]
}
type ExampleMutationResult implements MutationResult {
ok: Boolean
errors: [MutationError!]
}
type UserMutationResult implements MutationResult {
user: User
errors: [MutationError!]
}
As you can see above, the MutationResult interface is now implemented by several results, which allows me to write a reusable fragment that can be applied to any type that implements it, e.g.
fragment MutationResultErrors on MutationResult {
errors {
field
messages
}
}
Which I can then start using for all mutation queries that I'm defining. This is more predictable and expected in GraphQL rather than doing some client-side document transformations, string interpolations in a query, or something similar, as it'll be baked into your schema.
Side note: What I'd also say is, I've found that it's commonly seen that people have started to split their mutations into "errors" and "results" and made some kind of union or interface to differentiate between the two. But often they then implement generic errors with messages. It's important to say that errors that don't carry any relational data are actually already baked into GraphQL: https://spec.graphql.org/June2018/#sec-Errors

GraphQL Schema Language Handle Map Type from Uncontrolled API [duplicate]

Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm

Apollo/GraphQL field type for object with dynamic keys

Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm

Is there a way to pass a fragment to graphiql?

It's possible to pass a query, but apparently not a fragment:
server.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
query: `# Welcome to GraphiQL
query PostsForAuthor {
author(id: 1) {
firstName
posts {
title
votes
}
}
}`}));
Update 10/12/2017
It is possible to send fragments along with a query using Apollo's client:
http://dev.apollodata.com/core/fragments.html
This is not a solution to the original question, however; I would like to pass fragments to a graphiql server instance at startup.
by startup do you mean from the server? if so I don't believe that's how fragments are used. my understanding is as follows:
on the server you provide Types (like User)
on the client you query those Types using queries and fragments
for instance, if you provide type User on the server, on the client graphQL you can use fragments to query that type:
graphQL (client)
fragment authorData on AuthorType{
firstName
posts {
title
votes
}
}
query PostsForAuthor {
author(id: 1) {
...authorData
}
}
As you noticed (and as detailed here) GraphiQL takes a query argument:
query: an optional GraphQL string to use as the initial displayed query, if undefined is provided, the stored query or defaultQuery will be used.
If putting a fragment in as the value for that argument doesn't work, then I don't believe there is any way to start with a fragment ... but really why would you even want to? A fragment by itself isn't executable, and the whole idea is to start GraphiQL with a (executable) query.
If all you want is to be able to copy/paste in some text that you use frequently in your queries, a bookmarklet might be a better idea.

Can graphql return aggregate counts?

Graphql is great and I've started using it in my app. I have a page that displays summary information and I need graphql to return aggregate counts? Can this be done?
You would define a new GraphQL type that is an object that contains a list and a number. The number would be defined by a resolver function.
On your GraphQL server you can define the resolver function and as part of that, you would have to write the code that performs whatever calculations and queries are necessary to get the aggregate counts.
This is similar to how you would write an object serializer for a REST API or a custom REST API endpoint that runs whatever database queries are needed to calculate the aggregate counts.
GraphQL's strength is that it gives the frontend more power in determining what data specifically is returned. Some of what you write in GraphQL will be the same as what you would write for a REST API.
There's no automatic aggregate function in GraphQL itself.
You can add a field called summary, and in the resolve function calculate the totals.
You should define a Type of aggregated data in Graphql and a function you want to implement it. For example, if you want to write the following query:
SELECT age, sum(score) from student group by age;
You should define the data type that you want to return:
type StudentScoreByAge{
age: Int
sumOfScore: Float
}
and a Graphql function:
getStudentScoreByAge : [StudentScoreByAge]
async function(){
const res = await client.query("SELECT age, sum(score) as sumOfScore
from Student group by age");
return res.rows;
}
... need graphql to return aggregate counts? Can this be done?
Yes, it can be done.
Does GraphQL does it automatically for you? No, because it does not know / care about where you get your data source.
How? GraphQL does not dictate how you get / mutate the data that the user has queried. It's up to your implementation to get the requested aggregated data. You could get aggregated data directly from your MongoDB and serve it back, or you get all the data you need from your data source and do the aggregation yourself.
If you are using Hasura, in the explorer, you can definitely see an "agregate" table name, thus, your query would look something similar to the following:
query queryTable {
table_name {
field1
field2
}
table_name_aggregate {
aggregate { count }
}
}
In your results, you will see the total row count for the query
"table_name_aggregate": {
"aggregate": {
"count": 9973
}
This depends on whether you build the aggregator into your schema and are able to resolve the field.
Can you share what kind of GraphQL Server you're running? As different languages have different implementations, as well as different services (like Hasura, 8base, and Prisma).
Also, when you say "counts", I'm imagining a count of objects in a relation. Such as:
query {
user(id: "1") {
name
summaries {
count
}
}
}
// returns
{
"data": {
"user": {
"name": "Steve",
"summaries": {
"count": 10
}
}
}
}
8base provides the count aggregate by default on relational queries.

Resources