objection-graphql
objection-graphql copied to clipboard
How to retrieve DB context in a Mutation with `extendWithMutations`
Hi @timhuff ,
Regarding mutations, I am ditching my embedded code approach and going with your excellent extendWithMutations()
approach, but I have a couple of usage questions, please:
- What is the best way to setup and call
extendWithMutations()
for mutations on multiple models? I have multiple mutations per model, and obviously multiple models with this need. Should I do:
schema = mainModule
.builder()
.model(Person)
.extendWithMutations(mutationTypesPerson)
.model(Book)
.extendWithMutations(mutationTypesBook)
.build();
- How do I get access to the database context in the mutation code, without creating a new DB connection for each query? Obviously, I need to make database calls inside the mutation, but I'm too dumb to see how to do it :o
Do you perhaps have a simple example?
TIA, Adrian
I have tried:
import {
GraphQLObjectType,
GraphQLNonNull,
GraphQLInputType,
GraphQLInputObjectType,
GraphQLString,
GraphQLInt
} from 'graphql'
// import { CvU}
//...
export const CvUserType = new GraphQLObjectType({
name: 'CvUserType',
description: 'Use this object to create new cvUser',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLInt),
description: 'Record ID',
},
first_name: {
type: new GraphQLNonNull(GraphQLString),
description: 'First Name',
},
last_name: {
type: new GraphQLNonNull(GraphQLString),
description: 'Last Name',
},
}),
})
export const CreateCvUserInputType = new GraphQLInputObjectType({
name: 'CreateCvUserInputType',
description: 'CvUser',
fields: () => ({
first_name: {
type: new GraphQLNonNull(GraphQLString),
description: 'First Name',
},
last_name: {
type: new GraphQLNonNull(GraphQLString),
description: 'Last Name',
},
}),
})
export function createCvUser (builder) {
const theBuilder = builder
return new GraphQLObjectType({
name: 'RootMutationType',
description: 'Domain API actions',
fields: () => ({
createCvUser: {
description: 'Creates a new cvUser',
type: CvUserType,
args: {
input: { type: new GraphQLNonNull(CreateCvUserInputType )},
},
resolve: (root, inputCvUser) => {
return theBuilder.insertAndFetch(inputCvUser.input)
},
},
}),
})
}
but I get insertAndFetch
is not a function...
Please, even a slapped together answer will help!
I notice in the mutationType
in the example, that the first parameter for resolve: (root, inputPerson)
is coming through as undefined
, even though inputPerson
is fine, and accessible.
Is this possibly a bug?
Unsure if it is the same bug- but I haven't been able to get mutations working at all.
I get the error- Expected type CreatePersonType!, found \"test\".
My query looks like this:
mutation{
createPerson(input:"test") {
id
}
}
That query looks wrong to me. Any ideas on how it should look?
it probably needs to look more like this:
mutation { createPerson(input: { firstName: "test" }) }
input
expects to receive an object with match fields.
That will work, but even better, declare the personType
and createPersonInputType
, initialise them via variables, and pass the inputType object.
Thanks @Trellian that got it working.
Why is this example so verbose? Is there a way to write it simpler like the below example?
This is how I'd write it if I wasn't using objection-graphql
/schema.graphql
type Mutation {
CreatePerson(firstName: String!): String
}
/resolvers.js
Mutation: {
CreatePerson: async (_, { firstName }) => await PersonModel.query()
.allowInsert("[username]")
.insert({username})
},
@yarnball no problem :)
It can be done, as you say, without using an input
object, it's just considered better style to use one, especially in large projects, to help manage changes to the interface. With an input
object, you have one authoritative place to make changes to your interface, making maintenance easier and more reliable.
If you don't mind, would you please post your working code for reference? Other people will find it useful (especially me) :)
@Trellian Sure, for my code I create a wrapper so I can easily pass it a set of options.
Forgive the field names- they probably won't make total sense.
I'm very open to feedback!
Only issue I had with the below, is I cannot find a way to pass more than one mutation. I tried (without success):
mutationAction([mutation1, mutation2])
mutationAction(mutation1)
mutationAction(mutation2)
module.exports = mutationObj =>{
mutationObj = mutate[mutationObj]
return new GraphQLObjectType({
name: mutationObj.docs_mutationName,
description: mutationObj.docs_mutationDesc,
fields: () => ({
[mutationObj.mutationName]: {
description: mutationObj.docs_actionTitle,
type: new GraphQLObjectType({
name: mutationObj.mutationName,
description: mutationObj.docs_actionDescription,
fields: () => mutationObj.returnFields
}),
args: {
[mutationObj.inputFieldTitle]: {
type: new GraphQLNonNull(
new GraphQLInputObjectType({
name: mutationObj.argumentName,
description: mutationObj.argumentName,
fields: () => mutationObj.inputFields
})
)
}
},
resolve: mutationObj.resolver
}
})
})
}
So I pass it an object like this:
docs_mutationName:"userCreateType",
docs_mutationDesc: "Domain API actions",
docs_actionTitle: "Creates a new user",
mutationName: "userCreate",
docs_actionDescription:
"Use this object to create new person",
returnFields: {
id: {
description: "ID",
type: new GraphQLNonNull(GraphQLInt)
},
username: {
description: "Username",
type: new GraphQLNonNull(GraphQLString)
},
},
inputFieldTitle: "input",
argumentName: "UserCreateType",
inputFields: {
username: {
type: new GraphQLNonNull(GraphQLString),
description: "Username"
},
title: {
type: new GraphQLNonNull(GraphQLString),
description: "Title"
}
},
resolver: async (root, inputUser) => {
const { username, title } = inputUser.input
const user = await UserModel.query()
.allowInsert("[username, title]")
.insert({ username, title })
return {
id: user.id,
username: `Successfuly created ${username} with title ${title}`
}
}