Best Practices: Pagination, Errors and Security

Harry · 14 Sep 2026 · 1 views
Advertisement
Advertisement

Pagination

Never return an unbounded list. The community standard is cursor-based (connection) pagination, which returns edges plus page info and handles large, changing datasets well:

query {
  posts(first: 10, after: "cursor123") {
    edges { node { title } cursor }
    pageInfo { hasNextPage endCursor }
  }
}

Error handling

A GraphQL response can contain both data and an errors array – a query can partially succeed. Return typed, meaningful errors with an error code in the extensions, and do not leak stack traces to clients.

{
  "data": { "user": null },
  "errors": [{ "message": "User not found",
               "extensions": { "code": "NOT_FOUND" } }]
}

Guard against expensive queries

Because clients compose their own queries, they can ask for something enormous – deeply nested or huge lists. Protect the server:

  • Depth limiting – reject queries nested beyond a maximum.
  • Query complexity analysis – assign a cost to fields and cap the total.
  • Pagination limits – enforce a maximum first value.
  • Timeouts – abort long-running resolvers.

Authentication and authorization

Authenticate at the transport layer (e.g. a JWT in the HTTP header), put the user in the resolver context, and authorize inside resolvers – checking permissions per field where needed. The single endpoint does not change this; it is the same security you would apply to REST.

Key points

  • Use cursor/connection pagination; never return unbounded lists.
  • Return typed errors with codes; a response may hold both data and errors.
  • Limit query depth and complexity to stop abusive requests.
  • Authenticate at the transport, carry the user in context, authorize in resolvers.
Share this post:

Comments (0)

Please login or register to comment.