graphql / graphql.github.io

GraphQL Documentation at graphql.org

Home Page:https://graphql.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] Sample code for GraphQL.js tutorial

blue32a opened this issue · comments

Hello

I have been working through the graphql.js tutorial, particularly the section on "Authentication and Express Middleware". I encountered an issue when trying to execute the API designed to return the caller's IP address. Unfortunately, instead of receiving the expected IP address, the API response was data: { ip: null }.

Here are the steps I followed:

  1. I set up the environment and dependencies as outlined in the tutorial.
  2. Followed the instructions in the "Authentication and Express Middleware" section to implement the API for returning the caller's IP address.
  3. Executed a query expecting to receive the caller's IP address in the response.

Expected Result:
I expected to receive a response containing the caller's IP address, similar to data: { ip: "caller's IP address" }.

Actual Result:
The response was data: { ip: null }, indicating that the IP address could not be retrieved or was not properly passed through the middleware.

var root = {
  ip: function (args, request) {
    return request.ip // request is undefined
  },
}

Dependencies:

  "dependencies": {
    "express": "^4.18.3",
    "graphql": "^16.8.1",
    "graphql-http": "^1.22.0",
    "ruru": "^2.0.0-beta.11"
  }

I double-checked the code and setup to ensure compliance with the tutorial instructions but could not identify any deviations or mistakes on my part that could cause this issue.

I would greatly appreciate any guidance or suggestions you could offer to resolve this issue. If there are any additional details or information you need, please let me know, and I'll be happy to provide them.

Thank you for your time and support.

@enisdenjo I don't know for sure, because I never use the "rootValue" approach to defining resolution, but I'm guessing that these functions are called passing (args, context, requestInfo) and given that the second argument is called request in this state, I'm guessing that express-graphql defaulted to providing the request as the context if context was not specified. This not working any more would imply that graphql-http does not do the same (and who can blame it?!), but it would be good to either a) have this be the default behavior, or b) update this example to show how to add the request to context (and then change this to something like:

var root = {
  ip: function (args, context) {
    return context.request.ip
  },
}

@blue32a Thanks for bringing this to our attention, it's clearly something that was missed when we transitioned from express-graphql to graphql-http. In the mean time, I would recommend building a schema with resolvers rather than using a rootValue approach. One way of doing so is with the makeExecutableSchema function from graphql-tools:

const express = require("express");
const { createHandler } = require("graphql-http/lib/use/express");
const { makeExecutableSchema } = require("@graphql-tools/schema");

const schema = makeExecutableSchema({
  typeDefs: /* GraphQL */ `
    type Query {
      ip: String
    }
  `,
  resolvers: {
    Query: {
      ip(_, args, context) {
        return context.ip;
      },
    },
  },
});

const loggingMiddleware = (req, res, next) => {
  console.log("ip:", req.ip);
  next();
};

var app = express();
app.use(loggingMiddleware);
app.all(
  "/graphql",
  createHandler({
    schema: schema,
    context: (req) => ({
      ip: req.ip,
    }),
    graphiql: true,
  }),
);
app.listen(4000);
console.log("Running a GraphQL API server at localhost:4000/graphql");

(Note this code is untested, I just wrote it into GitHub.)

@benjie
Thanks for your comment. I was able to solve this problem.

Be careful with the req you receive in context. The req was not an Express request object, the Express request object is in req.raw.
https://github.com/graphql/graphql-http/blob/6ba00058ead9ff950fd80d39d412383abcc2663a/src/use/express.ts#L139

The final code looked like this.

const express = require('express');
const { createHandler } = require('graphql-http/lib/use/express');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    ip: String
  }
`);

const loggingMiddleware = (req, _res, next) => {
  console.log('ip:', req.ip);
  next();
}

const rootValue = {
  ip: function (_, context) {
    return context.ip;
  },
};

const app = express();
app.use(loggingMiddleware);
app.all(
  '/graphql',
  createHandler({
    schema,
    rootValue,
    context: (req) => ({
      ip: req.raw.ip,
    }),
  })
);
app.listen(4000);
console.log('Running a GraphQL API server at http://localhost:4000/graphql');