ThisCalculator

Everyday

GraphQL Config as Code: Directive Limits

Updated · Standard formula, shown below

GraphQL Config as Code: Directive Limits

GraphQL configuration as code lets you manage schema behavior through directives, but directives can only control what the GraphQL specification and your server implementation explicitly define—they cannot enforce security, validate business rules, or alter execution logic unless the server’s directive logic is custom-written. This article explains exactly which directives are built-in, what they can and cannot do, and where you need custom code or external tools.

What Are GraphQL Directives?

Directives are annotations in a GraphQL schema or query that tell the server to perform a specific transformation or check. They look like @deprecated or @include(if: true) and appear next to fields, types, or operations. The GraphQL specification defines a small set of built-in directives, but most real-world uses require custom directives implemented in your server’s code.

Configuration as code means you define these directives in your schema file (usually .graphql or .gql) and version them alongside your application code. This approach makes schema behavior reviewable, testable, and deployable through your normal CI/CD pipeline.

Built-in Directives: What the Spec Guarantees

The GraphQL specification (October 2021 edition) defines exactly three executable directives and two type-system directives. These are the only ones that work out of the box on any conforming server.

  • @include(if: Boolean) – includes a field or fragment in the response only if the condition is true.
  • @skip(if: Boolean) – skips a field or fragment if the condition is true.
  • @deprecated(reason: String) – marks a field or enum value as deprecated; tools can show warnings.
  • @specifiedBy(url: String) – points to a URL describing a custom scalar’s behavior.
  • @oneOf – for input objects, enforces that exactly one field is set (added in the 2021 spec).

These directives control response shape (include/skip), documentation (deprecated), scalar semantics (specifiedBy), and input validation (oneOf). They do not control authorization, rate limiting, or data fetching.

What Directives Cannot Control (Without Custom Code)

Out of the box, directives cannot do any of the following:

  • Authentication or authorization – no built-in directive checks who is calling or what they may see.
  • Data validation – beyond @oneOf, there is no pattern matching, length checks, or range checks.
  • Rate limiting or cost analysis – no directive limits query depth or complexity.
  • Field-level caching – no directive tells the resolver to cache a value.
  • Logging or tracing – no directive automatically records execution metrics.
  • Altering execution order – directives cannot change how resolvers run.

To get any of these behaviors, you must write a custom directive implementation in your server (e.g., in GraphQL-Java, Apollo Server, or graphql-js) that intercepts the field resolution and applies your logic. The directive definition in the schema is just a marker; the behavior lives in code.

Custom Directives: How They Actually Work

Custom directives are defined in the schema with the directive @name(args) on LOCATION syntax. The server then maps that name to a function or class that runs during execution. For example, in Apollo Server you might define @auth(role: String) and then in your resolver map check the user’s role before resolving the field.

Important: the GraphQL spec does not define how custom directives behave. Each server library has its own API for implementing them. Some libraries (like graphql-tools) provide a mapSchema utility to transform the schema based on directives, while others require you to wrap resolvers manually. This means custom directives are not portable across servers—a directive written for Apollo Server will not work in GraphQL-Java without rewriting.

Common Misconceptions and Pitfalls

A frequent mistake is assuming that adding a directive to the schema automatically enforces a rule. It does not. The directive only takes effect if you write the corresponding logic. Another pitfall is using directives for security and forgetting that the client can see the directive in the schema—so you must not expose sensitive logic details there.

Also, beware of directive ordering. The spec does not guarantee the order in which multiple directives on the same field are executed. If you rely on @auth running before @log, you need to test that your server actually does that, or combine them into one directive.

Finally, custom directives can hurt performance if they run on every field. For example, a directive that checks a database for authorization on each field will add latency. Consider batching or caching such checks.

Directives vs. Middleware vs. Schema Federation

Directives are not the only way to apply cross-cutting concerns. Middleware (like Apollo Server plugins) runs around the entire request, not per-field. Schema federation uses directives like @key and @external to compose subgraphs, but those are specific to federation and not part of the core spec.

For global concerns like authentication, middleware is often simpler than a custom directive because it runs once per request. For per-field concerns like deprecation or conditional inclusion, directives are the natural fit. For distributed schemas, federation directives are the standard—but they only work with a federation gateway.

How to Choose: Directive or Code?

Ask yourself: does this need to be visible in the schema and controlled by the API designer, or is it an internal implementation detail?

  • Use directives when the behavior is part of the API contract (e.g., deprecation, conditional fields, input constraints).
  • Use middleware or resolver wrappers when the behavior is about the request lifecycle (e.g., logging, rate limiting, authentication).
  • Use federation directives only if you are using Apollo Federation or similar.

If you choose a custom directive, document its behavior clearly and write tests that verify it works as intended, because the schema alone does not guarantee anything.

Safety and Maintainability

Directives that affect security or data integrity must be reviewed carefully. A misconfigured @auth directive could expose data. Always test negative cases (e.g., missing role, expired token). Also, keep directive definitions in a single file and version them; do not sprinkle them across multiple schema files without a clear structure.

Remember that GraphQL does not have a built-in way to restrict who can query what. Directives can help, but they are not a substitute for a proper authorization layer. For serious applications, combine directives with a gateway or a dedicated auth service.

Frequently Asked Questions

Can I use directives to hide fields from certain users?

Not directly. You could write a custom @auth directive that returns null or throws an error for unauthorized users, but the field still appears in the schema. To hide the field entirely, you would need to modify the schema dynamically, which is not a directive’s job.

Do directives affect query performance?

Built-in directives like @include and @skip have minimal overhead because they are evaluated before resolving the field. Custom directives can add significant overhead if they run database queries or external API calls per field. Measure and optimize accordingly.

Are custom directives portable across GraphQL servers?

No. The GraphQL spec defines the syntax but not the implementation. A custom directive written for Apollo Server will not work in graphql-js without changes. You must reimplement it for each server library.

Can directives validate input like regex or length?

Only if you write a custom directive that performs that validation. The built-in @oneOf is the only validation-like directive. For complex validation, consider using custom scalars or a validation library in your resolver.

What is the difference between @include and @skip?

They are opposites. @include(if: true) includes the field; @skip(if: true) skips it. Using both on the same field is allowed but redundant and can confuse readers.

Can I use directives to control caching?

Not with built-in directives. Some servers (like Apollo Server) have a @cacheControl directive, but it is an extension, not part of the spec. It only sets hints; the actual caching is done by a CDN or a cache layer.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.