Unifying Data Fetching with GraphQL : The Upgrade Every Developers Need

Kreeti SharmaKreeti Sharma
4 min read

Picture this: your frontend needs to render a blog post with its author's name and other authored posts. With a RESTful setup, that typically means:

  • GET /blog/:id

  • GET /author/:id

  • GET /author/:id/blogs

That’s three API calls just to render one view. On top of that, those endpoints could be managed by different microservices—each backed by different databases. The result? Overfetching, inconsistent latency, and a frustrating experience for both devs and users.

So how do we clean this up?

Enters GraphQL: Meta's Answer to Fragmented APIs

GraphQL was developed by Meta (Facebook) to streamline and unify how client apps interact with servers. It solves two core issues of REST:

  1. Over-fetching/Under-fetching: REST endpoints often return more or less data than needed.

  2. Multiple Roundtrips: REST requires multiple endpoint hits for relational data.

Instead, GraphQL introduces a single endpoint (/graphql) with only two HTTP verbs: GET for queries and POST for mutations. But here’s the magic—GraphQL knows your data relationships through its schema and lets you request exactly what you need. No more chasing endpoints.

Example: GraphQL Schema for Blog & Author

This schema defines a blog type and a root-level Query that returns a list of blogs and help us understand the relation between blog and author.

type Blog {
  id: String
  title: String
  content: String
  author: Author
}

type Author {
  id: String
  name: String
  email: String
  blogs: [Blog]
}

And here's a sample query:

query {
  blogs {
    id
    title
    author {
      name
      blogs {
        title
      }
    }
  }
}

With just this, your frontend gets the blog, its author's name, and titles of all their other blogs—in one roundtrip.

Building with Apollo GraphQL

Below example shows schema that defines a Book type and a root-level Query that returns a list of books. Schema-first design helps the frontend understand what data can be queried—enabling schema interpolation and IDE auto-completion. Let’s set up a basic GraphQL server using Apollo:

const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');

const typeDefs = `#graphql
  type Book {
    title: String
    subtitle: String
    publisher: String
    description: String
  }

  type Query {
    books: [Book]
  }
`;

const resolvers = {
  Query: {
    books: () => [
      {
        title: "Fox in Socks",
        subtitle: "My Subtitle",
        publisher: "Piyush",
        description: "Nice"
      }
    ]
  }
};

const server = new ApolloServer({ typeDefs, resolvers });

async function init() {
  const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
  console.log(`🚀 Server ready at: ${url}`);
}

init();

The resolver in above code connects our GraphQL books query to an actual data source (hardcoded array) which becomes the bridge between schema definition and real output. Then we start the server on port 4000 using Apollo’s startStandaloneServer that makes the server up to accept queries and mutations at /graphql.

Now for a mutation example:

This mutation, addBook, lets clients insert a new book with a title and author. Mutations are GraphQL’s way of modifying server-side data.

type Mutation {
  addBook(title: String, author: String): Book
}

Here's how you can implement the mutation resolver:

const resolvers = {
  Query: {
    books: () => [
      {
        title: "Fox in Socks",
        subtitle: "My Subtitle",
        publisher: "Piyush",
        description: "Nice"
      }
    ]
  },
  Mutation: {
    addBook: (_, { title, author }) => {
      const newBook = { title, author, subtitle: "", publisher: "", description: "" };
      // Here you would typically save the new book to a database
      console.log(`Book added: ${title} by ${author}`);
      return newBook;
    }
  }
};

const server = new ApolloServer({ typeDefs, resolvers });

async function init() {
  const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
  console.log(`🚀 Server ready at: ${url}`);
}

init();

With this setup, you can add a new book using the addBook mutation.

mutation CreateBook {
  addBook(title: "Fox in Socks", author: "Dr. Seuss") {
    title
    author
  }
}

Not All Sunshine: GraphQL Tradeoffs

Despite its elegance, GraphQL comes with caveats:

  • Added Latency: Validation on input/output introduces delays.

  • Memory Pressure: Data aggregation increases memory consumption.

  • No Lazy Loading: All requested data loads at once—no partial rendering.

  • Schema Sync: Backend and frontend must stay aligned on schema definitions.

Meta’s Recommendation: GraphQL as a Proxy Layer?

By using GraphQL as a proxy between frontend and backend, following are the few features that GraphQL supports:

  • It acts as a validation layer

  • Enables schema-based interpolation

  • Acts as a simple, unified API gateway

This setup enhances DX (Developer Experience) but requires conscious schema management and performance optimization.

Conclusion

In my opinion, GraphQL isn’t just a tool—it’s a philosophy of declarative data fetching. If your app juggles relational data across multiple services or calls several endpoints to populate a single view, switching to GraphQL is like going from a tangled web to a structured novel, hence upgrading data fetching experience for developers and users.

0
Subscribe to my newsletter

Read articles from Kreeti Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kreeti Sharma
Kreeti Sharma