Cypress can’t directly "intercept" GraphQL requests in the same way it intercepts standard HTTP requests because GraphQL queries are typically sent as POST requests with a JSON payload to a single endpoint, and Cypress’s cy.intercept() command looks for specific request properties like method, URL, and request body.

Here’s how you can effectively test your GraphQL interactions using Cypress, focusing on the underlying HTTP requests:

The GraphQL Request Anatomy

When your frontend application makes a GraphQL request, it usually looks something like this:

  • Method: POST
  • URL: A single endpoint, often /graphql or /api/graphql.
  • Headers: Content-Type: application/json, and often Authorization or other custom headers.
  • Body: A JSON object containing at least query (the GraphQL query string) and optionally variables and operationName.

Cypress intercepts these as standard HTTP POST requests. The key is to identify which GraphQL request you want to intercept based on its unique characteristics.

Identifying Your GraphQL Requests

The most reliable way to target a specific GraphQL request is by its query string or its operationName.

Example Frontend Code (Conceptual):

// Using Apollo Client
const GET_USER_QUERY = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`;

client.query({
  query: GET_USER_QUERY,
  variables: { id: '123' },
  operationName: 'GetUser' // This is a great identifier!
});

In this example, GetUser is the operationName, and the query string is the actual GraphQL query. You can use either to tell cy.intercept() which request to match.

Intercepting and Mocking

Let’s say you have a component that fetches user data.

cypress/support/commands.js:

Cypress.Commands.add('interceptGetUser', (userId = '123', mockData) => {
  const operationName = 'GetUser';
  const query = `query GetUser($id: ID!) { user(id: $id) { id name email } }`; // Or match a substring

  cy.intercept('POST', '/graphql', (req) => {
    // Check if it's the specific query and variables we're looking for
    if (req.body.operationName === operationName && req.body.variables && req.body.variables.id === userId) {
      req.reply({
        statusCode: 200,
        body: {
          data: {
            user: mockData || {
              id: userId,
              name: 'Mock User',
              email: 'mock@example.com',
            },
          },
        },
      });
    }
  }).as(operationName); // Alias for waiting
});

cypress/e2e/user_profile.cy.js:

describe('User Profile Page', () => {
  it('should display user information', () => {
    const userId = '456';
    const mockUserData = {
      id: userId,
      name: 'Jane Doe',
      email: 'jane.doe@example.com',
    };

    // Use the custom command to intercept
    cy.interceptGetUser(userId, mockUserData);

    // Visit the page that triggers the request
    cy.visit(`/users/${userId}`);

    // Wait for the request to be intercepted (optional but good practice)
    cy.wait('@GetUser');

    // Assert that the data is displayed correctly
    cy.contains('Jane Doe').should('be.visible');
    cy.contains('jane.doe@example.com').should('be.visible');
  });
});

Explanation:

  1. cy.intercept('POST', '/graphql', ...): We target all POST requests to /graphql.
  2. req => { ... }: This is the callback function where we inspect the incoming request.
  3. if (req.body.operationName === operationName && req.body.variables && req.body.variables.id === userId): This is the crucial part. We check the request body to ensure it’s the specific GraphQL operation we want to mock. Matching by operationName is often cleanest. If operationName isn’t consistently used, you might need to match against a portion of the req.body.query string.
  4. req.reply(...): If the request matches, we send back a mocked response. The body structure must match what your frontend expects from a GraphQL server (usually {"data": {...}} or {"errors": [...]}).
  5. .as(operationName): This gives the intercepted request an alias (@GetUser) that we can use with cy.wait().

Advanced Matching

  • Matching by Query String: If operationName isn’t available or reliable, you can match against the query string. Be careful with exact matches, as whitespace can vary. Using req.body.query.includes('GetUserQueryName') or a more robust regex might be necessary.

    cy.intercept('POST', '/graphql', (req) => {
      if (req.body.query && req.body.query.includes('GetUser') && req.body.variables && req.body.variables.id === userId) {
        // ... reply
      }
    });
    
  • Multiple Intercepts: You can set up multiple intercepts for different GraphQL operations on the same page. Ensure your matching logic is specific enough to target the correct one.

  • Testing Errors: Mocking GraphQL errors is just as important.

    cy.intercept('POST', '/graphql', (req) => {
      if (req.body.operationName === 'GetUser' && req.body.variables && req.body.variables.id === userId) {
        req.reply({
          statusCode: 200, // GraphQL errors often return 200 OK
          body: {
            errors: [{
              message: 'User not found',
              locations: [{ line: 2, column: 3 }],
              path: ['user']
            }],
            data: null, // Or omit data entirely
          },
        });
      }
    });
    

The Counterintuitive Part

Many developers assume they need to parse the entire GraphQL query string within cy.intercept to find the exact operation. However, most GraphQL clients include an operationName field in the JSON payload precisely for this purpose. Relying on operationName is significantly more robust and easier to maintain than trying to dynamically parse and match complex GraphQL query strings, especially when dealing with fragments, directives, or variations in whitespace. If your client doesn’t send operationName, consider configuring it to do so, or falling back to matching known substrings within the query field.

Next Steps

Once you’ve mastered mocking GraphQL requests, you’ll want to explore how to stub out network requests that are not GraphQL, such as REST endpoints used for authentication or other auxiliary services.

Want structured learning?

Take the full Cypress course →