How do I return a single or multiple results from the same graphQL query? - graphql

I have set up a GraphQL endpoint that returns me a client
query {
client(id:1) {
clientId
}
}
and another that returns a list of clients
query {
clients {
clientId
}
}
I have 2 backing db queries for these 2 graphql queries, but is there a way to have a single query for both? Or what is the graphql way of handling this?

The GraphQL way of handling this is exactly how you have done it. You usually need separate fields in your schema to handle retrieving one item vs multiple, just like you would have separate endpoints for these in a REST API.

You can have a single end point that returns a GraphQLList Type. This list can contain either one object or however many.
In your case, that single end point will be clients. You just have to use your backend to see if the consumer of your GraphQL API has supplied any arguments i.e. clientId. If the clientId has been supplied, filter your clientRepo by that supplied clientId. Otherwise return the whole list (repo) of clients.
clients: {
type: new GraphQLList(clientType), <--- Note this is a GraphQLList type
args: {
id: {
type: GraphQLInt
},
},
resolve: (parent, args) => {
if (args.id) {
return clientRepo.find(args.id);
}
return clientRepo.findAll();
}
}
You might want to visit the following links:
https://jaketrent.com/post/return-array-graphql/
https://stackoverflow.com/a/52773152/4195803

Related

Multiple queries in prisma graphql resolver

Following this example here:
https://github.com/prisma/prisma-examples/blob/latest/javascript/graphql-sdl-first/src/schema.js
Let's say I have a mutation where I want to update multiple users by passing their ids and emails accordingly. I know the updateMany would probably be the most suitable option, but since different users would have different values, not sure how to pass that without calling multiple resolvers separately. Something like this:
updateUsers: (_parent, args, context) => {
return context.prisma.user.updateMany({
where: {
id: { in: args.userIds },
},
data: {
email: ??? <--- use args.emails here
}
})
}
or should I just run multiple mutations:
updateUsers: async (_parent, args, context) => {
try {
args.emails.forEach(email => {
const user = await context.prisma.user.update({
where: {
id: { in: args.userIds },
},
data: {
email: args.email
}
})
return user;
}
} catch (error) {
console.log(error)
}
}
not sure if the last example would even work because of multiple return statements, since all the resolvers have a return statement, how can I run multiple queries/mutations?
updateMany allows you to bulk update all rows matching certain conditions with the same data. It does not help you in this case.
You could loop over an array of users to update id and email of each of them. Your approach will not work however, as you seem to have two arrays (a list of user IDs and a list of email addresses). However, instead of fixing your code I'd suggest to change the GraphQL interface you've defined.
Option 1) is close to what you did. You keep a bulk mutation updateUsers, but instead of two lists (a list of user IDs and a list of email addresses), it should accept a list of users, each of them having an user ID and an email address.
To improve performance you could use Promise.all and not wait for each update to happen, before starting the next one.
Option 2) is the preferable. Instead of a bulk mutation updateUsers, I would create a mutation updateUser that updates only one user. If client wants to update multiple users in the same request, they can! A single GraphQL request can contain multiple mutations.

How to insert many mutations in a single GraphQL request?

I using https://hygraph.com/, and I want insert (create many products) in a single GraphQL request.
At the moment I know how to insert one product:
mutation {
createProduct(data: { title: "Face Mask", slug: "dfavce-mask", price: 1000 }) {
id
}
}
I read the documentation, but I didn't see information about bulk creation records.
Link for hygraph documentation:
https://hygraph.com/docs/api-reference/content-api/mutations#create-entries
The top-level query you show is just a query against the Mutation type (or another type specified in the schema). Like any other query, it can have multiple fields. At a technical level, the only special thing about GraphQL mutations is that, if you do have multiple fields, they execute sequentially.
Also like other queries, if you want to request the same field multiple times (run similarly-named mutations) you need to use an alias to disambiguate the results.
mutation {
createFaceMask: createProduct(data: { title: "Face Mask" }) { id }
createHandSanitizer: createProduct(data: { title: "Hand Sanitizer" }) { id }
}

How to find out which mutation is being requested when configuring “Context”(Node.js, Apollo Server)

I would like to add conditional function on context depending on which mutation is being called in Node.js, Apollo Server.
How would I do that efficiently?
When configuring context, I have access to the request body and all the graphql request information is stored at “req.body.query”, which also contains what I need.
To make use of this, I would have to parse with the “parse” function from “graphql” module. But I dont think this is efficient, cuz now the same request is basically getting parsed twice(once by me and once by Apollo Server). Also the parsed result is not very user friendly with all the nested values.
Is there a clean way to know what mutation is being requested?
One common solution to this is the use of operation names. The client can provide an operation name to the server in the request to identify what it is doing.
{
"query": "{ mutation AddToBasket($myVariable: AddToBasketInput!) { addToBasket(input: $myVariable) { id } } }",
"operationName": "AddToBasket",
"variables": { "myVariable": "someValue" }
}
I think you can access the operation name from the body like this:
const server = new ApolloServer({
context: ({ req }) => {
if (req.body.operationName === 'AddToBasket') {
console.log("Found it!")
}
}
})
You can also create a plugin for Apollo Server that uses the didResolveOperation event to get the operation name value.
export class MyPlugin implements ApolloServerPlugin {
async requestDidStart() {
return {
async didResolveOperation(
requestContext
) {
console.log(requestContext.operationName);
},
}
}
}
Using operation name avoids a need to parse the GraphQL operation to determine which mutation is included in the request, but also requires the client to provide a known operation name value, which may or may not work for your use case.

Resolve to the same object from two incoherent sources in graphql

I have a problem I don't know how to solve properly.
I'm working on a project where we use a graphql server to communicate with different apis. These apis are old and very difficult to update so we decided to use graphql to simplify our communications.
For now, two apis allow me to get user data. I know it's not coherent but sadly I can't change anything to that and I need to use the two of them for different actions. So for the sake of simplicity, I would like to abstract this from my front app, so it only asks for user data, always on the same format, no matter from which api this data comes from.
With only one api, the resolver system of graphql helped a lot. But when I access user data from a second api, I find very difficult to always send back the same object to my front page. The two apis, even though they have mostly the same data, have a different response format. So in my resolvers, according to where the data is coming from, I should do one thing or another.
Example :
API A
type User {
id: string,
communication: Communication
}
type Communication {
mail: string,
}
API B
type User {
id: string,
mail: string,
}
I've heard a bit about apollo-federation but I can't put a graphql server in front of every api of our system, so I'm kind of lost on how I can achieve transparency for my front app when data are coming from two different sources.
If anyone has already encounter the same problem or have advice on something I can do, I'm all hear :)
You need to decide what "shape" of the User type makes sense for your client app, regardless of what's being returned by the REST APIs. For this example, let's say we go with:
type User {
id: String
mail: String
}
Additionally, for the sake of this example, let's assume we have a getUser field that returns a single user. Any arguments are irrelevant to the scenario, so I'm omitting them here.
type Query {
getUser: User
}
Assuming I don't know which API to query for the user, our resolver for getUser might look something like this:
async () => {
const [userFromA, userFromB] = await Promise.all([
fetchUserFromA(),
fetchUserFromB(),
])
// transform response
if (userFromA) {
const { id, communication: { mail } } = userFromA
return {
id,
mail,
}
}
// response from B is already in the correct "shape", so just return it
if (userFromB) {
return userFromB
}
}
Alternatively, we can utilize individual field resolvers to achieve the same effect. For example:
const resolvers = {
Query: {
getUser: async () => {
const [userFromA, userFromB] = await Promise.all([
fetchUserFromA(),
fetchUserFromB(),
])
return userFromA || userFromB
},
},
User: {
mail: (user) => {
if (user.communication) {
return user.communication.mail
}
return user.mail
}
},
}
Note that you don't have to match your schema to either response from your existing REST endpoints. For example, maybe you'd like to return a User like this:
type User {
id: String
details: UserDetails
}
type UserDetails {
email: String
}
In this case, you'd just transform the response from either API to fit your schema.

can some one explain this code to me

Good day im newbie here and im tackling graphql and im having some problem on mutation can someone explain this block of code for me thank you
RootMutation: {
createAuthor: (root, args) => { return Author.create(args); },
createPost: (root, { authorId, tags, title, text }) => {
return Author.findOne({ where: { id: authorId } }).then( (author) => {
console.log('found', author);
return author.createPost( { tags: tags.join(','), title, text });
});
},
},
Sure, this is an example of two mutations in a GraphQL server. We can break it down to understand what is going on.
First let's look at the type system. A GraphQL schema normally has two root fields query and mutation (and sometimes subscription). These root fields are the root of your data hierarchy and expose the queries (GET requests) and mutations (POST, PUT, DELETE, etc requests) that you have access to.
By the looks of it you are implementing a schema with a root mutation type that looks like this:
type Mutation {
createAuthor: Author
createPost: Post
}
A type in GraphQL is made up of a set of fields each of which can have an associated resolver. Resolvers in GraphQL are like the event handlers you would attach to endpoints in REST.
The code that you have above is defining two resolvers that will handle the logic associated with the createAuthor and createPost mutations. I.E. the code in the createPost resolver is what will be run when I issue a query like this:
mutation CreatePost($post: CreatePostInput!) {
createPost(input: $post) {
id
title
tags
text
}
}
The GraphQL runtime parses the query and routes the operation to the correct resolver depending on the content of the query. In this example, it would see that I am calling the createPost mutation and would make sure to call the createPost resolver which in your case looks like this:
createPost: (root, { authorId, tags, title, text }) => {
return Author.findOne({ where: { id: authorId } }).then( (author) => {
console.log('found', author);
return author.createPost( { tags: tags.join(','), title, text });
});
},
To understand how a resolver works, let's look at the GraphQLFieldResovler type definition from graphql-js
export type GraphQLFieldResolver<TSource, TContext> = (
source: TSource,
args: { [argName: string]: any },
context: TContext,
info: GraphQLResolveInfo
) => mixed;
As you can see a GraphQLFieldResolver is a function that takes 4 arguments.
source: The source is the parent object of the current field. For example if you were defining a resolver for a field fullName on the User type, the source would be the full user object.
args: The args are any input arguments for that resolver. In my query above it would contain the value of the $post variable.
context: Context is a global context for a GraphQL execution. This is useful for passing information around that a resolver might need. For example, you include a database connection that you can use from your resolvers without importing it in every file.
info: The info object contains information about your GraphQL schema, the query, and other information such as the path to the current resolver being executed. This is useful in many ways. Here is one post talking about how you can use it to precompute queries: (https://scaphold.io/community/blog/querying-relational-data-with-graphql/)
This idea of having types and field resolvers is part of what makes GraphQL so powerful. Once you've defined you type system and the resolvers for their fields you can structure your schema however you want and GraphQL will always make sure to call the correct resolver no matter how deeply nested a query might be.
I hope this helps :)

Resources