Client Area
GraphQL APIsAdvanced

Building GraphQL APIs on DomainIndia: Apollo Server, Hasura, and Best Practices

ByDomain India Team·DomainIndia Engineering
5 min read24 Apr 20264 views
# Building GraphQL APIs on DomainIndia: Apollo Server, Hasura, and Best Practices
TL;DR
GraphQL lets clients ask for exactly the fields they need — no more over- or under-fetching. This guide covers when GraphQL beats REST, running Apollo Server (Node.js) or Hasura (instant GraphQL from PostgreSQL) on DomainIndia hosting, and avoiding the "N+1 query" trap.
## GraphQL vs REST — when to pick which
FeatureRESTGraphQL
EndpointsMany (/users, /users/:id/posts, etc.)Single (/graphql)
Client flexibilityServer decides response shapeClient picks fields
Over/under-fetchingCommonRare
CachingEasy (HTTP cache)Harder (needs client-side cache like Apollo)
Learning curveLowHigher
Mobile appsFineGreat — thin networks love minimal payloads
Simple CRUDREST is simplerGraphQL is overkill
**Pick GraphQL when:** - Multiple clients (web + iOS + Android) need different fields - Deep nested data (user → posts → comments) where REST would need 3+ requests - Rapidly evolving frontend — backend doesn't need to deploy new endpoints **Stick with REST when:** - Simple CRUD - HTTP caching is critical - Team is small and REST-experienced ## Option A — Apollo Server (Node.js) Full control, custom resolvers, flexible auth. ```bash mkdir gql-api && cd gql-api npm init -y npm install @apollo/server graphql @as-integrations/express4 express ``` `server.js`: ```javascript import { ApolloServer } from '@apollo/server'; import { expressMiddleware } from '@as-integrations/express4'; import express from 'express'; const typeDefs = `#graphql type User { id: ID! name: String! email: String! posts: [Post!]! } type Post { id: ID! title: String! author: User! } type Query { user(id: ID!): User posts: [Post!]! } type Mutation { createPost(title: String!, authorId: ID!): Post! } `; const resolvers = { Query: { user: async (_, { id }, { db }) => db.user.findUnique({ where: { id } }), posts: async (_, __, { db }) => db.post.findMany(), }, User: { posts: async (user, _, { db }) => db.post.findMany({ where: { authorId: user.id } }), }, Post: { author: async (post, _, { db }) => db.user.findUnique({ where: { id: post.authorId } }), }, Mutation: { createPost: async (_, args, { db }) => db.post.create({ data: args }), }, }; const apollo = new ApolloServer({ typeDefs, resolvers }); await apollo.start(); const app = express(); app.use('/graphql', express.json(), expressMiddleware(apollo, { context: async ({ req }) => ({ db: prismaClient, user: req.user }), })); app.listen(4000, () => console.log('http://localhost:4000/graphql')); ``` Deploy on DomainIndia: - Shared cPanel: "Setup Node.js App" — same pattern as any Express API - VPS: systemd + nginx reverse proxy (see our [Node.js Development articles](https://domainindia.com/support/kb/category/dev-nodejs)) ## Option B — Hasura (instant GraphQL over PostgreSQL) Hasura reads your PostgreSQL schema and exposes a full GraphQL API — no resolvers to write. Install on VPS: ```bash # Docker-based (easiest) docker run -d -p 8080:8080 -e HASURA_GRAPHQL_DATABASE_URL=postgres://user:pass@localhost/mydb -e HASURA_GRAPHQL_ENABLE_CONSOLE=true -e HASURA_GRAPHQL_ADMIN_SECRET=supersecret hasura/graphql-engine:v2.36.0 ``` Visit `http://your-vps:8080/console`, log in with the admin secret, and every table is now queryable via GraphQL. Permissions, subscriptions (real-time), derived relationships — all via UI.
Insight

Hasura is incredible for internal tools. You save 90% of the boilerplate. For customer-facing APIs, front it with nginx + custom auth + rate limiting.

## The N+1 problem (and DataLoader fix) Classic GraphQL trap. Query: ```graphql { posts { title author { name } } } ``` Naive resolver runs: - 1 query for all posts - 1 query per post to get each author 100 posts = 101 queries. Disaster. **Fix: batch with DataLoader** ```javascript import DataLoader from 'dataloader'; const userLoader = new DataLoader(async (ids) => { const users = await db.user.findMany({ where: { id: { in: ids } } }); return ids.map(id => users.find(u => u.id === id)); }); // In resolver: author: async (post) => userLoader.load(post.authorId) ``` Now 100 posts = 2 queries (all posts + all authors batched). ## Authentication patterns Three common approaches: **JWT in Authorization header:** ```javascript context: async ({ req }) => { const token = req.headers.authorization?.replace('Bearer ', ''); const user = token ? jwt.verify(token, JWT_SECRET) : null; return { user, db }; } ``` **Session cookie:** use express-session middleware before Apollo. **Hasura JWT mode:** Hasura validates JWT issued by your auth service (Auth0, Firebase Auth, custom). Claims map to PostgreSQL roles for row-level permissions. ## Rate limiting GraphQL's flexibility lets clients ask for deeply nested data — one query can become expensive. Defend: **Query complexity analysis** (graphql-query-complexity): ```javascript import { createComplexityRule } from 'graphql-query-complexity'; plugins: [{ async requestDidStart() { return { async didResolveOperation({ request, document }) { const complexity = getComplexity({ schema, query: document, variables: request.variables, estimators: [simpleEstimator({ defaultComplexity: 1 })], }); if (complexity > 1000) throw new Error(`Query too complex: ${complexity}`); }, }; } }] ``` **Depth limiting:** ```javascript import depthLimit from 'graphql-depth-limit'; validationRules: [depthLimit(7)] ``` ## Common pitfalls ## FAQ
Q Can I run GraphQL on shared hosting?

Apollo Server (Node.js) — yes on shared cPanel via "Setup Node.js App". Hasura — no, it needs Docker. Use VPS for Hasura.

Q GraphQL or gRPC for microservices?

gRPC for service-to-service (faster, binary, strict types). GraphQL for client-facing (flexible, introspectable, better DX).

Q Does GraphQL replace REST completely?

No. Use both — GraphQL for complex client queries, REST for simple webhooks / file uploads / health checks.

Q How do I version a GraphQL API?

You don't — you add fields and deprecate old ones (@deprecated(reason: "use newField")). GraphQL's schema evolves continuously without breaking clients.

Q Subscriptions — what VPS size?

4 GB+ for production WebSocket loads. Each connected client uses ~50 KB RAM baseline. 10,000 concurrent = ~500 MB just for connections.

GraphQL servers love the resources of a good VPS. View VPS plans

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket