API Design

What Is a GraphQL API Endpoint?

SA
Samer Alsayegh
Founder
Published
7 min read
Flat vector illustration of a GraphQL API endpoint: a single node receiving query, mutation, and subscription requests and fanning out into resolved data fields
Key takeaway

A GraphQL API endpoint is a single URL. It runs every query and mutation over POST. That trades away REST's per-item caching and status codes. Match the endpoint's shape to how your data gets read.

A GraphQL API endpoint is a single URL that accepts every query and mutation a client sends. Most GraphQL servers expose it at /graphql. REST works the opposite way: each resource gets its own path — /users, /posts, /comments. With GraphQL, the client picks the shape of the response, not the server. One GraphQL query often swaps in for several REST round trips, per IBM's own look at both styles.

Content teams weighing GraphQL for content delivery face a familiar tradeoff. Flexible queries help the client. They also add real cost on the server side. Draftbase skips that tradeoff for content delivery. A plain REST-style endpoint returns MDX with links and media already filled in. No query language to parse or cache.

What Is a GraphQL API Endpoint?

Every GraphQL server, no matter the framework, exposes just one HTTP endpoint. That endpoint takes POST requests with a JSON body: a query string, plus an optional variables object. The server parses the query, checks it against a schema, then runs it.

This differs sharply from REST, where the URL itself encodes the resource and action. A GraphQL endpoint carries no meaning in its path. All the meaning lives in the request body. The server decides what data to return by reading the query, not the URL.

Because one endpoint serves every operation, routing logic disappears. There's no router mapping fifteen paths to fifteen handlers. Instead, a single resolver graph handles every field a client might ask for.

How Requests Reach the Endpoint

A GraphQL request is almost always an HTTP POST to that one URL. Here's the body:

{
  "query": "query { post(id: \"42\") { title author { name } } }",
  "variables": {}
}

Some servers also accept GET requests with the query as a URL parameter. That path mainly serves simple cached reads. POST is the default, per GraphQL's own serving guide, and that default has consequences.

Standard HTTP caching relies on GET requests and stable URLs. A CDN can cache GET /posts/42 by its path. It can't cache a POST body the same way. The cache key would need to include the entire query string. Most GraphQL explainers skip this tradeoff. The single-endpoint design that makes GraphQL flexible is the same design that makes edge caching hard, short of extra tools.

One Query Replaces Several REST Calls

Say a page needs a post's title plus its author's name. Here's a REST client:

GET /posts/42
GET /users/17

Here's the same page as one GraphQL query:

query {
  post(id: "42") {
    title
    author {
      name
    }
  }
}

The server resolves post and author in one execution pass. The client gets exactly the two fields it asked for. Nothing more. Fewer round trips. No over-fetching a full user object just to read a name. That's the core pitch behind a single endpoint.

No Versioned Endpoints

REST APIs often ship versioned paths — /v1/posts, /v2/posts — as the schema changes. GraphQL takes a different position. The GraphQL spec favors evolving one schema over shipping new endpoint versions.

Clients only request the fields they name in a query. Adding a new field to the schema doesn't break an existing query that never asked for it. Removing a field safely takes a deprecation cycle. Mark it with @deprecated. Watch usage drop. Remove it once clients stop querying it.

The catch: this discipline lives entirely on the server team. One endpoint doesn't force old clients to keep working — it just makes that easier, without adding more endpoints.

Fixing the Caching Gap With Persisted Queries

The caching problem has a known workaround: persisted queries. Instead of sending the full query text in the POST body, the client sends a short hash. The server looks up the matching query it already has stored.

That hash is small enough to fit in a URL. Apollo's server lets clients send the hash as a GET request instead of a POST. A stable GET URL is exactly what a CDN needs to cache a response. It closes most of the gap between GraphQL and REST on caching.

This isn't free. It requires a persisted-query store on the server. It also needs client tooling that knows to hash queries before sending them. Plan for CDN-level caching from the start. Don't bolt it on after a performance problem shows up in production.

Subscriptions Need a Different Endpoint

Queries and mutations fit the single-POST-endpoint model fine. Subscriptions don't. A subscription stays open and pushes new data as it changes. One request-response round trip can't do that.

Because of that, subscriptions run over WebSockets, not plain HTTP. The GraphQL spec itself sets no fixed rule here. Two community protocols fill the gap. The older is subscriptions-transport-ws. The newer, and now the common one, is graphql-ws. A client opens a WebSocket connection and picks a protocol in the handshake. From there, it sends and gets JSON messages tagged with a type field.

So a GraphQL API often has two live endpoints in practice, not one. There's the usual /graphql for queries and mutations. There's a separate WebSocket endpoint for anything that needs live updates. Worth knowing before you assume "single endpoint" means one connection type, full stop.

Status Codes Work Differently

REST APIs use HTTP status codes to flag success or failure — 404 for a missing item, 400 for bad input. GraphQL doesn't work that way. A GraphQL endpoint sends back 200 OK for almost every call, even one with errors in it.

The official spec says: if a response has a data key set, the server should send a 2xx status. That holds even when the response also has an errors list.

That means clients can't trust the status code alone. They must check the errors field in the body. That's the only way to know if the call partly or fully failed. This trips up developers used to REST's rules on status codes. Plan for it on day one, before a silent bug ships.

Introspection Turns the Endpoint Into a Live Schema Browser

A GraphQL endpoint can answer a special introspection query. That query returns its own schema: every type, field, and argument available. Tools like GraphiQL and Apollo Studio use it to build live schema explorers.

That convenience is also a risk. Anyone who can reach the endpoint can run an introspection query. That query maps the entire API surface, with no login needed. Apollo's own security guide says: turn introspection off in production. Left on, it hands an attacker a full map of every action, even ones never meant for public use.

That's the flip side of one endpoint doing everything. Securing it means securing the whole graph, not just the one field an attacker happens to touch.

The Underused Angle: One URL, Uneven Adoption

Coverage of GraphQL endpoints tends to stop at "single URL, flexible queries." What gets skipped: how far REST still leads. An Enterprise Strategy Group study, cited by Nordic APIs in 2025, found REST used by 92% of firms. GraphQL adoption sits near 70%. Most teams run both side by side, not one instead of the other.

That pattern matches what the single-endpoint tradeoff predicts. Teams reach for GraphQL when a client needs data from many sources in one request. Think a mobile app screen, or a dashboard. They keep REST for simple reads that a CDN can cache, where a stable URL does the heavy lifting for free. Choosing between them isn't about picking a winner. It's about matching the endpoint's shape to how the data actually gets read.

Where This Leaves Content APIs

A CMS delivery API is closer to the second case than the first. Most reads are "give me this entry" or "give me this list," not a wide-open crawl through a graph. Draftbase's delivery API stays REST-shaped for that reason. URLs stay predictable. GET requests stay cacheable. MDX content comes back with references and media already resolved. No resolver graph to secure. No introspection surface to lock down.

Conclusion

A GraphQL API endpoint is a single URL, reached almost always over POST. It runs every query and mutation through one schema. That design trades REST's per-resource caching and status codes for flexible, client-shaped responses. It's a real win for clients that pull data from many sources at once. It's a real cost for simple content reads. If your API mostly serves content to a React front end, a REST-style endpoint keeps things simpler: caching, error handling, security. You give up little in return. See how Draftbase's pricing compares if you're evaluating a delivery layer for your next project.

Frequently asked questions

Does a GraphQL endpoint use POST or GET?

A GraphQL endpoint takes POST requests by default, though some servers also accept GET for simple cached reads.

Why does a GraphQL endpoint return 200 even on errors?

A GraphQL endpoint returns 200 OK even when the response has errors in it. Clients must check the errors field in the body, not the status code.

What URL path do GraphQL endpoints usually use?

Most teams put it at /graphql. That path is a convention, not a spec rule, so a server can expose it anywhere.

Should I disable introspection on my GraphQL endpoint?

Turn off introspection in production. Left on, anyone can read your full schema. No login needed. That's a map for an attacker.

Does GraphQL support versioned endpoints like REST?

No. GraphQL favors changing one schema over time. Mark old fields with @deprecated instead of shipping versioned paths like /v1 and /v2.

SA
Samer Alsayegh
Founder at Draftbase

Samer is a software engineer and entrepreneur, founder of Draftbase and Ezi Home Services, building technology that simplifies home services. Passionate about software, APIs, automation, and creating products that solve real-world problems.

graphqlapi-designrest

Related posts

Draftbase is a headless CMS built for React devs.