Apollo Server Stitching - Get parent of parent in resolver - graphql

I have a questions based on https://www.apollographql.com/docs/graphql-tools/schema-stitching/
Let's say I have the following schemas (Note that it's built for an example, it makes no sense to have it structured this way)
//First schema
type User {
id
cars: [Car]!
}
type Car {
id
name: String
model: String
}
//Second schema
type Color {
id
name: String
rgb: String
opacity: Int
}
//Defined in Apollo Server
extend type Car {
color: Color
}
And the following resolver for
resolvers: {
Car: {
color: {
fragment: '... on Car { id }',
resolve(parent, args, context, info) {
return delegateToSchema({
schema: secondSchema,
operation: 'query',
fieldName: 'colorByUserAndCarId',
args: {
id: parent.id,
userId: ?
},
context,
info,
});
},
},
},
}
How can I get the userId, which is on type User and not Car?
While I was searching for an answer to this, I thought of some solutions, but I can't figure out how to make any of them work..
Make the type Color part of Car, so I would have every color's field in car directly, so I guess I would have the resolver based on User instead.. ?
Changing the fragment for '... on User', while being on type Car, but so far that doesn't work.
Adding the userId to the type Car by extending it, but I can't find how to get the userId anyway
Changing the schema and types from the root is not an option, all modifications need to be made within Apollo Server.

Writing the question and potential solutions helped me understanding better how it works and how this could be achieved.
I can add a resolver for "car" the same way I did for "color" and by the time I get in my car resolver, the object value for "car" is already there.. the parent value would look like { id: x, car: {...}}, id being the userId, so I can just do this in my car resolver
let myCar = parent.car;
myCar.userId = parent.id;
return myCar;
When I'll be in my color resolver, I'll be able to do parent.userId.

Related

How get rid of redundant wrapper object of a mutation result?

When I'm making a request to my backend through a mutation like that:
mutation{
resetPasswordByToken(token:"my-token"){
id
}
}
I'm getting a response in such format:
{
"data": {
"resetPasswordByToken": {
"id": 3
}
}
}
And that wrapper object named the same as the mutation seems somewhat awkward (and at least redundant) to me. Is there a way to get rid of that wrapper to make the returning result a bit cleaner?
This is how I define the mutation now:
export const ResetPasswordByTokenMutation = {
type: UserType,
description: 'Sets a new password and sends an informing email with the password generated',
args: {
token: { type: new GraphQLNonNull(GraphQLString) },
captcha: { type: GraphQLString },
},
resolve: async (root, args, request) => {
const ip = getRequestIp(request);
const user = await Auth.resetPasswordByToken(ip, args);
return user.toJSON();
}
};
In a word: No.
resetPasswordByToken is not a "wrapper object", but simply a field you've defined in your schema that resolves to an object (in this case, a UserType). While it's common to request just one field on your mutation type at a time, it's possible to request any number of fields:
mutation {
resetPasswordByToken(token:"my-token"){
id
}
someOtherMutation {
# some fields here
}
andYetAnotherMutation {
# some other fields here
}
}
If we were to flatten the structure of the response like you suggest, we would not be able to distinguish between the data returned by one mutation from another. We likewise need to nest all of this inside data to keep our actual data separate from any returned errors (which appear in a separate errors entry).

I dont want to redefine properties when defining a GraphQL type. Is there a way to get past it? [duplicate]

Is it possible to use inheritance with GraphQL input types?
Something like that (this, of course, doesn't work with input types):
interface UserInputInterface {
firstName: String
lastName: String
}
input UserInput implements UserInputInterface {
password: String!
}
input UserChangesInput implements UserInputInterface {
id: ID!
password: String
}
No, the spec does not allow input types to implement interfaces. And GraphQL type system in general does not define any form of inheritance (the extends keyword adds fields to an existing type, and isn't for inheritance). The spec is intentionally constrained to stay simple. This means that you're stuck repeating fields across input types.
That said, depending on the way you construct your schema, you could build some kind of type transformer that appends the common fields programmatically based on some meta-data, e.g. a directive.
Better yet, you might be able to solve your problem via composition (always keep composition over inheritance in mind).
E.g.
input Name {
firstName: String
lastName: String
}
input UserInput {
name: Name
password: String!
}
input UserChangesInput {
name: Name
id: ID!
password: String
}
The client now has to send an object a level deeper, but that doesn't sound like much of a price for avoiding big repeating chunks. It might actually be good for the client as well, as they can now have common logic for building names, regardless of the query/mutation using them.
In this example, where it's only 2 simple fields, this approach is an overkill, but in general - I'd say it's the way to go.
Starting with the June2018 stable version of the GraphQL spec, an Input Object type can extend another Input Object type:
Input object type extensions are used to represent an input object type which has been extended from some original input object type.
This isn't inheritance per se; you can only extend the base type, not create new types based on it:
extend input MyInput {
NewField: String
}
Note there is no name for the new type; the existing MyInput type is extended.
The JavaScript reference implementation has implemented Input Object extensions in GraphQL.js v14 (June 2018), though it's unclear how to actually pass the extended input fields to a query without getting an error.
For actual type inheritance, see the graphql-s2s library.
It's doable using a custom directive.
Code Summary
const typeDefs = gql`
directive #inherits(type: String!) on OBJECT
type Car {
manufacturer: String
color: String
}
type Tesla #inherits(type: "Car") {
manufacturer: String
papa: String
model: String
}
type Query {
tesla: Tesla
}
`;
const resolvers = {
Query: {
tesla: () => ({ model: 'S' }),
},
Car: {
manufacturer: () => 'Ford',
color: () => 'Orange',
},
Tesla: {
manufacturer: () => 'Tesla, Inc',
papa: () => 'Elon',
},
};
class InheritsDirective extends SchemaDirectiveVisitor {
visitObject(type) {
const fields = type.getFields();
const baseType = this.schema.getTypeMap()[this.args.type];
Object.entries(baseType.getFields()).forEach(([name, field]) => {
if (fields[name] === undefined) {
fields[name] = { ...field };
}
});
}
}
const schemaDirectives = {
inherits: InheritsDirective,
};
Query:
query {
tesla {
manufacturer
papa
color
model
}
}
Output:
{
"data": {
"tesla": {
"manufacturer": "Tesla, Inc",
"papa": "Elon",
"color": "Orange",
"model": "S",
}
}
}
Working example at https://github.com/jeanbmar/graphql-inherits.
If you came here looking for an explanation for the "implements", keyword, here it is:
An object type must be a super‐set of all interfaces it implements. The object type must include a field of the same name for every field defined in an interface.
(Excerpt taken from the June 2018 GraphQL spec.)
Here's an example
interface Foo {
id: ID!
foo: Int!
}
type Bar implements Foo #entity {
id: ID!;
foo: Int!;
bar: Int!;
}
So the Bar type doesn't inherit from the Foo interface, but it implements it. The former must include all the fields that are listed in the latter.
I think that this is a nice way to annotate types that should be like other types.

Skipping over a resolver for a query [duplicate]

I think I'm missing something obvious in the way GraphQL resolvers work. This is a simplified example of my schema (a Place that can have AdditionalInformation):
import { ApolloServer, gql } from 'apollo-server';
const typeDefs = gql`
type Place {
name: String!
additionalInformation: AdditionalInformation
}
type AdditionalInformation {
foo: String
}
type Query {
places: [Place]
}
`;
And the associated resolvers:
const resolvers = {
Query: {
places: () => {
return [{name: 'Barcelona'}];
}
},
AdditionalInformation: {
foo: () => 'bar'
}
};
const server = new ApolloServer({typeDefs, resolvers});
server.listen().then(({ url }) => {
console.log(`API server ready at ${url}`);
});
When I execute a basic query:
{
places {
name,
additionalInformation {
foo
}
}
}
I always get null as the additionalInformation:
{
"data": {
"places": [
{
"name": "Barcelona",
"additionalInformation": null
}
]
}
}
It's my first GraphQL app, and I still don't get why the AdditionalInformation resolver is not automatically executed. Is there some way to let GraphQL know it has to fire it?
I've found this workaround but I find it a bit tricky:
Place: {
additionalInformation: () => { return {}; }
}}
Let's assume for a moment that additionalInformation was a Scalar, and not an Object type:
type Place {
name: String!
additionalInformation: String
}
The value returned by the places resolver is:
[{name: 'Barcelona'}]
If you were to make a similar query...
query {
places {
name
additionalInformation
}
}
What would you expect additionalInformation to be? It's value will be null because there is no additionalInformation property on the Place object returned by the places resolver.
Even if we make additionalInformation an Object type (like AdditionalInformation), the result is the same -- the additionalInformation field will resolve to null. That's because the default resolver (the one used when you don't specify a resolver function for a field) simply looks for a property with the same name as the field on the parent object. If it fails to find that property, it returns null.
You may have specified a resolver for a field on AdditionalInformation (foo), but this resolver is never fired because there's no need -- the whole additionalInformation field is null so all of the resolvers for any fields of the associated type are skipped.
To understand why this is a desirable behavior, imagine a different schema:
type Article {
title: String!
content: String!
image: Image
}
type Image {
url: String!
copyright: String!
}
type Query {
articles: [Article!]!
}
We have a database with an articles table and an images table as our data layer. An article may or may not have an image associated with it. My resolvers might look like this:
const resolvers = {
Query: {
articles: () => db.getArticlesWithImages()
}
Image: {
copyright: (image) => `©${image.year} ${image.author}`
}
}
Let's say our call getArticlesWithImages resolves to a single article with no image:
[{ title: 'Foo', content: 'All about foos' }]
As a consumer of the API, I request:
query {
articles {
title
content
image
}
}
The image field is optional. If I get back an article object with a null image field, I understand there was no associated image in the db. As a front end client, I know not to render any image.
What would happen if GraphQL returned a value for the image regardless? Obviously, our resolver would break, since it would not be passed any kind of parent value. Moreover, however, as a consumer of the API, I would have to now parse the contents of image and somehow determine whether an image was in fact associated with the article and I should do something with it.
TLDR;
As you already suggested, the solution here is to specify a resolver for additionalInfo. You can also simply return that value in your places resolver, i.e.:
return [{name: 'Barcelona', additionalInfo: {}}]
In reality, if the shape of your schema aligns with the shape of your underlying data layer, it's unlikely you'll encounter this sort of issue when working with real data.

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