51 GraphQL Server Configurations for Production
51 GraphQL Server Configurations for Production
GraphQL has now become one of the main backbones for building scalable and flexible APIs. However, operating a GraphQL server in a production environment requires extra care, especially when it comes to configuration. Many teams end up dealing with downtime, data leaks, or slow performance because their GraphQL configuration was not optimized during the production deployment.
In this article, I’ll walk through 51 configurations you absolutely must pay attention to, complete with best practices, code examples, and tips drawn from my own experience and from friends in the community.
1. Enable Query Depth Limiting
Limit query depth to prevent DoS attacks caused by excessively nested queries.
1const depthLimit = require('graphql-depth-limit');
2
3app.use(
4 '/graphql',
5 graphqlHTTP({
6 schema,
7 validationRules: [depthLimit(5)],
8 }),
9);
1const depthLimit = require('graphql-depth-limit');
2
3app.use(
4 '/graphql',
5 graphqlHTTP({
6 schema,
7 validationRules: [depthLimit(5)],
8 }),
9);2. Query Complexity Limits
Prevent data over-fetching by limiting how “heavy” a query can be.
1const { createComplexityRule } = require('graphql-validation-complexity');
2app.use(
3 '/graphql',
4 graphqlHTTP({
5 schema,
6 validationRules: [
7 createComplexityRule({
8 maximumComplexity: 1000,
9 estimators: [...],
10 }),
11 ],
12 }),
13);3. Disable Playground/GraphiQL in Production
Make sure development tooling endpoints are turned off.
1const isProd = process.env.NODE_ENV === 'production';
2app.use(
3 '/graphql',
4 graphqlHTTP({
5 schema,
6 graphiql: !isProd,
7 }),
8);4. Implement Persisted Queries
Only allow queries that have already been registered.
1// With Apollo Server
2const apq = require("apollo-server-plugin-persisted-queries");
3const server = new ApolloServer({
4 schema,
5 plugins: [apq()],
6});5. Custom Error Masking
Never expose stack traces in the response.
1const formatError = (err) => {
2 if (process.env.NODE_ENV === 'production') {
3 return new Error('Internal server error');
4 }
5 return err;
6}
7// Use formatError in Apollo/Express
6. Limit Request Size
Don’t let the request body grow too large.
1app.use(express.json({ limit: '1mb' }));7. Rate Limiting
Prevent brute-force attacks through the API.
1const rateLimit = require('express-rate-limit');
2app.use('/graphql', rateLimit({
3 windowMs: 60 * 1000,
4 max: 20,
5}));8. Strict CORS Policy
Make sure you specify exactly which origins are allowed to access the API.
1app.use(cors({ origin: ['https://mydomain.com'], credentials: true }));9. Implement Dataloader
Batch and cache data requests.
1const DataLoader = require('dataloader');
2const userLoader = new DataLoader(userIds => batchGetUsers(userIds));10. Disable Introspection in Production
Unless it is truly required.
1const { NoSchemaIntrospectionCustomRule } = require('graphql');
2app.use('/graphql', graphqlHTTP({
3 validationRules: [NoSchemaIntrospectionCustomRule],
4}));11. Tracing & Monitoring
Integrate with APM tools such as Datadog, Sentry, or Prometheus.
1import { ApolloServerPluginUsageReporting } from "apollo-server-core";
2const server = new ApolloServer({
3 plugins: [ApolloServerPluginUsageReporting()],
4});12. Authentication Is Mandatory
Enable JWT, OAuth, or session checks before the resolvers run.
13. Granular Authorization
Use directives such as @auth(role: ADMIN) in TypeGraphQL/Apollo.
14. Disable HTTP GET for Mutations
Mutations should only be allowed over POST.
15. Compress Responses
Use gzip/brotli for large responses.
16. Implement a Cache Layer
Cache within resolvers for read-heavy data.
17. Error Logging
Integrate with Winston, Bunyan, or the ELK stack.
18. Set Query Timeouts
Don’t let queries run for too long.
1// Pseudo code:
2const TIMEOUT = 5 * 1000;
3await Promise.race([resolveQuery(), wait(TIMEOUT)]);19–25: Advanced Security & Isolation
| # | Feature | Notes |
|---|---|---|
| 19 | Helmet.js Headers | Add HTTP security headers |
| 20 | Disable ‘X-Powered-By’ | Avoid exposing your tech |
| 21 | Content Security Policy | Prevent XSS |
| 22 | IP Allowlist | Restrict specific access |
| 23 | Secure Cookies | Use Secure+HttpOnly |
| 24 | Input Validation | Use Joi/Yup |
| 25 | Sanitize Inputs | Prevent injection |
26–30: Schema Management
- Schema Registry: Use the Apollo/Hasura schema registry.
- Versioning: Add a version marker in the header.
- Deprecation: Mark deprecated fields & types.
- Automatic Documentation: Integrate with GraphQL Voyager/SpectaQL.
- Schema Diff Monitoring: Integrate schema diffs through a GitHub Action.
31–35: Observability
- Custom Metrics: Implement a counter for the number of each operation.
- Distributed Tracing: Use OpenTelemetry.
- Audit Log: Record who accessed what and when.
- Ops Dashboard: Visualize performance & error rates.
- Alerting: Set up threshold alerts in Grafana/Prometheus.
36–39: Production Infrastructure & Deployment
- Readiness/Liveness Probes: K8S health check endpoints.
- Rolling Update: No downtime during deployment.
- Auto Scaling: Based on QPS rate.
- Blue/Green Deployment: Prevent downtime and ease rollback mitigation.
40–43: CDN and Edge Cache
- Cache Documentation & Introspection
- Stale-While-Revalidate at the Proxy
- Edge Rate Limiting: At the CDN (Cloudflare/Akamai)
- GraphQL CDN Routing: For example, in Cloudflare Workers for geo-routing.
44–46: Environment Separation
- Strict ENV File: Don’t mix production config with staging/dev.
- Sensitive Secret Management: Use a vault; never put API secrets in the codebase.
- Config Auditing: Regularly review configuration changes.
47–48: Testing Strategy
- Contract Test: Use GraphQL Faker/Mock for clients.
- Load Test: With Artillery or k6.
49–50: Performance Optimization
- Resolver: Avoid N+1: Always use Dataloader or aggregate SQL queries.
- Schema Stitching & Federation: For microservices decomposition.
51: Graceful Shutdown
Handle signals such as SIGINT so that in-flight requests complete before the process exits.
1process.on('SIGINT', () => {
2 server.stop().then(() => {
3 process.exit(0);
4 });
5});Deployment Configuration Flow Diagram
flowchart LR
A[Start Development]
B[Review Config]
C[Automated Deployment]
D{Environment?}
E[Pre-prod Checks]
F[Production Checks]
G[Run Health Probe]
H[Launch]
A-->B
B-->C
C-->D
D-->|Staging|E
D-->|Production|F
E-->|Pass|G
F-->|Pass|G
G-->H
Conclusion
Deploying a GraphQL server to production is not just about pressing the deploy button. There are many configuration aspects that, if skipped, can hurt your business—and your engineering team’s reputation.
The keys are:
- Restrict query exploration (introspection and nesting),
- Protect endpoints with a security layer,
- Monitoring for observability,
- Disciplined schema management,
- And a solid deployment pipeline.
This checklist not only prevents errors and leaks, but also helps your team focus on delivering value and growing the business. Are you ready to bring your GraphQL server to a truly production-grade level? Always revise it, discuss it with your team, and make sure this checklist becomes a mandatory part of your CI/CD pipeline.
Also check out awesome-graphql-security for the latest tools you can integrate right away!
If you have additional configuration experiences to share, feel free to drop them in the comments. Happy scaling GraphQL! 🚀