Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Introduction to GraphQL  ›  Capstone Project

GraphQL on Frontend and Backend

GraphQL on Frontend 14 minOn the frontend a client library sends queries, caches results by type and id and generates types, while on the backend a server holds the schema and runs a resolver for each field, passing it the parent value, arguments and context.

GraphQL on Frontend and Backend

The bookshop has a React web app and a Node.js server. The frontend developer thinks in screens ("this card needs a title and an author"), the backend developer thinks in data sources ("books live in Postgres, reviews in another service"). GraphQL is the contract in the middle, and each side has its own job.

The rule

The frontend describes needs; the backend resolves each field.

On the frontend, a component states the data it needs as a query. A GraphQL client library (Apollo Client, Relay and urql are common ones) sends it, usually as an HTTP POST to a single endpoint such as /graphql, with a JSON body:

{ "query": "query { book(id: \"b1\") { title author { name } } }" }

Client libraries add three big helpers:

  • A normalised cache: objects are stored by type and id, so if two screens fetch book b1, it is kept once and both update together.
  • Loading and error states for each query.
  • Generated types: tools read the schema and produce TypeScript types for every query, so a typo in a field name fails at build time.

On the backend, a GraphQL server library (such as GraphQL.js, Apollo Server or GraphQL Yoga in JavaScript, graphql-java, Graphene in Python) holds the schema and a map of resolvers, one function per field that needs custom work:

const resolvers = {
  Query: {
    book: (parent, args, context) => context.db.findBook(args.id),
  },
  Book: {
    author: (book, args, context) => context.db.findAuthor(book.authorId),
  },
};

A resolver receives the parent object (the value its enclosing field returned), the field's arguments, and a shared context (logged-in user, database handles). Fields without a resolver, like Book.title, use a default that simply reads the property of the same name from the parent.

The shortcut

To see how the server answers, trace the query top-down and ask, for each field, "which resolver, and what parent?"

Resolving book b1's title and author nameStep 1 of 4
  1. Query.book

    No parent at the root. args.id is "b1", so it returns the book row { id, title, price, authorId }.

The trap is thinking the client talks to the database. It never does: it talks to the schema, and the resolvers are free to fetch from anywhere.

Drills

Answer fast — trust the rule.

quick drill

How does a typical frontend client send a query?

quick drill

What is the first argument the Book.author resolver receives?

quick drill

No resolver is written for Book.title. What happens?

quick drill

Why do client caches store objects by type and id?

Remember

  • The frontend describes needs; the backend resolves each field.
  • Trace top-down: for each field, which resolver runs and what parent it receives.
  • Trap: the client never touches the database; resolvers can fetch from anywhere.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainFollow one query end to end

A book card component needs book(id: "b1") { title author { name } }. Describe what happens from the moment the frontend sends this query to the moment the card renders, naming what the client library does and which resolvers the server runs with which parent values.