Gabriel Kunkel
Gabriel Kunkel

Reputation: 2761

400 Bad Requst to GraphQL endpoint (What's wrong with my test's query?)

I am largely following the GraphQL documentation about how to create a mutation. This is my first day working with GraphQL. I want to create an endpoint for comments.

Isolating the problem

I'm getting back 400 "Bad Request" from my supertest in Mocha and when I try to run the GraphiQL it gives me a message in an errors array that says, "Query root type must be provided." ...But I am including rootValue.

enter image description here

One place the problem could be is in the buildSchema.

// imports (...working fine)

// Comment Interface for TypeScript is here ...and working
// ...

// gaphql schema (The problem could be here.)
const schema = buildSchema(`
    input CommentInput {
        _id: String
        author: String
        text: String
    }
    type Comment {
        _id: String
        author: String
        text: String
    }
    type Mutation {
        createOrUpdateComment(input: CommentInput): Comment
    }
`);

// rootValue
const root = {
    createOrUpdateComment: ({input}: { input: IComment }) => {
       return {
         _id: "id here",
         author: input.author,
         text: input.text
       }
    }
};

export default graphqlHTTP({
    graphiql: true,
    rootValue: root,
    schema
});

This gets imported and then used in the top level of the express server, as comments like so:

app.use("/comments", comments);

What's working: The GraphiQL does pop up at localhost:8080/comments in the browser, so I'm pretty sure there's no issues on the Express side of things. (TypeScript compiles just fine, by the way.) I also have a different GraphQL api endpoint that returns, "Hello world!" on the top level of the server that I am testing using Mocha and it passes (after it initially failed), so my testing suite seems to be working well. There are no additional environmental variables. It's just what you see here.

The test that I want to pass, which gives me the 404 bad request is:

// imports (...working fine)

describe("graphQl comments test", () => {

    it("should add a comment", (done) => {

        const query = `mutation createOrUpdateComment($input: CommentInput) {
              _id
              author
              text
          }`;

        const variables = {
              input: {
                  author: "Brian",
                  text: "This is my favorite video of all time."
              }
          };

        request(app)
            .post("/comments")
            .send(JSON.stringify({ query, variables})) // (The problem could be here.)
            .expect(200)
            .end((err, res) => {
                if (err) { return done(err); }
                // tslint:disable-next-line: no-unused-expression
                expect(res.body.data.id).to.exist;
                expect(res.body.data.author).to.equal("Brian");
                expect(res.body.data.text).to.equal("This is my favorite video of all time.");
                done();
            });
    });
});

Since it says "bad request", the way I handle the query and the variables in the test definitely could be the problem.

The Question

Is my problem the query and variables in the test or is it my buildSchema? ...Or is it both? How would I write either differently to get the test to pass?

Update

After I commented out the rootValue in the exports, like so, since it was saying, "Query root type must be provided," in GraphiQL I found there is no difference. It's as if the rootValue isn't there, even though it is.

export default graphqlHTTP({
    graphiql: true,
    // rootValue: root,
    schema
});

Once I added a Query and a getComment() per something mentioned in this Github issue the error message in GraphiQL is gone. The test, unfortunately, is still giving 400 Bad Request so the problems seem to be unrelated.

Update 2

I tested my endpoint out with GraphiQL and it functioned as expected/desired, when I post with:

mutation {
  createOrUpdateComment(input: {_id: "4", author: "Some Author", text: "some unique text"}) {
    _id
    author
    text
  }
}

I'm working on making my tests query similar, but they're not analogues. This makes it clear that the problem is in the test itself and when I log the body's response I get this:

enter image description here

So there's something wrong with my test's query string.

Solved.

See my answer below.

Upvotes: 1

Views: 2520

Answers (2)

Gabriel Kunkel
Gabriel Kunkel

Reputation: 2761

My main problems in the test was that

  • I was JSON.stringifying the data. That was in the documentation's example using fetch, but supertest converts the data into a JSON string automatically.
  • I adjusted my query to only be a one string with no variables added. I'd like to play around with this more soon and see if I could send it with variables, but I just wanted to simplify things.
  • On Daniel Rearden's advice I logged out the body. This is a priceless piece of debugging advice that I didn't think to do in the test, because I just assumed, "Well, it's erroring out, right?" Duh. Nope. There's always a response. This helped me set my return values straight.

This is the test file I ended up with:

import { expect } from "chai";
import request = require("supertest");
import app from "../src";

describe("graphQl comments test", () => {

    it("should add a comment", (done) => {

        const query = `mutation {
            createOrUpdateComment(input: {_id: "4", author: "Brian", text: "This is my favorite video of all time."}) {
              _id
              author
              text
            }
          }`;

        request(app)
            .post("/comments")
            .send({query})
            .expect(200)
            .end((err, res) => {
                if (err) { return done(err); }
                // tslint:disable-next-line: no-unused-expression
                expect(res.body.data.createOrUpdateComment._id).to.exist;
                expect(res.body.data.createOrUpdateComment.author).to.equal("Brian");
                expect(res.body.data.createOrUpdateComment.text).to.equal("This is my favorite video of all time.");
                done();
            });
    });
});

Upvotes: 0

Daniel Rearden
Daniel Rearden

Reputation: 84687

There are three operations in GraphQL -- queries, mutations and subscriptions. Each operation has a single object type associated with it. These are called root types. These root types are by default named, respectively, Query, Mutation and Subscription (the names are irrelevant and can be changed for your specific schema, but these are the defaults). The root types serve as the entry point, or root, to the rest of your schema for any query made against it.

The query root type is required by the specification, while the other two root types are optional and may be omitted.

The error is not related to the root value whatsoever. In fact, you probably shouldn't be using buildSchema and rootValue in the first place. As the error indicates, you have not provided a query root type in your schema. The only root type your schema includes is Mutation. You need to add Query in order to resolve the error.

Upvotes: 1

Related Questions