Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to retrieve DB context in a Mutation with extendWithMutations #65

Open
Trellian opened this issue Oct 10, 2018 · 7 comments
Open

How to retrieve DB context in a Mutation with extendWithMutations #65

Trellian opened this issue Oct 10, 2018 · 7 comments

Comments

@Trellian
Copy link

Trellian commented Oct 10, 2018

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:

  1. 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();    
  1. 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

@Trellian
Copy link
Author

Trellian commented Oct 14, 2018

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!

@Trellian
Copy link
Author

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?

@yarnball
Copy link

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?

@Trellian
Copy link
Author

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.

@yarnball
Copy link

yarnball commented Dec 29, 2018

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})
	},

@Trellian
Copy link
Author

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

@yarnball
Copy link

yarnball commented Jan 9, 2019

@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}`
		}
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants