1

I want to create a form that allows me to create a value from a nested field. Root values in the same mutation work ok, but when I want to create the nested value, I cannot. Take for instance the below mutation.

export const createPremise = (newPremiseEntry) => {
  const mutation = gql`
    mutation CreatePremise($input: PremiseInput!) {
      createPremise(data: $input) {
        _id
        _ts
        content
        createdAt
      }
    }
  `
  return graphQLClient.request(mutation, { input: newPremiseEntry })
}

In the GraphQL Playground, I can create the nested operation like so

mutation{
  createPremise(data:{
    content: " This is a premise"
    createdAt: "2020-09-23T17:01:00Z"
    belongs:{
      connect: "317324044732990025" //This is the _Id of the story it belongs too
    }
  })
  {
     // This is the return
    _id
    content
    belongs {
      name // This is the name of the Story, connected by the _id above.
    }
  }
}

Now, I can create a premise with the root value content without the nested value on my form like so:

const ENTRIES_PATH = '/api/entries/allPremises'

    const putPremiseEntry = (payload) =>
        fetch(ENTRIES_PATH, {
            method: 'POST',
            body: JSON.stringify(payload),
            headers: {
                'Content-Type': 'application/json',
            },
        }).then((res) => (res.ok ? res.json() : Promise.reject(res)))

Then I do this:

const useEntriesFlow = () => {
    const onSubmit = async (payload) => {
        await putPremiseEntry(payload)
        await mutate(ENTRIES_PATH)
    }
    return {
        onSubmit,
    }
}

const EntryPremiseForm = ({ onSubmit: onSubmitProp }, storyId) => {
    const [show, setShow] = useState(false);

    const handleClose = () => setShow(false);
    const handleShow = () => setShow(true);
    const initial = {
        content: '', // setting the initial state of content.
    }
    const [values, setValues] = useState(initial)
    const [formState, setFormState] = useState('initial')
    const isSubmitting = formState === 'submitting'

    const onSubmit = (ev) => {
        ev.preventDefault()
        setFormState('submitting')
        onSubmitProp(values)
            .then(() => {
                setValues(initial)
                setFormState('submitted')
            })
            .catch(() => {
                setFormState('failed')
            })
    }
    const makeOnChange =
        (fieldName) =>
            ({ target: { value } }) =>
                setValues({
                    ...values,
                    [fieldName]: value,
                })
    return (
        <>
                <form className="" onSubmit={onSubmit}>
               //this is my input for creating the premise with content
                        <input required 
                            className={cn(inputClasses, '')}
                            aria-label="premise"
                            placeholder="Premise"
                            value={values.content}
                            onChange={makeOnChange('content')} // On my form, I add the `content` value which goes into my premise object, creating the premise.
                        />
                        <Button type="submit" disabled={isSubmitting}>
                            {isSubmitting ? <LoadingSpinner /> : 'Create Premise'}
                        </Button>
                </form>
                {{
                    failed: () => <ErrorMessage>Something went wrong. :(</ErrorMessage>,
                    submitted: () => ( <SuccessMessage>Thanks for signing the guestbook.</SuccessMessage>
                    ),
                }[formState]?.()}
        </>
    )
}

This is all fine, It works.

THE PROBLEM

The problem comes when I need to do the same thing, but with an extra field that is nested. This type Premise belongs to another object called Story

type Story {
  name: String!
  createdAt: Time!
  premises: [Premise] @relation
}

type Premise {
  content: String!
  belongs: Story!
  createdAt: Time!
}

belongs is how faunadb creates the relation

The mutation looks like this:

export const createPremise = (newPremiseEntry) => {
  const mutation = gql`
    mutation CreatePremise($input: PremiseInput!) {
      createPremise(data: $input) {
        _id
        _ts
        content
        belongs{name}  //nested relationship, not sure if syntax is correct.
        createdAt
      }
    }
  `
  return graphQLClient.request(mutation, { input: newPremiseEntry })
}

The difference being:

belongs{name}

name is the name of the story it belongs to. found on the type Story

FIRST PROBLEM

How do I represent this in my POST?

export default async function handler(req, res) {
  const handlers = {

    POST: async () => {
      const {
        body: { 
        content, 
        belongs{name} // ?? WHAT DO I PUT HERE? HOW DO I REPRESENT BELONGS.NAME AS SEEN IN TYPE PREMISE?
        },
      } = req
      const createdPremise = await createPremise({
        content, belongs{name}, //???
        createdAt: new Date(),
      })
      res.json(createdPremise)
    },
  }

  if (!handlers[req.method]) {
    return res.status(405).end()
  }

  await handlers[req.method]()
}

SECOND PROBLEM

In my const EntryPremiseForm above how do I represent that belongs.name

const handleClose = () => setShow(false);
    const handleShow = () => setShow(true);
    const initial = {
        content: '',
        belongs.name: '', //??? How do I set the inital state for this nested relationship field. remember, content works fine
    }

When I have that, I can then do this

<input  
      required
      className={cn(inputClasses, '')}
      aria-label="premise"
      placeholder="Premise"
      value={values.belongs.name} // ??? HOW DO I REPRESENT THAT HERE
      onChange={makeOnChange('values.belongs.name')} //???
/>

Any help is greatly apreciated.

Shingai Munyuki
  • 551
  • 1
  • 11
  • 25
  • That `Premise` type has a `@relation` to a `Story` is irrelevant for your mutation. What does the `PremiseInput` look like? – Bergi Dec 10 '21 at 02:24
  • What is `putPremiseEntry`? That doesn't look like a GraphQL API. – Bergi Dec 10 '21 at 02:25
  • Its Faunadb Graphql, take a look here: https://docs.fauna.com/fauna/current/api/graphql/relationships#one2many – Shingai Munyuki Dec 10 '21 at 02:27
  • putPremiseEntry is just a variable – Shingai Munyuki Dec 10 '21 at 02:31
  • According to that documentation, you just exchange the input `belongs: { connect: "…" }` for `belongs: { create: { name, … } }`. Is that what you mean by "*I want to create the nested value*"? – Bergi Dec 10 '21 at 02:39
  • Yes, thank you! I'm struggling with correct syntax on my POST, and on the Graphql mutation query itself. let me try this – Shingai Munyuki Dec 10 '21 at 02:43
  • The mutation query shouldn't change at all. You still fetch the `content belongs { name }` of the created premise. Only the input value will be different. – Bergi Dec 10 '21 at 02:46
  • Apologies, probably a silly question but, Where do I put this? belongs: { create: { name, … } }? – Shingai Munyuki Dec 10 '21 at 02:51
  • In the same place where you previously used `connect`, in the `payload` of `onSubmit` or the `values` of the form. You haven't shown the code of the input where you previously chose the value to connect to. – Bergi Dec 10 '21 at 03:01

0 Answers0