Graphql multiple mutations - javascript

I have 3 mutations in a query. 6 input parameters. If (profile_status === true), then send the mutation. And so for each mutation. How to do it?
mutation updateProfile(
$input: UpdateProfileMutationInput!
$input2: UpdateUserEmailMutationInput!
$input3: UpdateUserPasswordMutationInput!
$profile_status: Boolean!
$email_status: Boolean!
$password_status: Boolean!
) {
#include(if: $profile_status) updateProfile(input: $input) {
...CoreUser
}
#include(if: $email_status) updateEmail(input: $input2) {
...CoreUpdateUserEmail
}
#include(if: $password_status) updatePassword(input: $input3) {
...CoreUpdateUserPassword
}
}
I use #apollo/client.
Include only works for fields. Is there a similar one for mutation?

From the specification:
The #skip directive may be provided for fields, fragment spreads, and inline fragments...
Implementation:
directive #skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
As you can see, it doesn't have MUTATION location - it's not implemented for mutations. If you are the developer of the server, you can always create your own schema directive (guide for Apollo Server).

Related

How to resolve validation error when querying with graphql? (typescript)

I am trying to query by passing in the name field but I get two different errors.
"Validation error of type MissingFieldArgument:
Missing field argument id # 'getBlog'"
"Validation error of type UnknownArgument:
Unknown field argument name # 'getBlog'"
I was able to successfully query it with the id field. Im new to graphql and im using this on my app that also uses aws amplify and aws appsync.
schema.graphql
type Blog #model {
id: ID!
name: String!
posts: [Post] #connection(keyName: "byBlog", fields: ["id"])
}
queries.ts
// this is an auto generated file. This will be overwritten
export const getBlog = /* GraphQL */ `
query GetBlog($name: String!) { //changed from ($id: ID!)
getBlog(name: $name) { //changed from (id: $id)
id
name
posts {
items {
id
title
blogID
createdAt
updatedAt
}
nextToken
}
createdAt
updatedAt
}
}
`;
app.tsx
const getRecord = async () => {
const result = await API.graphql(graphqlOperation(getBlog, {name: "testing"}))
console.log(result)
}
I also tried pushing it to aws amplify push but it detected no changes. I didnt expect it to as i didnt change anything in the schema, only the queries. thanks in advance
If you look at your GraphQL schema you should see the definition for the GetBlog query. It likely looks like:
query {
GetBlog(id: ID!): Blog
}
That particular query can only be queried by id. Assuming you have control of the server you should also be able to define a GetBlogByName query:
query {
GetBlogByName(name: String): Blog
}
But then it will be up to you to ensure that name is unique or deal with potentially >1 response.

Is using GraphQL input for every mutation a problem?

I am developing an application that has a quite sizeable amount of Queries and Mutation. Structures for data are often not complex, but there is plenty of them, so I have made myself a snippet, that generates the most common things repeating throughout them. This snippet also generates an input for mutations so it can be used for both simple and complex data structures. In quite a bit of instances, the input is just for adding a name. The API is supposed to be used mainly by my fronted, but after the app gets mature enough should be publicly available. Is doing this a problem in terms on conventions?
Sample of what I mean
/*=============================================
Types
=============================================*/
interface AddSampleSchemaInput {
input: AddSampleSchema
}
interface AddSampleSchema {
name: string
}
/*=============================================
Main
=============================================*/
export const SampleSchemaModule = {
typeDefs: gql`
type Mutation {
addSampleSchema(input: AddSampleSchemaInput): SampleSchema!
}
type SampleSchema {
_id: ID!
name: String!
}
input AddSampleSchemaInput {
name: String!
}
`
,
resolvers: {
Mutation: {
addSampleSchema: async (parents: any, args: AddSampleSchemaInput, context: GraphqlContext) => {
}
}
}
}
Sample of what I assume it should be.
/*=============================================
Main
=============================================*/
export const SampleSchemaModule = {
typeDefs: gql`
type Mutation {
addSampleSchema(name: String): SampleSchema!
}
type SampleSchema {
_id: ID!
name: String!
}
`
,
resolvers: {
Mutation: {
addSampleSchema: async (parents: any, args: { name: string }, context: GraphqlContext) => {
}
}
}
}
export default SampleSchemaModule
Would usage of the first code example be a problem. This means using input (input AddSampleSchemaInput), even if it were to contain just a single value (in this case name).
Or in other words is using input for every mutation a problem no matter the complexity.
Or the impact on frontent:
addDogBreed({
variables: {
input: {
name: "Retriever",
avergeHeight: 0.65
}
}
})
addDog({
variables: {
input: {
name: "Charlie"
}
}
})
// ======= VS =======
addDogBreed({
variables: {
input: {
name: "Retriever",
avergeHeight: 0.65
}
}
})
addDog({
variables: {
name: "Charlie"
}
})
In this case, is having the first one instead of the second one a problem?
Is having an input that only contains one key is something problematic?
No, on the contrary, it is something desirable in GraphQL. While nesting may sometimes seem superfluous, it is key in forward compatibility and extensibility of your schema. You should not have different conventions of how to design your mutation arguments depending on the number of inputs. If you always use an input object, you can easily deprecate existing fields or add new optional fields and stay compatible with all existing clients. If you were to completely change the shape of the mutation arguments just because you have an object with a single key, it would break compatibility.
I'm not seeing a problem that would drive you to
"only use GraphQL when dealing with Fetching / Get Data, and normal
REST API Request for mutating data (create, update, delete)."
Like #Bergi said. Plus you can provide your entity with multiple mutators some which can work like a PATCH or a PUT request.

Firebase Function With Converter Wipes Document Reference on Set()

I have a scheduled function that resets an integer value back to zero in my firestore. The problem that I'm running into is that, while the merge-set succeeds (for the specified properties), it somehow resets my Organization document reference to null.
So far I've tried the following
Not using a converter along with the Update() function (instead of Set()). While this works, it is untyped, and I have to get rid of the converter which encapsulates the moment() to Date conversion.
Using Set() and simply pass the entire object.
user.reference?.withConverter(userConverter).set(user)
This is also working but it overrides the entire user object and can lead to concurrency issues in case a user also updates his object while the timed function is running.
I'm looking for a solution that allows me to use the converter class along with a merge Set().
The User interface looks like this
export interface User extends Document {
email?: string
name?: string
organization?: Organization | null
numberOfForwards?: number
lastForwardReset?: moment.Moment
}
with its converter like so
export class UserConverter implements firestore.FirestoreDataConverter<User> {
toFirestore(user: User): firestore.DocumentData {
return {
email: user.email,
name: user.name,
organization: user.organization ? user.organization.reference : null,
number_of_forwards: user.numberOfForwards,
last_forward_reset: user.lastForwardReset?.toDate()
}
}
fromFirestore(snapshot: firestore.QueryDocumentSnapshot): User {
const data = snapshot.data()!
return {
reference: snapshot.ref,
email: data.email,
name: data.name,
organization: data.organization ? { reference: data.organization } : null,
numberOfForwards: data.number_of_forwards,
lastForwardReset: moment(data.last_forward_reset.toDate())
}
}
}
export const resetNumberOfForwards = functions.pubsub
.schedule('every 15 minutes')
.onRun(async () => {
const reset = (user: User) => {
console.log(`Resetting ${user.email} from [${user.numberOfForwards}] to [0]`)
// Claim user reference
user.reference
?.withConverter(userConverter)
.set({ numberOfForwards: 0, lastForwardReset: Moment() }, { merge: true })
}
for the partial set to work, I've included the following snippet on top of my file
firebase.firestore().settings({
ignoreUndefinedProperties: true
})
I think there are two issues going on here. For a partial set() you should use the merge option or else it will overwrite the document.
ref.set(data, {merge: true})
In addition, in your toFirestore method, either set the organization field as undefined and let the ignoreUndefinedProperties: true setting remove it, or don't include it at all if organization was not given. Something like this
toFirestore((numberOfForwards, lastForwardReset, ...user): User): firestore.DocumentData {
if (user.organization) {
user.organization = user.organization.reference;
}
return {
...user,
number_of_forwards: numberOfForwards,
last_forward_reset: lastForwardReset?.toDate()
}
}
I took out the numberOfForwards and lastForwardReset fields from the user object here and use the spread operator to copy over the remaining fields to the return value, but you could also save a temporary object, modify it, and return that.
PS: I know this is old, but it came up in my search so thought I might add an answer still.

Conditional typing for redux reducer

I am trying to create a re-usable component that renders a 'select' form item and its associated options. It accepts redux prop that is an action creator that is responsible for passing the selected option into the redux store to be used throughout the application.
I have two action creators that are possible options:
The first is :
setCompany: (state, action: PayloadAction<string>) => {
state.selectedCompany = action.payload;
},
The second is:
setStatus: (state, action: PayloadAction<SelectStatus['status']>) => {
state.status = action.payload;
},
The JobState['status'] type is: status: 'success' | 'error' | 'default' | 'processing' | 'warning';
In the Select component, I attempt to do the following:
interface SelectsProps {
fetchOptionData?: () => void;
optionsArray?: [];
placeholder: string;
showSearch: boolean;
badges: boolean;
redux?: ActionCreatorWithPayload<string | SelectStatus['status']>;
// I also tried ActionCreatorWithPayload<string> | //ActionCreatorWithPayload<SelectStatus['status']> but neither is working
}
In my parent component, i try to pass in a redux action of setStatus, defined above. However, I receive an error of :
`Type 'ActionCreatorWithPayload<"success" | "error" | "default" | "processing" | "warning", string>' is not assignable to type 'ActionCreatorWithPayload<string, string>'.`
<Selects
redux={setStatus}
placeholder='Default'
showSearch={false}
badges={true}
optionsArray={badges}
/>
};
If I do it as a union, it seems to default to string and not accept a status to be passed if valid. Is there a way I can allow the correct payload type to be selected?
Thank you
Solution
You need a union of the two action creator types rather than an action creator of the union.
redux?: ActionCreatorWithPayload<string> | ActionCreatorWithPayload<SelectStatus["status"]>;
Explanation
ActionCreatorWithPayload<string | SelectStatus['status']> means an action creator that can be called with an argument of either string OR SelectStatus['status']. setCompany is ok because SelectStatus['status'] is a subset of string. But setStatus is an error because it can only accept SelectStatus['status']. It cannot accept the union.
You need the union of the two action creators which is ActionCreatorWithPayload<string> | ActionCreatorWithPayload<SelectStatus["status"]>. This means that you can have a function that accepts string or a function that accepts SelectStatus['status']. It no longer needs to accept the union of the two.
You may have problems when you call this union since you don't know which argument type it accepts.
Note: I don't know why you use SelectStatus["status"] is one place and JobState["status"] in the other. I am assuming that they are the same. I would extract that type to a named type:
export type JobStatus = JobState["status"]

Apollo Server, Graphql - Must provide query string

Im not sure what im doing wrong here? I've been stuck now for soem time on getting my mutations to run with my apollo-server-lambda in my serverless setup, my queries works fine bu when i try to run a query like this:
{ "mutation": "{ signIn(username: \"SomeUser\", password: \"SomePassword\" ) { token } }" }
I just get the message: " Must provide query string." status 400.
I've set up my resolver like so:
const resolvers = {
Query: {
users: async (_, args, ctx) => User.load(args, ctx)
},
Mutation: {
signIn: async (_, { username, password }, ctx) => Auth.signIn({ username, password }, ctx)
}
};
For additional infor here is my typeDefs:
const typeDefs = gql`
type User {
id: ID!,
firstname: String,
lastname: String,
username: String,
createdAt: String,
role: String
}
type AuthToken {
token: String
}
type Query {
hello: String,
users(id: Int): [User]
}
type Mutation {
signIn(username: String!, password: String!): AuthToken!
}
`;
I'm using postman to test my graphql endpoint and my content type is application/json
I dont know if any one here can tell me what im doing wrong, i tryed to move it all to Query resolver, and it works replace "mutation" with "query" then but it dosent make sens to me using the "query" here and i guess later on when i actually want to use the Mutation to mutate data i would need this to work anyway?
Can any one tell me where im wrong here?
EDIT
I installed: graphql-playground-middleware-lambda and set up the serverless setup with: https://github.com/prisma/graphql-playground#as-serverless-handler and if i use Graphiql it works as intented, but im still interested if any one knows whats wrong with the json i send via postman?
When sending the request, your request body should be a properly-formatted JSON object, with a query property (and optionally, a variables property if including variables):
{
"query": "<GraphQL Document>",
"variables {},
}
This is the case whether the operation itself is a query or a mutation.
The actual value of the query property above must be a syntactically correct document, as outlined in the GraphQL specification. A document will typically consist of a single operation definition (either a query or a mutation) that includes all the requested fields for that operation. The document will also include fragments, if using any.
An operation definition looks like this:
OperationType [Name] [VariableDefinitions] [Directives] SelectionSet
So you could have a document like this:
mutation SomeMutation {
signIn(username: "SomeUser", password: "SomePassword") {
token
}
}
Here, the type of the operation is mutation, the name is SomeMutation and everything between the outermost set of curly brackets is the selection set. If you had any variables, their types would be declared in parentheses before the selection set.
The operation name is optional, but it's helpful to include it for debugging purposes on the backend. Technically, the operation type can be omitted as well, in which case GraphQL simply assumes the type is a query. For example, this is still a valid document:
{
users {
id
}
}
and is equivalent to
query SomeName {
users {
id
}
}
The former is referred to as query shorthand. Obviously, this cannot be used for mutations, so mutations must always explicitly state their operation type. A complete example:
{
"query": "mutation SomeName ($username: String!, $password: String!) { signIn(username: $username, password: $password) { token } }",
"variables {
"username": "SomeUser",
"password": "SomePassword"
},
}

Categories