GraphQL-js mutation optional arguments - javascript

how would I achieve a GraphQL Mutation in nodeJS that has arguments which are optional?
My current mutation has an args field, but all the arguments are mandatory. Since I couldn't find anything in the documentation, I don't know how or if this is possible.
This is my current code:
const fakeDB = {
count: 0
};
const schema = new GraphQLSchema({
query: //...
mutation: new GraphQLObjectType({
name: 'adsF',
fields: {
updateCount: {
type: GraphQLInt,
args: {
count: { type: GraphQLInt } // I want to make this argument optional
},
resolve: (value, { count }) => {
// Catch if count is null or undefined
if (count == null) {
// If so just update with default
fakeDB.count = 5;
} else {
fakeDB.count = count
}
return fakeDB.count;
})
}
}
})
});
Thanks for Your help!

Types in GraphQL are nullable by default. That means that the way you have specified the mutation at the moment makes the count optional. If you wanted a field to be mandatory you need to mark it as non null

Related

How to add a field to a GraphQL document?

Let's say this is my graphql query:
mutation Test ($input: UpdateUserAccountInput!) {
updateUserAccount(input: $input) {
... on UpdateUserAccountPayload {
userAccount {
name
}
}
}
}
I want to modify to have the following fragment:
... on Error {
message
}
I was able to figure out that I can get AST using parse from graphql package, i.e.
import {
parse,
gql,
} from 'graphql';
parse(gql`
mutation Test ($input: UpdateUserAccountInput!) {
updateUserAccount(input: $input) {
... on UpdateUserAccountPayload {
userAccount {
name
}
}
}
}
`)
Now I am trying to figure out how to add ... on Error { message } to this query.
The problem that I am trying to solve is that my tests sometimes quietly fail because mutation returns an error that I did not capturing. I am extending my GraphQL test client to automatically request errors for every mutation and throw if error is returned.
I assume there exists some utilities that allow me to inject fields into AST, but so far I was not able to find them.
I think I figured it out.
Here is my solution:
const appendErrorFieldToMutation = (query: string) => {
const inlineErrorFragmentTemplate = {
kind: 'InlineFragment',
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {
kind: 'Name',
value: 'message',
},
},
],
},
typeCondition: {
kind: 'NamedType',
name: {
kind: 'Name',
value: 'Error',
},
},
};
const document = parseGraphQL(query);
if (document.definitions.length !== 1) {
throw new Error('Expected only one definition');
}
const definition = document.definitions[0] as OperationDefinitionNode;
if (definition.operation !== 'mutation') {
return query;
}
if (definition.selectionSet.selections.length !== 1) {
throw new Error('Expected only one document selection');
}
const documentSelection = definition.selectionSet.selections[0] as InlineFragmentNode;
const errorField = documentSelection.selectionSet.selections.find((selection) => {
return (selection as InlineFragmentNode).typeCondition?.name.value === 'Error';
});
if (!errorField) {
// #ts-expect-error – Intentionally mutating the AST.
documentSelection.selectionSet.selections.unshift(inlineErrorFragmentTemplate);
}
return printGraphQL(document);
};
I've not used any utilities, so perhaps there is a smarter way to do the same.

Change Nested State Value With A Single Function? (javascript/React)

I have some react user privilege state data I need to manage. I would like the ability to change the object privileges based on their property through a dynamic function. I'm not sure how to target the specific nested privilege property to change the value. Is this possible?
Question: How can I change the value of a nested privilege property to the functions type and value parameter?
Heres an Example:
const [userPrivilages, setUserPrivilages] = useState([{
_id: "123"
privilages: {
edit: true, //before!
share: true,
del: false
}
},
{
...more users
}
])
//my attempt
const changePrivilage = (type, value) => {
const newPrivilages = userPrivilages.map(user => {
return {
...user,
privilages: {
...privilages,
//change the privilage of "type" from the functions parameter to the value parameter
}
}) setUserPrivilages(newPrivilages)
}
changePrivilage("edit", false)
Desired output:
const [userPrivilages, setUserPrivilages] = useState([{
_id: "123"
privilages: {
edit: false, //After!
share: true,
del: false
}
},
{
...more users
}
])
Thanks!
You can use [] to refer to variable as a key like below:
const changePrivilage = (type, value) => {
const newPrivilages = userPrivilages.map(user => {
return {
...user,
privilages: {
...user.privilages,
[type]: value // here it is !!!
}
}) setUserPrivilages(newPrivilages)
}
Try this :
(see comments for understanding code)
const changePrivilage = (type,value) => {
const newUserPrivilages = userPrivilages.map(user => {
let newPrivilages = user.privilages; // get old privilages of user
newPrivilages[type] = value; // update type with new value
return {
...user,
privilages: newPrivilages, // set privilages as newPrivilages
};
});
setUserPrivilages(newUserPrivilages);
};
Note : this will change properties for all users. If you want to update only for specific user, pass _id as well to changePrivilage and execute newPrivilages[type] = value; // update type with new value inside if condition comparing user _id.

Updating a shadow field, via updateOne pre hook, in mongoose?

Can anyone explain how to use the updateOne pre-hook, in mongoose (5.9.5)?
I am need to create a normalised 'shadow field' (not sure the right term) to help with certain searches. While I am able to update the shadow field during a save, I am having trouble during update.
The save pre-hook:
personSchema.pre('save', function (next) {
if (this.isModified('name')) {
const name = this.name;
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.shadowName = shadowName.toLowerCase();
} else {;
this.shadowName = name;
}
}
// do stuff
next();
});
Doing the equivalent for updateOne does not seem to work (shadowName stays with the value it was given during the initial save):
personSchema.pre('updateOne', function (next) {
const name = this.name;
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.update({}, { shadowName: shadowName.toLowerCase() });
} else {
this.shadowName = name;
}
// do stuff
next();
});
The schema:
const personSchema = new mongoose.Schema({
resourceId: {
type: String,
required: true,
unique: true,
index: true,
uppercase: true
},
name:{
type: String,
required:true,
index: true
},
// can be used for searches, but don't update directly
shadowName: {
type: String,
index: true
},
});
BTW I can confirm the hook is called, but the field is not updated.
Turns out you can't access the field values directly and instead need to leverage the get() and set() methods on the query.
Changing the pre-updateOne hook to be the following works:
personSchema.pre('updateOne', function (next) {
const name = this.get('name');
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.set('shadowName', shadowName.toLowerCase());
} else {
this.set('shadowName', name);
}
// do stuff
next();
});

How can I update a property inside an object in Mongoose

i'm trying to update a property inside an object using $inc in mongoose. I've tried several ways but apparently the syntax is not valid.
this is the relevant part of the Schema:
stats: {
type: {
totalMatches: {
type: Number,
default: 0
},
totalWins: {
type: Number,
default: 0
},
totalRebuys: {
type: Number,
default: 0
},
totalTimesHosted: {
type: Number,
default: 0
},
averagePosition: {
type: Number,
default: 0
},
gainLossRatio: {
type: Number,
default: 0
},
totalFinishesForEachPosition: {
type: [Number],
default: [0]
}
}
}
});
const UserModel = mongoose.model("User", userSchema);
This is the part for the update, the syntax error is inside the $inc block:
UserModel.savePokerResultToPlayerData = (game) => {
_.each(game.results, (playerResult, index) => {
let resultToSave = {
matchId: game._id,
date: game.date,
ranking: index + 1,
prizeMoney: playerResult.prizeMoney,
rebuys: playerResult.rebuys,
isHostPlayer: game.host === playerResult._id
};
const statsObject = prepareStatsDataToBeUpdated(resultToSave);
UserModel.findByIdAndUpdate(
playerResult._id,
{
$push: { results: resultToSave },
$inc: {
stats.totalMatches: 1,
stats.totalWins: statsObject.totalWins,
stats.totalRebuys: statsObject.totalRebuys,
stats.totalTimesHosted: statsObject.totalTimesHosted
}
}
)
.exec()
.then()
.catch(err => {
console.log('error: ' + err);
});
});
};
prepareStatsDataToBeUpdated = (resultToSave) => {
return {
totalWins: resultToSave.ranking === 1 ? 1 : 0,
totalRebuys: resultToSave.rebuys,
totalTimesHosted: resultToSave.isHostPlayer ? 1 : 0
};
};
I've looked at a few similar questions here and tried the solution but all of them got me a syntax error.
I know i can find the related user, work on it and save it but i believe it loses the purpose of $inc and $push.
Probably you got syntax error about javascript.
It's forbidden to write invalid variable name on the left side of the ":" when you define object, here you use dot notation in place where have to be valid variable name or string:
stats.totalMatches: 1
please use quotes:
"stats.totalMatches": 1

GraphQL Args error: argument type must be Input Type but got: function GraphQLObjectType(config) {

On server start (node index.js) I am getting the following error with my GraphQL NodeJS server:
Error: Query.payment(data:) argument type must be Input Type but got:
function GraphQLObjectType(config) {
_classCallCheck(this, GraphQLObjectType);
This error happened when I changed my original args from a string
args: {
data: { type: graphQL.GraphQLString }
},
To an object type:
args: {
data: { type: graphQL.GraphQLObjectType }
},
I need an object type as I need to send several fields as params.
GraphQL Server:
var Query = new graphQL.GraphQLObjectType({
name: 'Query',
fields: {
payment: {
type: graphQL.GraphQLString,
args: {
data: { type: graphQL.GraphQLObjectType }
},
resolve: function (_, args) {
// There will be more data here,
// but ultimately I want to return a string
return 'success!';
}
}
}
});
How can I allow it to accept an object?
Frontend (if needed. But the error is happening before I even send this):
var userQuery = encodeURIComponent('{ payment ( data: { user : "test" } )}');
$.get('http://localhost:4000/graphql?query=' + userQuery, function (res) {
//stuff
});
If you want use Object as an argument, you should use GraphQLInputObjectType instead of GraphQLObjectType. And keep in mind that GraphQL is strongly type based, so you're not allowed to use a generic GraphQLObjectType as arg type and then dynamically query args. You have to explicitly define all possible fields in this input object (and choose which of them would be mandatory and which not)
Try use this approach:
// your arg input object
var inputType = new GraphQLInputObjectType({
name: 'paymentInput',
fields: {
user: {
type: new GraphQLNonNull(GraphQLString)
},
order: {
type: GraphQLString
},
...another fields
}
});
var Query = new graphQL.GraphQLObjectType({
name: 'Query',
fields: {
payment: {
type: graphQL.GraphQLString,
args: {
data: { type: new GraphQLNonNull(inputType) }
},
resolve: function (_, args) {
// There will be more data here,
// but ultimately I want to return a string
return 'success!';
}
}
}
});

Categories

Resources