You have learned the whole GraphQL toolbox: schemas, queries, mutations, resolvers, fragments, and performance patterns. This final lesson distills the habits that separate maintainable GraphQL APIs from painful ones, covers security essentials, and answers the honest question: when should you pick GraphQL over REST, and when should you not?
The most common mistake is mirroring your database. A schema should describe what clients need in their language. Prefer viewer { unreadNotifications } over exposing a notifications table and forcing every client to filter it. Practical guidelines:
String!) only when they can never legitimately be missing; adding non-null later is a breaking change in reverse.publishPost instead of a generic updatePost with twenty optional fields.GraphQL APIs generally avoid /v2 endpoints. Because clients ask for exactly the fields they use, you can add new fields freely and retire old ones gradually:
type Post {
title: String!
body: String! @deprecated(reason: 'Use contentBlocks instead')
contentBlocks: [ContentBlock!]!
}Tools like GraphQL field usage tracking tell you when a deprecated field finally has zero traffic and can be removed safely.
A predictable mutation pattern pays off quickly. A widely adopted convention is one input object and one payload type per mutation:
type Mutation {
createPost(input: CreatePostInput!): CreatePostPayload!
}
input CreatePostInput {
title: String!
body: String!
}
type CreatePostPayload {
post: Post
userErrors: [UserError!]!
}
type UserError {
field: String
message: String!
}Returning userErrors in the payload keeps expected validation failures out of the transport-level errors array, so clients can render field-specific messages easily.
A flexible query language means clients can also write hostile queries. Protect your server with:
friends { friends { friends ... } } cannot recurse forever.Caching deserves thought too: HTTP caching is harder than REST because most requests hit one POST endpoint, so rely on client-side normalized caches, CDN-cacheable persisted GET queries, and server-side caching inside resolvers.
GraphQL and REST also coexist happily: many companies expose GraphQL for product frontends while keeping REST or gRPC between internal services.
@deprecated and field usage data instead of versioned endpoints.userErrors for expected failures.That wraps up the GraphQL course. A great next step is building a small full-stack project: a Node.js GraphQL server from lesson 7 consumed by the frontend techniques from lesson 10.