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

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.

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.

How to collect and return warnings from services while processing GraphQL query?

What is the best way to collect some specific property from all the leafs of the GraphQL graph, reducing it to some single array? For example, my service functions can "throw" some arbitrary string warnings which I want to collect and supply to the client besides the main data, e.g. expected output:
type EntityOutput {
entity: Entity
warnings: [String!]
}
Resolver:
#Mutation()
async updateEntity(
#Args('id', ParseUUIDPipe) id: string,
#Args('data') input: UpdateDto
): Promise<EntityOutputDto>
{
return {
entity: await this.service.update(id, input),
warnings: [] // ???
};
}
Service method:
async update(id: string, input: UpdateDto): Promise<Entity> {
const entity = await this.repository.findOneOrFail(id, { relations: ['type'] }); // check existence
if (Object.values(input).some(v => v !== undefined)) {
const updateData: Partial<Entity & UpdateDto> = Object.assign({ id }, input);
if (input.isCurrentEntityOfItsType === true) {
await this.typesService.update(entity.type.id, { currentEntityId: id }); // <-- this also can create its own warnings
} else if (input.isCurrentEntityOfItsType === false) {
await this.typesService.update(entity.type.id, { currentEntityId: null as any });
}
await this.repository.save(updateData);
} else {
console.warn(`No properties to change were been provided`); // <-- this is a warning I want to save
}
return this.findOne(id);
}
I think my question can be splitted into 2:
To collect warnings from the service, i.e., in general case, the function calls stack of arbitrary depth. It actually looks more like a general programming problem than a NestJS thing
But even when one implement the feature from the first paragraph the NestJS will walk along the GraphQL graph by itself and there can be additional logs in nested fields.
The solution in its complete general form probably will be over-complicated but at least can anyone suggest the good design for the case represented by the example code?
I have a couple of thoughts:
Should every function in the service return its warnings alongside its main response (for example, in a tuple) so we can incrementally "fold" the array of warnings while "unfolding" the calls stack?
Maybe it would be better to implement using some decorator by which we will mark our service methods?
Maybe RxJS – the NestJS beloved one – can offer us some solution? (I don't know a lot about this library/their philosophy)
Actually the default form of the NestJS output is already looking similar to what I want, it's a JSON with 2 root properties: "errors" and "data". And they can be automatically sent to you simultaneously if the error happened is not so fatal to proceed. Can we somehow overwrite the default response object schema and place warnings there?
The whole question is heavily inspired by this SO discussion but it unfortunately says nothing about the actual possible implementation.
So I've implemented a custom context factory which is executed automatically on every GraphQL request and constructs the object of desired format:
app.module.ts:
export interface AppContext {
warnings: string[];
}
const contextFactory: ContextFunction<any, AppContext> = () => ({
warnings: []
});
Now we can benefit from our newly created interface to add strong typings whenever we reference the context, e.g.:
some.resolver.ts
#Mutation()
async remove(
#Args('id', ParseUUIDPipe) id: string,
#Context() ctx: AppContext
): Promise<FindOneDto>
{
return new FindOneDto(await this.service.remove(id, ctx.warnings));
}
Here the service can add its own warnings to the context.
To collect all of them and return to the API caller I override formatResponse function and append the warnings to the extensions (this is a special GraphQL meta-field serving the developing purposes):
app.module.ts:
const graphqlConfig: GqlModuleOptions = {
context: contextFactory,
formatResponse: (
response: GraphQLResponse | null,
context: GraphQLRequestContext<AppContext>,
): GraphQLResponse =>
{
const warnings = context.context.warnings;
if (warnings.length) {
if (response) {
const extensions = response.extensions || (response.extensions = {});
extensions.warnings = warnings;
} else {
return { extensions: { warnings } };
}
}
return response || {};
},
...
}
Similar approach is used in the official Apollo extension example: https://github.com/apollographql/apollo-server/blob/main/packages/apollo-tracing/src/index.ts.
The only drawback I see now is that injecting the context in resolver's arguments breaks the compliance with auto-generated TypeScript interfaces (I use schema-first approach). In such case, we can switch to per-request-based mode so our resolver/service class instance will be created individually for each and every new request: https://docs.nestjs.com/fundamentals/injection-scopes. Now we can access a context right in the methods without introducing any additional parameters. But this comes with increased latencies and, perhaps, memory-consumption. Another approach will be to create a standalone Nest interceptor.

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 :)

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

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

Resources