Skip to content

Passing context to subgraph in federation #3922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
maon-fp opened this issue Mar 21, 2025 · 0 comments
Open

Passing context to subgraph in federation #3922

maon-fp opened this issue Mar 21, 2025 · 0 comments

Comments

@maon-fp
Copy link

maon-fp commented Mar 21, 2025

Describe the bug

Hello,

I'm currently switching from @apollo/server (using schema stitching) to graphql-yoga using federation.

The setup runs three subgraphs and a supergraph created by rover (as described in https://the-guild.dev/graphql/yoga-server/docs/features/apollo-federation#using-the-file-system). Serving is done by express.

The schema provides both queries which need authentication and freely accessible ones. Therefore I'm parsing the JWT up front in an express middleware. An user object is injected in the context base on the JWT payload. Authenticated queries rely on htis user object in the context.

Mutations and queries are forwarded to the correct service (e.g. login mutation) but it looks like the context (and therefore the user object) is not.

It seems to me that I am missing something fundamental.

I've tried to boil the code down to a minimal functionality:

import { readFileSync } from 'fs';

import express from 'express';
import jwt from 'jsonwebtoken';
import { createYoga } from 'graphql-yoga';
import { renderGraphiQL } from '@graphql-yoga/render-graphiql';
import { getStitchedSchemaFromSupergraphSdl } from '@graphql-tools/federation';

const SECRET = 'xyz';

async function checkAuth(request, response, next) {
  const token = request.get('authorization');

  try {
    if (!token) return next();

    const decoded = await jwt.verify(token, SECRET);

    request.user = decoded.user;

    return next();
  } catch (error) {
    return response.status(400).json({
      name: 'BadRequest',
      message: 'BadRequest',
    });
  }
}

async function start() {
  const app = express();

  app.use(checkAuth);

  const supergraphSdl = readFileSync('./supergraph.graphql', 'utf-8');

  const schema = getStitchedSchemaFromSupergraphSdl({ supergraphSdl });

  const publicYoga = createYoga({
    schema,
    graphqlEndpoint: '/',
    renderGraphiQL,
    plugins: [
      {
        onRequestParse({ request, serverContext }) {
          console.log({
            requestUser: request.user,
            serverContextUser: serverContext.req.user,
          });
        },
      },
    ],
  });

  express.use(publicYoga.graphqlEndpoint, publicYoga);
}

start();

JWT parsing works as I can see the user in the context:

  // output of onRequestParse
  {
    requestUser: undefined,
    serverContextUser: {
      userId: 'uck5c6k6yg008c09l1bseif96y',
      admin: false,
    },
  };

Context in the subgraph:

{
  req: IncomingMessage {
    _events: [Object],
    _readableState: [ReadableState],
    _maxListeners: undefined,
    socket: [Socket],
    httpVersionMajor: 1,
    httpVersionMinor: 1,
    httpVersion: '1.1',
    complete: true,
    rawHeaders: [Array],
    rawTrailers: [],
    joinDuplicateHeaders: null,
    aborted: false,
    upgrade: false,
    url: '/',
    method: 'POST',
    statusCode: null,
    statusMessage: null,
    client: [Socket],
    _consuming: true,
    _dumped: false,
    next: [Function: next],
    baseUrl: '/public/graphql',
    originalUrl: '/public/graphql',
    _parsedUrl: [Url],
    params: {},
    query: {},
    res: [ServerResponse],
    body: [Object],
    _body: true,
    length: undefined,
    _eventsCount: 0,
    [Symbol(shapeMode)]: true,
    [Symbol(kCapture)]: false,
    [Symbol(kHeaders)]: [Object],
    [Symbol(kHeadersCount)]: 12,
    [Symbol(kTrailers)]: null,
    [Symbol(kTrailersCount)]: 0
  },
  res: ServerResponse {
    _events: [Object: null prototype],
    _eventsCount: 3,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: false,
    chunkedEncoding: false,
    shouldKeepAlive: true,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: true,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    strictContentLength: false,
    _contentLength: null,
    _hasBody: true,
    _trailer: '',
    finished: false,
    _headerSent: false,
    _closed: false,
    _header: null,
    _keepAliveTimeout: 5000,
    _onPendingData: [Function: bound updateOutgoingData],
    req: [IncomingMessage],
    _sent100: false,
    _expect_continue: false,
    _maxRequestsPerSocket: 0,
    locals: [Object: null prototype] {},
    [Symbol(shapeMode)]: false,
    [Symbol(kCapture)]: false,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kChunkedBuffer)]: [],
    [Symbol(kChunkedLength)]: 0,
    [Symbol(kSocket)]: [Socket],
    [Symbol(kOutHeaders)]: [Object: null prototype],
    [Symbol(errored)]: null,
    [Symbol(kHighWaterMark)]: 65536,
    [Symbol(kRejectNonStandardBodyWrites)]: false,
    [Symbol(kUniqueHeaders)]: null
  },
  waitUntil: [Function: waitUntil]
}

Your Example Website or App

Don't know how to setup a federation on CodeSandbox

Steps to Reproduce the Bug or Issue

  • Create federation supergraph
  • Add information to context in gateway
  • Try reading information from context in subgraph

Expected behavior

Context of gateway should be passed to subgraph

Screenshots or Videos

No response

Platform

  • OS: Linux
  • NodeJS: v22.3.0
  • @graphql-yoga/* version(s):
    "@apollo/subgraph": "2.10.0",
    "@graphql-tools/schema": "7.1.5",
    "@graphql-tools/utils": "10.8.6",
    "@graphql-yoga/render-graphiql": "5.13.1",
    "express": "4.21.2",
    "graphql": "16.10.0",
    "graphql-tag": "2.12.6",
    "graphql-type-json": "0.3.2",
    "graphql-yoga": "5.13.1"

Additional context

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant