GraphQL Uncovered: Unlocking the Power of Efficient API

Table of contents
- Introduction
- What is GraphQL?
- Why should I use GraphQL
- Common Myths Busted
- GRAPHQL VS REST API
- Understanding Core Concepts
- Writing Your First Query
- Writing Your First Mutation
- Writing Your First Subscription
- Write Your First Schema
- Tools for Working with GraphQL
- Building a GraphQL Server with Apollo and Express
- Example 1: Sending Real-Time Notifications Using GraphQL
- Common Errors to Avoid
- Example 2 : Uploading Files Using GraphQL
- Common Errors to Avoid
- Conclusion

Introduction
In web development, efficient data fetching is essential for responsive applications. While REST APIs have been the traditional choice for client-server communication, their limitations often lead to over-fetching or under-fetching data. GraphQL emerges as a powerful alternative, offering enhanced flexibility and control.
What is GraphQL?
GraphQL, developed by Facebook and now maintained by a global community, is a modern API standard that allows clients to specify exactly what data they need. Unlike REST, which relies on multiple endpoints with fixed data structures, GraphQL exposes a single endpoint that responds with precisely the requested data.
Why should I use GraphQL
Flexible Data Fetching : Fetch exactly the data you need, avoiding over-fetching or under-fetching.
Single Endpoint: Access all resources through one endpoint, simplifying API management.
Strongly Typed Schema: Ensures clarity with built-in validation through a defined schema.
Real-Time Capabilities: Enable live updates with GraphQL Subscriptions for real-time data.
Better Developer Experience: Self-documenting APIs with interactive tools like GraphiQL.
Efficient for Modern Apps: Fetch nested and specific data in a single request for better performance.
Version-less API: Evolve APIs without versioning, using deprecation for backward compatibility.
Database Agnostic: Works with SQL, NoSQL, or any backend, adding flexibility to your stack.
Active Ecosystem: Leverage tools like Apollo and Relay with strong community support.
Common Myths Busted
Q. Is GraphQL a database language like SQL?
No, GraphQL is a query language for APIs, not databases. It is database-agnostic and applicable in any API context.
Q. Is GraphQL only for React developers?
No, GraphQL can be implemented in any programming language and is not limited to React.
Q. Does GraphQL replace REST?
Not necessarily. GraphQL and REST can coexist, with GraphQL serving as an abstraction layer over REST APIs.
GRAPHQL VS REST API
When to Use REST API
Simple CRUD Applications: Ideal for straightforward create, read, update, and delete operations.
Well-Defined Resources: Works well when resources and relationships are stable.
Cacheable Requests: RESTβs standard HTTP methods facilitate easy caching.
Existing Ecosystem: If your team is familiar with REST, it may be more practical to continue using it.
When to Use GraphQL
Complex Queries: Best for applications needing complex queries from multiple sources.
Client-Specific Data Needs: Allows different clients to request only the data they need.
Rapid Development: Facilitates quick iterations without extensive versioning.
Real-Time Applications: Supports subscriptions for real-time data updates.
Unified Data Access: Simplifies data access from multiple sources through a single API.
Understanding Core Concepts
GraphQL uses its own type system The Schema Definition Language (SDL)
to define API schemas. For example:
type Person {
name: String!
age: Int!
}
This defines a Person type with required fields name and age. Relationships can also be expressed:
type Post {
title: String!
author: Person!
}
Writing Your First Query
GraphQL APIs expose a single endpoint, allowing clients to specify their data needs. For example:
This query returns a list of names without additional fields. To include more data, simply adjust the query:
{
allPersons {
name
age
}
}
Writing Your First Mutation
Mutations allow clients to modify data. For example, to create a new Person:
mutation {
createPerson(name: "Bob", age: 36) {
name
age
}
}
This mutation returns the newly created person's details.
Writing Your First Subscription
When a new person is created, the server pushes the data to the client.
subscription {
newPerson {
name
age
}
}
Write Your First Schema
A GraphQL schema consists of types and root types:
type Query {
allPersons: [Person!]!
}
type Mutation {
createPerson(name: String!, age: Int!): Person!
}
type Subscription {
newPerson: Person!
}
type Person {
id: ID!
name: String!
age: Int!
}
Tools for Working with GraphQL
Apollo Server: An open-source GraphQL server for Node.js.
Apollo Client: A state management library for JavaScript.
GraphQL Playground & GraphiQL: Interactive IDEs for exploring APIs.
Prisma & Hasura: Tools for database access and instant GraphQL APIs.
Relay: A framework for building data-driven React applications with GraphQL.
Building a GraphQL Server with Apollo and Express
Step 1: Set Up Your Project
Create a new Node.js project:
mkdir graphql-server
cd graphql-server
npm init -y
Step 2: Install Dependencies
Install Express, Apollo Server, and GraphQL:
npm install express apollo-server-express graphql
Step 3: Create the Server
Create index.js and set up your GraphQL server:
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const app = express();
// Define your schema
const typeDefs = gql`
type Query {
hello: String
}
`;
// Define your resolvers
const resolvers = {
Query: {
hello: () => 'Hello, world!',
},
};
// Create an instance of ApolloServer
const server = new ApolloServer({ typeDefs, resolvers });
// Apply the Apollo GraphQL middleware to your Express server
server.start().then(res => {
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log(`Server ready at http://localhost:4000${server.graphqlPath}`)
);
});
Step 4: Run Your Server
Start your server:
node index.js
Access your GraphQL server at http://localhost:4000/graphql and run:
{
hello
}
This will return:
{
"data": {
"hello": "Hello, world!"
}
}
To further deepen our understanding, lets explore 2 examples
Example 1: Sending Real-Time Notifications Using GraphQL
1. Use GraphQL Subscriptions by setting up a subscription type in your schema for real-time updates.
2. Integrate a library like graphql-subscriptions or Apollo Server with a Pub/Sub system (e.g., PubSub).
3. Trigger events in your resolver (e.g., when data changes) to publish updates to subscribed clients.
4. Clients subscribe to the event using subscribe in their queries to receive real-time notifications.
import { createServer } from "http";
import express from "express";
import { ApolloServer, gql } from "apollo-server-express";
import { ApolloServerPluginDrainHttpServer } from "apollo-server-core";
import { PubSub } from "graphql-subscriptions";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { WebSocketServer } from "ws";
import { useServer } from "graphql-ws/lib/use/ws";
const PORT = 4000;
const pubsub = new PubSub();
// Schema definition
const typeDefs = gql`
type Query {
currentNumber: Int
}
type Subscription {
numberIncremented: Int
}
`;
// Resolver map
const resolvers = {
Query: {
currentNumber() {
return currentNumber;
},
},
Subscription: {
numberIncremented: {
subscribe: () => pubsub.asyncIterator(["NUMBER_INCREMENTED"]),
},
},
};
// Create schema, which will be used separately by ApolloServer and
// the WebSocket server.
const schema = makeExecutableSchema({ typeDefs, resolvers });
// Create an Express app and HTTP server; we will attach the WebSocket
// server and the ApolloServer to this HTTP server.
const app = express();
const httpServer = createServer(app);
// Set up WebSocket server.
const wsServer = new WebSocketServer({
server: httpServer,
path: "/graphql",
});
const serverCleanup = useServer({ schema }, wsServer);
// Set up ApolloServer.
const server = new ApolloServer({
schema,
plugins: [
// Proper shutdown for the HTTP server.
ApolloServerPluginDrainHttpServer({ httpServer }),
// Proper shutdown for the WebSocket server.
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
},
],
});
await server.start();
server.applyMiddleware({ app });
// Now that our HTTP server is fully set up, actually listen.
httpServer.listen(PORT, () => {
console.log(
`π Query endpoint ready at http://localhost:${PORT}${server.graphqlPath}`
);
console.log(
`π Subscription endpoint ready at ws://localhost:${PORT}${server.graphqlPath}`
);
});
// In the background, increment a number every second and notify subscribers when it changes.
let currentNumber = 0;
function incrementNumber() {
currentNumber++;
pubsub.publish("NUMBER_INCREMENTED", { numberIncremented: currentNumber });
setTimeout(incrementNumber, 1000);
}
// Start incrementing
incrementNumber();
Common Errors to Avoid
Ensure all necessary modules are correctly imported.
Ensure that the WebSocket server path is distinct if you plan to have multiple subscriptions or WebSocket functionalities.
If you are accessing the GraphQL server from a different origin (e.g., a frontend application), you may encounter CORS (Cross-Origin Resource Sharing) issues. You can enable CORS in your Express app:
Example 2 : Uploading Files Using GraphQL
1. Install required packages like graphql-upload and set up middleware in your server (e.g., app.use(graphqlUploadExpress())).
2. Define a custom scalar type for Upload in your GraphQL schema.
3. Use resolvers to handle file uploads, where the Upload scalar resolves to a file stream.
4. Process the file stream (e.g., save it to the server or a cloud service) and return the file metadata in the response.
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const {
GraphQLUpload,
graphqlUploadExpress, // A Koa implementation is also exported.
} = require('graphql-upload');
const { finished } = require('stream/promises');
const {
ApolloServerPluginLandingPageLocalDefault,
} = require('apollo-server-core');
const typeDefs = gql`
# The implementation for this scalar is provided by the
# 'GraphQLUpload' export from the 'graphql-upload' package
# in the resolver map below.
scalar Upload
type File {
filename: String!
mimetype: String!
encoding: String!
}
`;
type Query {
# This is only here to satisfy the requirement that at least one
# field be present within the 'Query' type. This example does not
# demonstrate how to fetch uploads back.
otherFields: Boolean!
}
type Mutation {
# Multiple uploads are supported. See graphql-upload docs for details.
singleUpload(file: Upload!): File!
}
const resolvers = {
// This maps the `Upload` scalar to the implementation provided
// by the `graphql-upload` package.
Upload: GraphQLUpload,
Mutation: {
singleUpload: async (parent, { file }) => {
const { createReadStream, filename, mimetype, encoding } = await file;
// Invoking the `createReadStream` will return a Readable Stream.
// See https://nodejs.org/api/stream.html#stream_readable_streams
const stream = createReadStream();
// This is purely for demonstration purposes and will overwrite the
// local-file-output.txt in the current working directory on EACH upload.
const out = require('fs').createWriteStream('local-file-output.txt');
stream.pipe(out);
await finished(out);
return { filename, mimetype, encoding };
},
},
};
async function startServer() {
const server = new ApolloServer({
typeDefs,
resolvers,
// Using graphql-upload without CSRF prevention is very insecure.
csrfPrevention: true,
cache: 'bounded',
plugins: [
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});
await server.start();
const app = express();
// This middleware should be added before calling `applyMiddleware`.
app.use(graphqlUploadExpress());
server.applyMiddleware({ app });
await new Promise<void>(r => app.listen({ port: 4000 }, r));
console.log(`π Server ready at http://localhost:4000${server.graphqlPath}`);
}
startServer();
Common Errors to Avoid
Ensure all necessary modules are correctly imported.
Ensure that the server has the necessary permissions to write to the file system. If the server cannot write to the specified file path, it will throw an error.
const out = require('fs').createWriteStream('local-file-output.txt');
- Ensure that CSRF prevention is correctly configured. Misconfigurations can lead to security vulnerabilities or issues with handling requests.
csrfPrevention: true,
Conclusion
In this blog, we explored the powerful capabilities of GraphQL, particularly in the context of building efficient APIs that cater to modern application needs. We discussed how GraphQL addresses the limitations of traditional REST APIs by allowing clients to request exactly the data they need, thereby optimizing data fetching and reducing over-fetching or under-fetching issues. Through practical examples, we demonstrated how to implement a GraphQL server with real-time subscriptions and file uploads, showcasing the flexibility and responsiveness that GraphQL offers.
By leveraging tools like Apollo Server and Express, developers can create robust applications that not only provide a seamless user experience but also facilitate rapid development and iteration. As the demand for real-time data and efficient data management continues to grow, GraphQL stands out as a compelling choice for developers looking to build scalable and maintainable applications. Embracing GraphQL can significantly enhance your development workflow and empower you to create more dynamic and interactive web experiences.
Subscribe to my newsletter
Read articles from Lavanya Varshney directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
