Representing calculations/script functions in GraphQL queries and schema - graphql

We are using GraphQL as a query language for a data aggregation engine.
I am looking for ideas to represent simple (or complex) arithmetic calculation functions in GraphQL referring to existing types/attributes defined in a schema, and can be used over existing attributes.
I am looking into Custom scalars and Directives
Example -
{
item{
units
price_per_unit
market_price: function:multiply(units, price_per_unit)
market_price_usd: function:usdPrice(units, price_per_unit, currency)
}
}
where function:multiply is already defined in the GraphQL schema as a type
functions {
multiply(operand 1, operand2) {
result
}
usdPrice(operand1, operand2, currency) {
result: {
if(currency == GBP) {
operand1 * operand2 * .76
}
{
}
internally resolver will multiply operand 1 and operand 2 to create result.

This isn't something GraphQL is especially good at. By far the easiest thing to do will be to retrieve the individual fields and then do the computation on the client, something like
data.item.forEach((i) => { i.total_price = i.units * i.price_per_unit });
In particular, there's no way to run any sort of "subquery" in GraphQL. Given a "multiply" function like you've shown there's no GraphQL syntax that will let you "call" it with any particular inputs.
If you think the specific computed values are common enough, you can also add them to the GraphQL schema, and compute them server-side if requested using a custom resolver function.
type Item {
units: Int!
pricePerUnit: CurrencyValue!
# computed, always units * pricePerUnit
marketPrice: CurrencyValue!
}
type CurrencyValue {
amount: Float!
currency: Currency!
# computed, always amount * currency { usd }
usd: Float!
}
type Currency {
code: String!
"1 currency = this many US$"
usd: Float!
}
allowing queries like
{
item {
marketPrice { usd }
}
}

Related

Need recommendations for Graphql resolvers structure

I am trying to make structure for GraphQl resolvers, but a bit stuck.
Imagine that we have different sale type entities
type Sale {
name: String
value: Float
type: Int
}
Currently we have a query which gets all the sales by type and compare them for different date ranges
type SaleResult {
name: String
main_value: Float
compare_value: Float
difference: Float
type: Int
}
input DateParamsInput {
start: DateTime!
end: DateTime!
}
input SomeFilterInput {
dateRangeMain: DateParamsInput
dateRangeCompare: DateParamsInput
type: [Int]
}
Query {
getSales(filter: SomeFilterInput) [SaleResult]
}
and now we were requested to add a switcher to grab data either without compare value or with it. We can add new query but I am trying to find a way to have single query for both cases
There is a solution, how I see it
Query {
getSales2(type: [Int]) {
sale {
name
type
diff(mainRange, compareRange) {
main_value
compare_value
diff
}
value(mainRange)
}
}
}
In this case we will have 2 nested resolvers: diff and value. The first one can return data with compare value and another one for single value
Another way
Query {
getSales2(type: [Int], mainRange) {
sale {
name
type
value
compare(compareRange) {
value
diff
}
}
}
}
but in this case we have to have value before compare resolver starts and I am not sure this is the working approach
Since I have no one to validate this, I am asking for your help. Do you have any thoughts?

Validation error of type UndefinedFragment: Undefined fragment

I've a graphql-jave v8.0 app running on Spring boot v1.5.10, and I'm trying to utilize 'fragment' feature of GraphQL to fetch limited number of fields with the following schema type definition:
type School {
id: ID
name: String
address: String
age: String
jobTitle: String
...
}
fragment UserFields on School {
age
jobTitle
}
type Query {
user (id: String!): School!
}
schema {
query: Query
}
When I execute this query:
{
user (id: "123")
{
... UserFields
}
}
The expected result should be:
{
"user": {
"age": "12",
"jobTitle": "student"
}
}
However, It results in the following error
"message": "Validation error of type UndefinedFragment: Undefined fragment
UserFields # 'user'",
Off course I can do this with explicitly passing the field name in the query but for the sake of example, I'm interested in utilizing fragment feature of GraphQL.
Any idea what I'm doing wrong please?
Fragments aren't defined in the schema, they're something for letting you create abstractions whilst building complex queries -- the main purpose is to allow you to avoid repetition when querying the same type in multiple parts of your query.
As Andrew said, and as the official docs exemplify, fragments are not defined in the schema but (ad hoc) in the query:
{
user (id: "123") {
... UserFields
}
}
fragment UserFields on School {
age
jobTitle
}
Unconditional fragments (like the one here) are used to avoid repetition. Imagine having multiple places where you want to select age and jobTitle in the same operation.
Conditional fragments, on the other hand, are used to make a conditional selection depending on the concrete interface implementation or union subtype.

How to filter greater than in GraphQL

type Person {
firstName: String!,
lastName: String!,
age: Int!
}
How to query all people what are older than 18?
It probably depends on the backend you are using but e.g. in graph.cool you could something like this:
query {
allPersons(filter: {
age_gt: 18
}) {
firstName
lastName
}
}
Graphql will not include these complex filters atleast for
DECADE as graphql is acting to be library.
And libraries become part of other libraries and framework.They don't
become framework
Right now to use graphql to full extend you can combine it with ORM'S
like Hasura Or Graphile Or Prisma
Hasura
query {
article(
where: {rating: {_gte: 4}}
) {
id
title
rating
}
}
Prisma
query {
posts(where: {
AND: [{
title_in: ["My biggest Adventure", "My latest Hobbies"]
}]
}) {
id
title
}
}
Advice
Do not use graphql directly/independently ,use it with above orm's
If you are using Prisma as backend, you could use the greater than operator (_gt), like so:
query {
persons(where: {age_gt: 18}) {
firstName
lastName
age
}
}
You can also use other operators like:
_gt (greater than)
_lt (less than)
_gte (greater than or equal to)
_lte (less than or equal to)
_in (equal to)
_not_in (not equal to)
They are compatible with any data types like Integer, Float, Double, Text, Boolean, Date, etc.

How to create generics with the schema language?

Using facebook's reference library, I found a way to hack generic types like this:
type PagedResource<Query, Item> = (pagedQuery: PagedQuery<Query>) => PagedResponse<Item>
​
interface PagedQuery<Query> {
query: Query;
take: number;
skip: number;
}
​
interface PagedResponse<Item> {
items: Array<Item>;
total: number;
}
function pagedResource({type, resolve, args}) {
return {
type: pagedType(type),
args: Object.assign(args, {
page: { type: new GraphQLNonNull(pageQueryType()) }
}),
resolve
};
function pageQueryType() {
return new GraphQLInputObjectType({
name: 'PageQuery',
fields: {
skip: { type: new GraphQLNonNull(GraphQLInt) },
take: { type: new GraphQLNonNull(GraphQLInt) }
}
});
}
function pagedType(type) {
return new GraphQLObjectType({
name: 'Paged' + type.toString(),
fields: {
items: { type: new GraphQLNonNull(new GraphQLList(type)) },
total: { type: new GraphQLNonNull(GraphQLInt) }
}
});
}
}
But I like how with Apollo Server I can declaratively create the schema. So question is, how do you guys go about creating generic-like types with the schema language?
You can create an interface or union to achieve a similar result. I think this article does a good job explaining how to implement interfaces and unions correctly. Your schema would look something like this:
type Query {
pagedQuery(page: PageInput!): PagedResult
}
input PageInput {
skip: Int!
take: Int!
}
type PagedResult {
items: [Pageable!]!
total: Int
}
# Regular type definitions for Bar, Foo, Baz types...
union Pageable = Bar | Foo | Baz
You also need to define a resolveType method for the union. With graphql-tools, this is done through the resolvers:
const resolvers = {
Query: { ... },
Pageable {
__resolveType: (obj) => {
// resolve logic here, needs to return a string specifying type
// i.e. if (obj.__typename == 'Foo') return 'Foo'
}
}
}
__resolveType takes the business object being resolved as its first argument (typically your raw DB result that you give GraphQL to resolve). You need to apply some logic here to figure out of all the different Pageable types, which one we're handling. With most ORMs, you can just add some kind of typename field to the model instance you're working with and just have resolveType return that.
Edit: As you pointed out, the downside to this approach is that the returned type in items is no longer transparent to the client -- the client would have to know what type is being returned and specify the fields for items within an inline fragment like ... on Foo. Of course, your clients will still have to have some idea about what type is being returned, otherwise they won't know what fields to request.
I imagine creating generics the way you want is impossible when generating a schema declaratively. To get your schema to work the same way it currently does, you would have to bite the bullet and define PagedFoo when you define Foo, define PagedBar when you define Bar and so on.
The only other alternative I can think of is to combine the two approaches. Create your "base" schema programatically. You would only need to define the paginated queries under the Root Query using your pagedResource function. You can then use printSchema from graphql/utilities to convert it to a String that can be concatenated with the rest of your type definitions. Within your type definitions, you can use the extend keyword to build on any of the types already declared in the base schema, like this:
extend Query {
nonPaginatedQuery: Result
}
If you go this route, you can skip passing a resolve function to pagedResource, or defining any resolvers on your programatically-defined types, and just utilize the resolvers object you normally pass to buildExecutableSchema.

Share structure between GraphQL schemas

I have a Apollo GraphQL server talking to an API returning responses with roughly the following structure:
{
"pagination": {
"page": 1,
// more stuff
},
sorting: {
// even more stuff
},
data: [ // Actual data ]
}
This structure is going to be shared across pretty much all responses from this API, that I'm using extensively. data is going to be an array most of the time, but can also be an object.
How can I write this in an efficient way, so that I don't have to repeat all these pagination and sorting fields on every data type in my schemas?
Thanks a lot!
I've sorted your problem by creating a lib called graphql-s2s. It enhances your schema by adding support for type inheritance, generic types and metadata. In your case, creating a generic type for your Paginated object could be a viable solution. Here is an example:
const { transpileSchema } = require('graphql-s2s')
const { makeExecutableSchema } = require('graphql-tools')
const schema = `
type Paged<T> {
data: [T]
cursor: ID
}
type Node {
id: ID!
creationDate: String
}
type Person inherits Node {
firstname: String!
middlename: String
lastname: String!
age: Int!
gender: String
}
type Teacher inherits Person {
title: String!
}
type Student inherits Person {
nickname: String!
questions: Paged<Question>
}
type Question inherits Node {
name: String!
text: String!
}
type Query {
students: Paged<Student>
teachers: Paged<Teacher>
}
`
const executableSchema = makeExecutableSchema({
typeDefs: [transpileSchema(schema)],
resolvers: resolver
})
I've written more details about this here (in Part II).
When you define your schema, you will end up abstracting out pagination, sorting, etc. as separate types. So the schema will look something like:
type Bar {
pagination: Pagination
sorting: SortingOptions
data: BarData # I'm an object
}
type Foo {
pagination: Pagination
sorting: SortingOptions
data: [FooData] # I'm an array
}
# more types similar to above
type Pagination {
page: Int
# more fields
}
type SortingOptions {
# more fields
}
type BarData {
# more fields
}
So you won't have to list each field within Pagination multiple times regardless. Each type that uses Pagination, however, will still need to specify it as a field -- there's no escaping that requirement.
Alternatively, you could set up a single Type to use for all your objects. In this case, the data field would be an Interface (Data), with FooData, BarData, etc. each implementing it. In your resolver for Data, you would define a __resolveType function to determine which kind of Data to return. You can pass in a typename variable with your query and then use that variable in the __resolveType function to return the correct type.
You can see a good example of Interface in action in the Apollo docs.
The downside to this latter approach is that you have to return either a single Data object or an Array of them -- you can't mix and match -- so you would probably have to change the structure of the returned object to make it work.

Resources