Search code examples
node.jstypescriptmongodbmongoosegraphql

Why does GraphQL Mutation fail with 400 error?


My mutation takes a useState variable as a parameter and passes it to the Mongoose API to save on MongoDB. I've been trying to get this API to work, but for some reason, I always get a POST http://localhost:4000/graphql 400 (Bad Request) error.

I'm trying to console.log the mutation input variable server-side but the code never reaches the resolver.

Here's my code:

frontend/new-post/page.tsx

const ADD_POST = gql`
  mutation CreatePost($post: PostInput!) {
    createPost(post: $post) {
      post
    }
  }
`;

export default function page() {
  const [createPost, {data, loading, error}] = useMutation(ADD_POST)
  const [post, setPost] = useState<IPost>({
    contactInfo: {
      phone: "",
      email: "",
      sns: {
        type: "Twitter",
        username: ""
      }
    },
    title: "",
    description: "",
})

  const handleSubmit = async () => {
      //Send "/api/new-post" to process post variable
      const { data } = await axios.post("/api/new-post", { post: post });
      console.log("NewPost ", data)
      console.log("NewPost ", data.post)
      // Works up until here
      await createPost({variables: {post: data.post} })
      router.push('/')
    }catch(err: any){
      console.log(err.message)
    }
  };
  

       //sePost function, onChangeHandler and Inputs for each fields here...

    <button
      type="button"
      onClick={()=>handleSubmit()}>
        Click
    </button>

backend/src/PosttypeDef.ts

export const postTypeDefs = `#graphql

  scalar JSON

  type Post {
    _id: ID!
    contactInfo: JSON
    title: String!
    description: String!
  }

  type Query {
    posts: [Post!]!
    }
    

  type CreatePostResponse {
    success: Boolean!
    error: String
  }

  type Mutation {
    createPost(post: PostInput): String
  }

  input PostInput {
    contactInfo: JSON
    title: String!
    description: String!
  }
`;

backend/post-resolver.ts

const dateScalar = new GraphQLScalarType({

export const postResolvers = {
  JSON: GraphQLJSON,

  Query: {
    // Return all posts
    posts: async () => {
      try {
        await connectDB();
        const posts = await Post.find();
        return posts;
      } catch (err) {
        console.log(err);
      }
    },
   

    //Return posts by User's ObjectId
    postsByUserId: async (parent, args) => {
      const { postedBy } = args;
      await connectDB();
      const posts = await Post.find();
      return posts.filter((post) => post.postedBy.toString() === postedBy);
    }
  },

  Mutation: {
    createPost: async (parent, args)=>{
      try{
        const {post} = args;
        // Just want to make this console.log work
        console.log("API endpoint", post)
        //Add to Mongodb, logic follows...
        return { success: true };

      }catch(err){
        return { success: false, error: err.toString() };
      }
    }
  }

};

Mongoose schema

import mongoose from 'mongoose';

const postSchema = new mongoose.Schema({
  contactInfo: {
    phone: { type: String, default: '' },
    email: { type: String, default: '' },
    sns: {
      type: {
        type: String,
        default: 'Twitter'
      },
      username: { type: String, default: '' }
    }
  },
  title: { type: String, required: true },
  description: { type: String, default: '' },

});


const Post = mongoose.model('Post', postSchema);

export default Post;

Solution

  • The typical source for a 400 error is GraphQL syntax error. That error would happen before your resolver is called. Try the same query in the playground first to make sure it works.

    I suspect the issue is that you've defined your CreatePost schema to return a String but you've included a field in your mutation result. Try changing:

    createPost(post: $post) {
      post
    }
    

    to

    createPost(post: $post)
    

    Also your resolver logic (that is never reached) is attempting to return an object return { success: true }; rather than a string so that will fail next.