I have 2 graphql mutations : mutation1 and mutation2.
mutation2 uses mutation1 result (field id).
I'd like to create a seed and then run it in graphql playground.
My issue is that i can't use the id field in mutationResult in my other mutation.
so - this line is an error : id: mutationResult.id
Is there a way to do this?
I know i can create a js script for this, or import apollo client, but i'd like to use gql seed file instead.
AND YES - i'm aware of this issue : https://github.com/graphql/graphql-js/issues/462
mutation {
mutationResult: mutation1(
params: {
email: "test#gmail.com"
name: "test"
}
) {
id
}
mutation2(
params: {
id: mutationResult.id // THIS IS AN ERROR
}
)
}
Related
I have a nestJs project that is using Graphql and Jest.
The thing is, the graphql schema generates correctly when running the app but not when using jest.
For this schema :
input LoginRequest {
email: String!
code: String!
}
type LoginResponse {
jwtToken: String!
refreshToken: String!
}
The app starts but using the jest command it returns :
Cannot determine a GraphQL output type for the "email". Make sure your class is decorated with an appropriate decorator.
jest.config.js is pretty generic :
module.exports = {
preset: 'ts-jest',
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: '.',
testEnvironment: 'node',
testMatch: ['**/*.spec.[jt]s?(x)'],
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
testPathIgnorePatterns: [
'<rootDir>/build/',
'<rootDir>/dist',
'<rootDir>/node_modules',
],
}
I tried removing email in the schema so it looks like this :
input LoginRequest {
code: String!
}
type LoginResponse {
jwtToken: String!
refreshToken: String!
}
The app still starts with no issue but jest now returns :
Cannot determine a GraphQL output type for the "code". Make sure your class is decorated with an appropriate decorator.
And again for every fields until there's none left.
I found that I could use --runInBand or isolatedModules: true in the config but that does not fix the issue
Does Jest generate the graphql schema differently ? If so, where can I configure it correctly ?
I'm using a type GraphQL service that I have no control over. They have separate mutations which create/update/delete a single entity with something like
mutation DeleteThing($personId: ID!, $input: DeleteInput!) {
deleteThing(personId: $personId, input: $input) {
...ClientFragment
}
}
variables: { personId:'SomeId', input: { id: 'ThingId'}}
Now I may need to change 10-40 of this type of entity per request and I want to return only the final ClientFragment after every query has ran. What is the best way to go about this?
I know I can do something like:
mutation DeleteThing($personId: ID!, $input1: DeleteInput!, $input2:DeleteInput!) {
d1:deleteThing(personId: $personId, input: $input1) {
...ClientFragment
}
d2:deleteThing(personId: $personId, input: $input2) {
...ClientFragment
}
d3:...
}
variables: { personId:'SomeId', input1: { id: 'ThingId'}, input2: { id: 'OtherThingId'}}
But I may not care if one of these mutations throws a 404, since I just want them gone. I also would have to query after the mutation to get the final ClientFragment as these mutations may run asynchronously. Perhaps there is a more elegant solution?
I want to bulk update list of entries with graphQL mutation in faunaDB.
The input data is list of coronavirus cases from external source. It will be updated frequently. The mutation should update existing entries if the entry name is present in collectio and create new ones if not present.
Current GRAPHQL MUTATION
mutation UpdateList($data: ListInput!) {
updateList(id: "260351229231628818", data: $data) {
title
cities {
data {
name
infected
}
}
}
}
GRAPHQL VARIABLES
{
"data": {
"title": "COVID-19",
"cities": {
"create": [
{
"id": 22,
"name": "Warsaw",
"location": {
"create": {
"lat": 52.229832,
"lng": 21.011689
}
},
"deaths": 0,
"cured": 0,
"infected": 37,
"type": "ACTIVE",
"created_timestamp": 1583671445,
"last_modified_timestamp": 1584389018
}
]
}
}
}
SCHEMA
type cityEntry {
id: Int!
name: String!
deaths: Int!
cured: Int!
infected: Int!
type: String!
created_timestamp: Int!
last_modified_timestamp: Int!
location: LatLng!
list: List
}
type LatLng {
lat: Float!
lng: Float!
}
type List {
title: String!
cities: [cityEntry] #relation
}
type Query {
items: [cityEntry!]
allCities: [cityEntry!]
cityEntriesByDeathFlag(deaths: Int!): [cityEntry!]
cityEntriesByCuredFlag(cured: Int!): [cityEntry!]
allLists: [List!]
}
Everytime the mutation runs it creates new duplicates.
What is the best way to update the list within single mutation?
my apologies for the delay, I wasn't sure exactly what the missing information was hence why I commented first :).
The Schema
An example of a part of a schema that has arguments:
type Mutation {
register(email: String!, password: String!): Account! #resolver
login(email: String!, password: String!): String! #resolver
}
When such a schema is imported in FaunaDB there will be placeholder functions provided.
The UDF parameters
As you can see all the function does is Abort with the message that the function still has to be implemented. The implementation starts with a Lambda that takes arguments and those arguments have to match what you defined in the resolver.
Query(Lambda(['email', 'password'],
... function body ...
))
Using the arguments is done with Var, that means Var('email') or Var('password') in this case. For example, in my specific case we would use the email that was passed in to get an account by email and use the password to pass on to the Login function which will return a secret (the reason I do the select here is that the return value for a GraphQL resolver has to be a valid GraphQL result (e.g. plain JSON
Query(Lambda(['email', 'password'],
Select(
['secret'],
Login(Match(Index('accountsByEmail'), Var('email')), {
password: Var('password')
})
)
))
Calling the UDF resolver via GraphQL
Finally, how to pass parameters when calling it? That should be clear from the GraphQL playground as it will provide you with the docs and autocompletion. For example, this is what the auto-generated GraphQL docs tell me after my schema import:
Which means we can call it as follows:
mutation CallLogin {
login (
email: "<some email>"
password: "<some pword>"
)
}
Bulk updates
For bulk updates, you can also pass a list of values to the User Defined Function (UDF). Let's say we would want to group a number of accounts together in a specific team via the UI and therefore want to update multiple accounts at the same time.
The mutation in our Schema could look as follows (ID's in GraphQL are similar to Strings)
type Mutation { updateAccounts(accountRefs: [ID]): [ID]! #resolver }
We could then call the mutation by providing in the id's that we receive from FaunaDB (the string, not the Ref in case you are mixing FQL and GraphQL, if you only use GraphQL, don't worry about it).
mutation {
updateAccounts(accountRefs: ["265317328423485952", "265317336075993600"] )
}
Just like before, we will have to fill in the User Defined Function that was generated by FaunaDB. A skeleton function that just takes in the array and returns it would look like:
Query(Lambda(['arr'],
Var('arr')
))
Some people might have seen an easier syntax and would be tempted to use this:
Query(Lambda(arr => arr))
However, this currently does not work with GraphQL when passing in arrays, it's a known issue that will be fixed.
The next step is to actually loop over the array. FQL is not declarative and draws inspiration from functional languages which means you would do that just by using a 'map' or a 'foreach'
Query(Lambda(["accountArray"],
Map(Var("accountArray"),
Lambda("account", Var("account")))
))
We now loop over the list but don't do anything with it yet since we just return the account in the map's body. We will now update the account and just set a value 'teamName' on there. For that we need the Update function which takes a FaunaDB Reference. GraphQL sends us strings and not references so we need to transform these ID strings to a reference with Ref as follows:
Ref(Collection('Account'), Var("account"))
If we put it all together we can add an extra attribute to a list of accounts ids as follows:
Query(Lambda(["accountArray"],
Map(Var("accountArray"),
Lambda("account",
Do(
Update(
Ref(Collection('Account'), Var("account")),
{ data: { teamName: "Awesome live-coders" } }
),
Var("account")
)
)
)
))
At the end of the Map, we just return the ID of the account again with Var("account") in order to return something that is just plain JSON, else we would be returning FaunaDB Refs which are more than just JSON and will not be accepted by the GraphQL call.
Passing in more complex types.
Sometimes you want to pass in more complex types. Let's say we have a simple todo schema.
type Todo {
title: String!
completed: Boolean!
}
And we want to set the completed value of a list of todos with specific titles to true. We can see in the extended schema generated by FaunaDB that there is a TodoInput.
If you see that extended schema you might think, "Hey that's exactly what I need!" but you can't access it when you write your mutations since you do not have that part of the schema at creation time and therefore can't just write:
type Mutation { updateTodos(todos: [TodoInput]): Boolean! #resolver }
As it will return the following error.
However, we can just add it to the schema ourselves. Fauna will just accept that you already wrote it and not override it (make sure that you keep the required fields, else your generated 'createTodo' mutation won't work anymore).
type Todo {
title: String!
completed: Boolean!
}
input TodoInput {
title: String!
completed: Boolean!
}
type Mutation { updateTodos(todos: [TodoInput]): Boolean! #resolver }
Which means that I can now write:
mutation {
updateTodos(todos: [{title: "test", completed: true}])
}
and dive into the FQL function to do things with this input.
Or if you want to include the ID along with data you can define a new type.
input TodoUpdateInput {
id: ID!
title: String!
completed: Boolean!
}
type Mutation { updateTodos(todos: [TodoUpdateInput]): Boolean! #resolver }
Once you get the hang of it and want to learn more about FQL (that's a whole different topic) we are currently writing a series of articles along with code for which the first one appeared here: https://css-tricks.com/rethinking-twitter-as-a-serverless-app/ which is probably a good gentle introduction.
I am trying to use variables with GraphQL queries but the browser is responding with bad request.
here is my code
return this.apollo.query({
query: gql `query Project($projectId: String!){
project( where: { id: $projectId } )
{
id
initials
}
}`,
variables: { projectId: pid },
fetchPolicy: 'network-only'
})
for some reason its not picking the project variable in the query. when i replace the variables with hard-coded values it works fine.
can you please help me to understand why.
The issue with the code was with the data type of $projectId. this is an actual id in the database(i.e prisma in my case). so I changed the code to :
return this.apollo.query({
query: gql `query Project($projectId: ID!){
project( where: { id: $projectId } )
{
id
initials
}
}`,
variables: { projectId: pid },
fetchPolicy: 'network-only'
})
and its working now.
I found graphql playground very helpful and providing variables in the bottom of the screen. that way you can debug the code faster
I'm using GraphQL to try to create a record that has a relation to another type. The types are Task and Day
datamodel.graphql:
type Task {
id: ID! #unique
content: String!
completed: Boolean!
dateToDo: Day!
}
type Day {
id: ID! #unique
content: String!
tasks: [Task]
}
I want to create a task so that it has a reference of the date it should be completed (from the Day type)
schema.graphql
type Mutation {
createTask(content: String!, completed: Boolean!, dateToDo: ID! ): Task!
}
my mutation resolver looks like this:
const Mutations = {
async createTask(parent, args, ctx, info) {
const task = await ctx.db.mutation.createTask(
{
data: {
dateToDo: {
connect: {
id: args.dateToDo
}
},
...args
}
},
info
);
return task;
},
when I run this mutation to create the task:
mutation CREATE_ONE_TASK {
createTask(
content: "a new task",
completed: false,
dateToDo: "cjqycv9dtjklr09179y9zfntq")
{
id
}
}
I get this error:
"message": "Variable \"$_v0_data\" got invalid value
{\"dateToDo\":\"cjqycv9dtjklr09179y9zfntq\",\"content\":\"a new
task\",\"completed\":false}; Expected type
DayCreateOneWithoutTasksInput to be an object at value.dateToDo.",
My questions are: Am I using connect correctly in the mutation resolver? And what the heck is DayCreateOneWithoutTasksInput (I see its been automagically added in prisma.graphql) and how do I use it to create a Task that has a relation to a Day's ID?
The mutation to create the task has the following shape:
mutation b {
createTask(
data: {
content: "Task1"
completed: false
dateToDo: { connect: { id: "cjqzjvk6w000e0999a75mzwpx" } }
}
) {
id
}
}
The type DayCreateOneWithoutTasksInput Prisma is asking for is autogenerated and is the one expected for the field dataToDo. The name means that Prisma would accept a type that creates one Day node but does not have the field tasks or a type that specifies a connection. The WithoutTasksInput part states is there because the type can only be used nested in a mutation where you start from a task, Prisma therefore already has the value to fill in for the tasks field on the nested Day node and you do not need to specify it if you create the day instead of connecting an existing one.
If you use the Playground you can explore the schema that contains all the types on the right side.
schema explorer in the playground
Hope that helps!