Adding GraphQL Support to RaceGriot

After successfully building and deploying the REST API for RaceGriot, the next step is integrating GraphQL. While REST APIs work well, GraphQL provides more flexibility, efficiency, and control over data fetching. I have limited experience with GraphQL, so this is a great learning opportunity for me as well.

This post walks through how to add GraphQL support for RaceGriot, using Apollo Server with Node.js.

1️⃣ Why Add GraphQL?

REST APIs are common and the de-facto standard in a lot of cases, however they aren’t without limitations. One of their most glaring shortcomings is that they require multiple endpoints to fetch different sets of data. This can lead to:

  • Over-fetching: retrieving unnecessary data
  • Under-fetching: not getting all needed data in one request
  • Multiple API calls to fetch related data like race details & location info

GraphQL addresses these issues by using a schema-based approach which:

  • Allows clients to request only the data they need
  • Consolidates multiple requests into a single query
  • Provides a strongly typed schema

Unlike REST, GraphQL exposes a single endpoint where clients can specify exactly what they need. This eliminates the need for multiple endpoints and reduces over-fetching. Additionally, GraphQL’s strongly typed schema ensures consistency and allows clients to explore the API dynamically.

For RaceGriot, this means a better way to fetch race data without needing multiple REST endpoints.

2️⃣ Why Apollo Server with Node.js?

Since RaceGriot’s REST API is built with FastAPI (Python), we have two options for integrating GraphQL:

  1. Adding GraphQL directly to FastAPI using a Python-based library like Strawberry or Ariadne.
  2. Building a separate GraphQL layer with Apollo Server (Node.js) that fetches data from my existing REST API.

After evaluating both, we’ll go with Apollo Server with Node.js for these reasons:

  • Decoupled Architecture: this approach keeps the REST API and GraphQL services separate, making maintenance easier.
  • High Performance: Apollo Server is optimized for handling GraphQL queries efficiently.
  • Large Ecosystem: Strong support for third-party tools like Apollo Studio, caching, and federation.
  • Flexible Data Sources: this allows us to fetch data from multiple sources, including REST APIs, databases, and external APIs.

While we can embed GraphQL within the FastAPI service, using a dedicated Node.js Apollo Server provides a scalable and modular approach. This setup also keeps our FastAPI backend focused solely on REST while leveraging Apollo to bridge the gap between GraphQL and our existing data sources.

3️⃣ Setting Up Apollo Server for RaceGriot

Since we’re choosing the Node.js approach for GraphQL, we first need to install the Apollo Server and GraphQL dependencies:

npm install @apollo/server graphql

Next, we create a GraphQL schema to define how race data is structured and queried.

const { gql } = require("apollo-server");

const typeDefs = gql`
  type Location {
    city: String
    state: String
    country: String
    zip_code: String
  }

  type Race {
    id: ID!
    name: String
    location: Location
    date: String
    type: String
    website: String
    registration_link: String
  }

  type Query {
    races: [Race]
  }
`;

module.exports = typeDefs;

This defines:

  • Race type with attributes like id, name, date, etc.
  • Location type as a nested field inside Race
  • A Query type with a races field to fetch all races

The Race and Location types align with our existing REST API response structure, ensuring consistency across both API paradigms.

4️⃣ Implementing Resolvers

Resolvers define how GraphQL queries retrieve data. When a client sends a query, the resolver functions determine what data to return.

Resolvers are fundamental to GraphQL itself. In any GraphQL implementation (whether in Python, JavaScript, or another language), resolvers serve act as the middleware between the GraphQL schema and the actual data source (e.g., databases, REST APIs, or in-memory objects). Instead of directly returning hardcoded data, I connect GraphQL to my existing RaceGriot REST API.

const fetch = require("node-fetch");

const resolvers = {
  Query: {
    races: async () => {
      const response = await fetch("https://racegriot.onrender.com/races");
      return response.json();
    },
  },
};

module.exports = resolvers;

This returns the JSON response so GraphQL can process and serve it to clients.

5️⃣ Initializing Apollo Server

Now, we’re ready to initialize Apollo Server and integrate it with Express:

const { ApolloServer } = require("@apollo/server");
const express = require("express");
const { expressMiddleware } = require("@apollo/server/express4");
const typeDefs = require("./schema");
const resolvers = require("./resolvers");

const startServer = async () => {
  const app = express();
  const server = new ApolloServer({ typeDefs, resolvers });
  await server.start();
  app.use("/graphql", expressMiddleware(server));
  app.listen({ port: 4000 }, () => {
    console.log("🚀 Server ready at http://localhost:4000/graphql");
  });
};

startServer();

After this step, GraphQL is running locally at http://localhost:4000/graphql! Using the same steps as for the REST API, I deploy the GraphQL implementation of RaceGriot to Render for public consumption.

6️⃣ Testing the Deployed API

To demonstrate how GraphQL compares to our previous REST API implementation, here’s how we fetch the race data. The following query fetches all races from the API:

GraphQL Query

query {
  races {
    name
    location {
      city
      state
      country
    }
    date
  }
}

Expected Response

{
  "data": {
    "races": [
      {
        "name": "Boston Marathon",
        "location": {
          "city": "Boston",
          "state": "MA",
          "country": "USA"
        },
        "date": "2025-04-21"
      },
      {
        "name": "London Marathon",
        "location": {
          "city": "London",
          "state": null,
          "country": "UK"
        },
        "date": "2025-04-27"
      }
    ]
  }
}

At this stage, our GraphQL API only supports fetching all races. In a future update, we’ll add query parameters to allow filtering by name, location, or date, similar to how our REST API handles it.

Conclusion

With GraphQL now integrated, RaceGriot is:
✅ More flexible: clients can request only the data they need.
✅ More efficient: Reduces unnecessary API calls.
✅ Publicly accessible – Deployed on Render for global use.

The next step? Enhancing the API further with Apollo Connectors for better modularization and data fetching. Stay tuned for Part 4! 🚀

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *