apollo watch method with variables - apollo-client

I am trying to use apollo client watch method in angular to query spring boot server. I am not able to pass arguments with this method.
Since "aid" is mandatory, when it is trying to make a call I getting error like
ERROR Error: GraphQL error: Variable 'aid' has coerced Null value for NonNull type 'String!'
Below is my code in typescript.
export class AgreementGQL extends Query {
document = gql`query agreement($aid: String!) {
agreement(id: $aid) {
id
name
}
}
Below is calling code to the agreement. Where agreement is injected in constructor.
this.agreement.watch({
aid: "1234567"
}).valueChanges.subscribe(result => {
console.log("*********** result : " + JSON.stringify(result.data));
});
I tried using "variables" as well, but no luck.
this.agreement.watch({ variables:{
aid: "1234567"
}}).valueChanges.subscribe(result => {
console.log("*********** result : " + JSON.stringify(result.data));
});

You just need to set the value as a key/value pair like:
const myVal = '123';
Then pass that as an object into the watch method...
const agreement = this.agreementQuery
.watch({ myVal })
.valueChanges.pipe(pluck('data'));
Then Subscribe to get the data out:
agreement.subscribe(data => console.log(data));
This approach worked for me.

Related

Access context from Apollo GraphQL mutation field directive

I have an input type like this:
input PetGiraffe {
name: String #addUserLastName
}
Inside the directive, I need access to the request's context, so that I can add the user's last name to the giraffe's name. Here's the relevant part of what I've got so far:
const addUserLastNameDirective = {
typeDefs: gql`directive #addUserLastName on INPUT_FIELD_DEFINITION`,
transformer: (schema: GraphQLSchema, directiveName = 'addUserLastName') => {
return mapSchema(schema, {
[MapperKind.INPUT_OBJECT_FIELD]: (fieldConfig, fieldName, typeName, schema) => {
const directive = getDirective(schema, fieldConfig, directiveName)?.[0];
if (directive) {
// Need context in here because the user is in the context.
}
},
});
},
};
For queries, I understand you can override the fieldConfig.resolve method and get access to the context that way. But if I try that with this mutation, it throws: field has a resolve property, but Input Types cannot define resolvers.
The closest I could find was this from the graphql-tools docs, but that doesn't solve my problem of accessing the context.

Explanation for different implementations of resolver function in graphql

I've been reading through the graphQL docs and found that they've explained the implementation of the graphql server in 2 ways: one using graphql-yoga which is a fully featured graphql server and another one is using graphql, express-graphql and express. In both cases, we pass the schema and resolver functions while creating the server instance.
But the implementation of resolver function differs. While using graphql-yoga, the resolver function is provided with 4 arguments which contains information about the parent object, arguments received, context, info. whereas in the other case (using graphql), the resolver function only gets the arguments object.
Why is that so ? If I want the info, context objects, how do I get it ?
Using graphql-yoga example: https://graphql.org/learn/execution/
Using graphql example: https://graphql.github.io/graphql-js/mutations-and-input-types/
// Code example using graphql
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var schema = buildSchema(`
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`);
var root = {
rollDice({numDice, numSides}) {
return [1, 2];
},
addDice({numDice}) {
console.log("Adding something");
return "Added";
}
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
// Code example using graphql-yoga
let graphqlServer = require("graphql-yoga");
const typeDefs = `
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`;
const resolvers = {
Query: {
rollDice(parent, args, context, info) {
console.log(args.numDice);
console.log(args.numSides);
return [1, 2];
}
},
Mutation: {
addDice(parent, args, context, info) {
console.log(args.numDice);
return "Added";
}
}
};
const server = new graphqlServer.GraphQLServer({
typeDefs,
resolvers
});
server.start(() => {
console.log("server started on localhost:4000");
});
Difference between these 2 code snippets:
The resolver functions are present inside appropriate types (i.e. Query, Mutation) in one case. In the other case, they are present inside one root object. This means that I can have methods with same name in Query and Mutation in the first case, whereas in the second case that's not possible since they are keys of a single object and keys should be unique.
Why is this so ? Am I basically missing something ? How can the implementation details differ from one package to another ?
REAL TALK: the GraphQL.js docs are not that great. In my opinion, they never should have used examples with buildSchema in the first place because it understandably leads to this kind of confusion.
GraphQL.js (i.e. the graphql package) is the JavaScript implementation of GraphQL. Building a schema in GraphQL.js is done programmatically, by constructing an instance of the GraphQLSchema class:
const userType = new GraphQLObjectType({
name: 'User',
fields: {
id: {
type: GraphQLID,
},
email: {
type: GraphQLString,
},
},
});
const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
resolve: () => ({ id: 1, email: 'john.doe#example.com' }),
},
},
});
const schema = new GraphQLSchema({
query: queryType,
})
If we print this schema in Schema Definition Language (SDL), it looks like this:
type Query {
user: User
}
type User {
id: ID
email: String
}
Working with SDL is much easier than having to write out all that code. However, GraphQL.js does not provide a way to build a fully-featured schema from SDL. It does provide a buildSchema function, but this utility constructs a schema without any resolvers (and a number of other features like union/interface type resolution).
The graphql-tools package provides a makeExecutableSchema function that lets you build a schema from SDL and a resolver map object. This is what's used under the hood by apollo-server and graphql-yoga. makeExecutableSchema constructs a schema from SDL using buildSchema and then mutates the resulting object, adding the resolvers in after the fact.
In GraphQL.js, the resolve function (or resolver) for a field takes four parameters -- the parent value, the field's arguments, the context and a GraphQLResolveInfo object. If we're creating a GraphQLObjectType like userType in the above example, this is the optional function we can provide for each of the fields in our object. This is the same function you define when you construct a resolver map to use with graphql-yoga. This is the only implementation of a field resolver.
So what's the deal with buildSchema??
The examples in the docs take advantage of GraphQL's default field resolver:
export const defaultFieldResolver: GraphQLFieldResolver<any, *> = function(
source,
args,
contextValue,
info,
) {
if (typeof source === 'object' || typeof source === 'function') {
const property = source[info.fieldName];
if (typeof property === 'function') {
return source[info.fieldName](args, contextValue, info);
}
return property;
}
};
As you can see, the default resolution logic looks for a property with the same name as the field on the source (parent) value. In our example above, the user resolver returns {id: 1, email: 'john.doe#example.com'} -- this is the value the field resolves to. The field is of the type User. We do not have a resolver defined for our id field, so the default resolver does its thing. The id field resolves to 1 because that's the value of the property named id on the parent object the resolver receives.
However, the parent value can also be a function instead of an object. If it's a function, it gets called first and then the return value is used. What does the function get called with? Well, it can't pass it a parent value (because of infinite recursion), but it can pass it the remaining three parameters (args, context and info). So that's what it does.
Now for the magic trick 🎩🐇
In our example, I can omit the resolver for the user field and pass a function to the root value instead.
const root = {
user: () => ({id: 1, email: 'john.doe#example.com'})
}
The root object is just an optional object that's passed down as the parent value to resolvers at the root level (like your Query or Mutation types). Otherwise, those resolvers would not have a parent value.
Query is an operational root type -- it serves as an "entry point" to the rest of your schema. Any fields on the Query type will be passed the root object as the parent value. If I omit a resolver for the user field, the default resolver will 1) examine the parent object for a property with the same name, 2) find a property and determine that it's a function, 3) call the function, 4) resolve the field to the return value of the function.
TADA!
However, because the function is called by the default resolver, and is not used as a resolver itself, it will only receive the three aforementioned parameters, instead of 4.
This is a neat way to work around not being able to actually provide custom resolvers for a schema, but it's very limited. It only works for root types, so we can't similarly provide fake resolvers for User fields or other types. We can't use interfaces or unions in our schema because we can't provide resolveType functions. And so on...
Hopefully that provides some clarity. And hopefully we can get the docs updated in the near future to avoid all this confusion in the first place.

Pass through GraphQL variables to second function in an elegant manner

I'm working with GraphQL and having some trouble finding the best way to pipe variables from the query to the result.
I have a schema like so:
type Fragment {
# The id of the fragment
id: String!
# The key of the fragment
key: String!
# The type of component
component_type: String!
# The params used to build the fragment
params: JSON
# Component data
data: JSON
children: [JSON]
items: [JSON]
}
The fragment is meant as a "cms" fragment. I want to pass some query data through to another backend after this resolves.
My query looks like this:
query getFragmentsWithItems($keys: [String!]!
$platform: PlatformType
$version: String
$userInfo: UserInput
$userId: Int
) {
fragmentsWithItems(keys: $keys, platform: $platform, version: $version, userInfo: $userInfo, userId: $userId) {
key
data
children
params
items
}
}
Here's the problem: I have some query data in the data field from the Fragment. That data is not available until that Fragment has resolved. I want to take that data and send it to a different backend. I want to do this with GraphQL, and I was hoping to do something like:
Fragment: () => {
async query(obj, args, context, info, {modles}) => {
const items = await models.getItems(obj.query_string);
}
}
But I need the user_info and user_id that I passed to the original query. Apparently that is only accessible from the info argument which is not meant to be used.
The other path I've taken is to have a manual resolver that does something like so:
const resolveFI = ({ keys, platform, version, userInfo, userId, models }) => {
if (!keys || !keys.length) {
return Promise.resolve(null);
}
return models.release.get({ platform, version }).then(release =>
Promise.all(
keys.map(key =>
models.fragments.get({
key,
platform,
version,
release: release.id
})
)
).then(data => {
const promises = [];
data.rows.forEach(r => {
if (r.data.query_data) {
const d = {
// Can just ignore
filters: r.data.query_data.filters || {},
user_info: userInfo,
user_id: userId
};
promises.push(
new Promise(resolve => {
resolve(
models.itemSearch.get(d).then(i => ({ items: i.items, ...r }))
);
})
);
}
...etc other backends
This works, however a manual promise chain seems to defeat the purpose of using GraphQL.
The last thing I tried was making items a non-scalar type, something like:
type Fragment {
items: ItemSearchResult(user_info: UserInput) etc
But since I can't pipe the actual result from Fragment to the ItemSearchResult that doesn't work.
I realize this is pretty long-winded so I'm open to edits or clarifying.
I'm looking to see if I've missed a better approach or if I should just bag it and have the client apps do the item query after they get the Fragment data back.
It's not that you're not supposed to use info -- it's just a tremendous pain in the butt to use ;) In all seriousness, it's meant to be used for optimization and more advanced use cases, so you shouldn't hesitate to use it if a better solution doesn't present itself. There are libraries out there (like this one) that you can use to parse the object more easily.
That said, there's a couple of ways I imagine you could handle this:
1.) Inside your query resolver(s)
getFragmentsWithItems: async (obj, args, ctx, info) => {
const fragments = await howeverYouDoThat()
const backendCalls = fragments.map(fragment => {
// extract whatever data you need from the fragment
return asyncCallToBackEnd()
})
await backendCalls
return fragments
}
Unfortunately, if you have a lot of different queries returning fragments, you'll end up with redundancy.
2.) Inside the resolver for an existing field (or an additional one) on the Fragment type.
If you go this route, and you need args passed to the query field, you can extract them using the info. Alternatively, you can also mutate the context object inside your query resolver and attach those arguments to it. Then, all resolvers "below" the query resolver (like the resolvers for your Fragment fields) can access those arguments through the context.
3.) Apollo Server lets you define a formatResponse function when configuring its middleware. This essentially provides a hook to do whatever you want with the response before it's returned to the client. You could parse the response inside that function and make the calls to the other backend from there.

How can you prevent running a resolver based on the selection set?

Example:
query {
me {
starredPosts {
id
}
}
}
How can the server notice that only the ids are requested, and use the already-fetched user.starredPosts (an array of ids), instead of calling Posts.findOne(id) for each id?
We had the same problem and are in the process of open-sourcing the tools we've built out over the last year and a half internally to address these issues: https://github.com/4Catalyzer/graphql-node-resource/pull/1.
The solution we use is, for object resolvers like that, to resolve them to a "stub" object that contains only the ID, something like:
const childField = {
type: ChildType,
resolve: obj => ({ id: obj.childId }),
};
Then we use DataLoader to fetch the additional fields on the child objects when they're required by using our own default resolver.
We connect to our internal REST API, which supports batching on those requests, so queries that require additional fields get efficiently dispatched and resolved.
However, this does introduce potential for error when writing custom resolvers, as there's no guarantee that obj actually has the relevant fields. We've addressed this by setting up our static types to prevent unchecked access to properties of obj.
You can examine info.fieldNodes[0].selectionSet.selections or use graphql-fields package:
const postsIds = user.starredPosts
const selectionSet = Object.keys(graphqlFields(info))
const onlySelectingId = isEqual(['__typename', 'id'], selectionSet.sort())
if (onlySelectingId) {
return postIds.map(id => ({ id, __typename: 'Post' }))
} else {
return favs.map(id => Post.findOneById(id))
}

GraphQL - Get all fields from nested JSON object

I'm putting a GraphQL wrapper over an exiting REST API as described in Zero to GraphQL in 30 minutes. I've got an API endpoint for a product with one property that points to a nested object:
// API Response
{
entity_id: 1,
nested_object: {
key1: val1,
key2: val2,
...
}
}
Is it possible to define the schema so that I can get this entire nested object without explicitly defining the nested object and all of its properties? I want my query to just specify that I want the nested object, and not need to specify all the properties I want from the nested object:
// What I want
{
product(id: "1") {
entityId
nestedObject
}
}
// What I don't want
{
product(id: "1") {
entityId
nestedObject {
key1
key2
...
}
}
}
I can do the second version, but it requires lots of extra code, including creating a NestedObjectType and specifying all the nested properties. I've also figured out how to automatically get a list of all the keys, like so:
const ProductType = new GraphQLObjectType({
...
fields: () => ({
nestedObject: {
type: new GraphQLList(GraphQLString),
resolve: product => Object.keys(product.nested_object)
}
})
})
I haven't figured out a way to automatically return the entire object, though.
You may try to use scalar JSON type. You can find more here (based on apollographql).
add scalar JSON to a schema definition;
add {JSON: GraphQLJSON} to a resolve functions;
use JSON type in a shema:
scalar JSON
type Query {
getObject: JSON
}
an example of a query:
query {
getObject
}
a result:
{
"data": {
"getObject": {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
}
}
Basic code:
const express = require("express");
const graphqlHTTP = require("express-graphql");
const { buildSchema } = require("graphql");
const GraphQLJSON = require("graphql-type-json");
const schema = buildSchema(`
scalar JSON
type Query {
getObject: JSON
}
`);
const root = {
JSON: GraphQLJSON,
getObject: () => {
return {
key1: "value1",
key2: "value2",
key3: "value3"
};
}
};
const app = express();
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true
})
);
app.listen(4000);
console.log("Running a GraphQL API server at localhost:4000/graphql");
I can do the second version, but it requires lots of extra code, including creating a NestedObjectType and specifying all the nested properties.
Do it! It will be great. That's the way to go in order to use GraphQL to its full potential.
Aside from preventing over-fetching, it also gives you a lot of other benefits like type validation, and more readable and maintainable code since your schema gives a fuller description of your data. You'll thank yourself later for doing the extra work up front.
If for some reason you really don't want to go that route though and fully understand the consequences, you could encode the nested objects as strings using JSON.stringify.
But like I said, I recommend you don't!

Resources