1

It seems reasonable to expect one resolver to handle input for any combination of one or more of an object's values. I shouldn't have to write separate resolvers for 'title', 'published', 'author', etc., right?

Here's my example object:

let books = [
  {
    title: 'Clean Code',
    published: 2008,
    author: 'Robert Martin',
    id: 'afa5b6f4-344d-11e9-a414-719c6709cf8e',
    genres: ['refactoring'],
  },
  {
    title: 'Agile software development',
    published: 2002,
    author: 'Robert Martin',
    id: 'afa5b6f5-344d-11e9-a414-719c6709cf9e',
    genres: ['agile', 'patterns', 'design'],
  },
]

typeDefs:

const typeDefs = gql`
  type Book {
    title: String
    published: Int
    author: String
    id: ID
    genres: [String]
  }

  type Query {
    bookCount: Int!
    allBooks(title: String, author: String, genre: String): [Book]
    findBooks(title: String!): Book
  }

  type Mutation {
    addBook(
      title: String!
      published: Int
      author: String!
      genres: [String]
    ): Book
    editBook(
      id: ID
      title: String
      published: Int
      author: String
      genres: [String]
    ): Book
  }
`

Here's the resolver I currently have:

 Mutation: {
     editBook: (_, args) => {
      const book = books.find(b => b.id === args.id)
      if (!book) {
        return null
      }
      const updatedBook = {
        ...book,
        title: args.title,
        author: args.author,
        published: args.published,
        genres: [args.genres],
      }
      books = books.map(b => (
        b.id === args.id ? updatedBook : b))
      return updatedBook
    },
    }

Here's what is currently happening.

Original object:

"allBooks": [
      {
        "id": "afa5b6f4-344d-11e9-a414-719c6709cf8e",
        "title": "Clean Code",
        "author": "Robert Martin",
        "published": 2008,
        "genres": [
          "refactoring"
        ]
      },
{...}
]

Mutation query:

mutation {
  editBook(id:"afa5b6f4-344d-11e9-a414-719c6709cf8e", title:"changed"){
    id
    title
    author
    published
    genres
  }
}

Returns this:

{
  "data": {
    "editBook": {
      "id": "afa5b6f4-344d-11e9-a414-719c6709cf8e",
      "title": "changed",
      "author": null,
      "published": null,
      "genres": [
        null
      ]
    }
  }
}

How do I write the resolver to change one or more of an object's values, without changing the unspecified values to 'null'?

My javascript skills are, I'll admit, rather shaky, and I'm guessing the answer lies with a more eloquent map function, but since the code runs inside a graphql schema module, it doesn't handle console.log so troubleshooting is problematic. Any recommendations to address that would be extremely helpful as well, so I can troubleshoot my own problems better.

Dre
  • 31
  • 6
  • Not totally sure what you mean by "since the code runs inside a graphql schema module, it doesn't handle console.log" -- you can call `console.log` inside your resolver code and it will write to standard output. – Daniel Rearden Feb 19 '20 at 22:59
  • @DanielRearden I was just attempting to paraphrase - admittedly I may have misinterpreted something - the answer to [this question](https://stackoverflow.com/questions/52955585/console-log-resolver-data-in-graphql). I might be missing out on something in how I'm using Visual Studio Code. I never figured out how to run code repl-style within VSC, and I admit I rely way too heavily on `console.log` lol. But I tried again just now - sanity check - and I actually do see the `console.log` output ***if*** I run the front-end app and use `inspect` in chrome devtools. But still no standard output. – Dre Feb 20 '20 at 15:49
  • As that answer states, you can't log something on the server side and expect it to show up in your client code. Presumably, you're running your server by entering a command like `node server` or `npm start`. Wherever you enter that command is where the console output will be shown. – Daniel Rearden Feb 20 '20 at 23:04

1 Answers1

0

Couple of points

There's no need to use map or to reassign books. Because the book variable is a reference to the object in your books array, you can mutate it (but not reassign it) and you will mutate the object in the array. See this question for additional details.

const book = books.find(b => b.id === args.id)
if (book) {
  // you cannot reassign book and still have the original value change,
  // but you can mutate it
  book.title = args.title
  book.author = args.author
  book.published = args.published
  book.genres = args.genres
}
return book

To change only the values present in args, you have some options:

You can use the logical OR operator (||) to provide another value to assign if the first value is falsey. Here, book.title will only be used if args.title is undefined, null, 0, NaN, "", or false.

book.title = args.title || book.title

You can use Object.assign, which copies the properties from one object to another. Since args will be missing those properties that aren't provided, you can just do something like the below snippet. This is much more elegant and concise, but will only work if your argument names match your property names.

Object.assign(book, args)

You can loop through the args object. This might be more desirable if you have one or more arguments that don't match the properties of book:

Object.keys(args).forEach(argName => {
  const argValue = args[argName]
  book[argName] = argValue
})

Putting it all together, we might do:

const book = books.find(b => b.id === args.id)
if (book) {
  Object.assign(book, args)
}
return book
Daniel Rearden
  • 80,636
  • 11
  • 185
  • 183
  • Yes! Wonderful! Two of those options work perfectly, the first and last. I almost cannot believe that the `Object.assign` method works as is, it's so simple and elegant! However, just because I'm curious, I want to get the `Object.keys` method to work as well, so I can understand it better. I tried it in my code, but I don't know what to put in `[bookProperty]`... my args do indeed always match the book properties, so what belongs in there? I tried the actual property name, like `title` but I get syntax error. `args.title` also failed. – Dre Feb 20 '20 at 15:40