GraphQL Introduction Explained: Schema, Queries & Resolvers for Beginners
GraphQL is a query language and runtime for APIs developed by Facebook in 2012 that lets clients request exactly the data they need through a single endpoint.
What You’ll Learn
- What GraphQL is and the problems it solves
- Schema Definition Language (SDL) basics
- How queries, mutations, and subscriptions work
- Running a GraphQL server with Apollo Server
- Using GraphiQL to explore and test queries
Why GraphQL Matters
REST APIs have a fundamental mismatch: the server decides what data to return, but the client knows what data it needs. GraphQL flips this — the client describes its data requirements, and the server fulfills them. DodaTech’s Durga Antivirus Pro dashboard has 12+ UI components, each needing different data from the same entities. With GraphQL, the dashboard fetches everything in one request, and each component’s data needs are explicit in the query.
flowchart LR
A["REST:\n/GET users\n/GET users/1/posts\n/GET users/1/posts/1/comments"] --> C["3 HTTP Requests\nOver-fetching at each"]
B["GraphQL:\nquery { user(id:1) {\n name\n posts { title\n comments { body } } } }"] --> D["1 Request\nExact data"]
style B fill:#dbeafe,stroke:#2563eb
style A fill:#fef3c7,stroke:#d97706
The Problem GraphQL Solves
Imagine building a mobile dashboard for Durga Antivirus Pro. The dashboard needs to show:
- User’s name and plan (from
GET /users/me) - List of devices (from
GET /devices) - Latest threats (from
GET /threats?limit=5) - For each device, its last scan time (from
GET /devices/{id})
With REST, this requires 4+ HTTP requests and returns more data than needed. With GraphQL:
query DashboardData {
me { name plan }
devices { id name os lastScan { completedAt } }
threats(limit: 5, orderBy: detectedAt_DESC) { id name severity }
}One request, one response, exactly the fields specified.
Schema Definition Language (SDL)
The schema is the heart of every GraphQL API. It defines what data is available and how to access it.
# Types define the shape of data
type Device {
id: ID!
name: String!
os: String!
lastScan: DateTime
threats: [Threat!]!
}
type Threat {
id: ID!
name: String!
severity: ThreatSeverity!
detectedAt: DateTime!
}
# Enums define a fixed set of values
enum ThreatSeverity {
LOW
MEDIUM
HIGH
CRITICAL
}
# The Query type defines entry points for reading data
type Query {
devices: [Device!]!
device(id: ID!): Device
threats(severity: ThreatSeverity, limit: Int): [Threat!]!
}Key symbols:
!means non-nullable — the field will always return a value[Threat!]!means a non-nullable array of non-nullable Threat objectsIDis a unique identifier (serialized as a string)DateTimeis a custom scalar for date/time values
Writing Your First GraphQL Server
const { ApolloServer, gql } = require('apollo-server');
// 1. Define the schema
const typeDefs = gql`
type Device {
id: ID!
name: String!
os: String!
}
type Query {
devices: [Device!]!
device(id: ID!): Device
}
`;
// 2. Mock data
const devices = [
{ id: '1', name: 'Office-PC', os: 'Windows 11' },
{ id: '2', name: 'Dev-Macbook', os: 'macOS 14' },
];
// 3. Define resolvers
const resolvers = {
Query: {
devices: () => devices,
device: (parent, args) => devices.find(d => d.id === args.id),
},
};
// 4. Start the server
const server = new ApolloServer({ typeDefs, resolvers });
server.listen(4000).then(() => {
console.log('GraphQL server at http://localhost:4000');
});Run this with node server.js and open http://localhost:4000 — you’ll see GraphiQL, an in-browser IDE for testing queries.
Queries with Arguments
# Query with required and optional arguments
query GetDevice {
device(id: "1") {
name
os
}
}
query GetCriticalThreats {
threats(severity: CRITICAL, limit: 10) {
id
name
detectedAt
}
}Resolver Arguments
Every resolver receives four arguments:
const resolvers = {
Query: {
threats: (parent, args, context, info) => {
// parent: result of the parent resolver (unused for top-level queries)
// args: { severity: 'CRITICAL', limit: 10 }
// context: shared context (auth user, database connection)
// info: query execution details (field names, selections)
let results = threats;
if (args.severity) {
results = results.filter(t => t.severity === args.severity);
}
return args.limit ? results.slice(0, args.limit) : results;
},
},
};Common Mistakes
1. Not Using a Schema-First Approach
Writing resolvers before defining types leads to inconsistent APIs. Always define your schema first — it’s the contract between client and server.
2. Ignoring the N+1 Problem
Nested resolvers (devices → threats) without DataLoader make N+1 database queries. Always batch child queries.
3. Returning Entire Database Objects
Resolvers should return API types, not database rows. Map database columns to GraphQL fields explicitly.
4. Not Handling Errors Properly
GraphQL returns errors alongside data. Always handle partial success — some resolvers may fail while others succeed.
Practice Questions
- What does the
!symbol mean in GraphQL schema? - What are the four arguments a resolver receives?
- Why does GraphQL use a single endpoint instead of multiple like REST?
- What is the N+1 problem and how do you solve it?
- How does GraphQL handle partial errors?
Answers:
- Non-nullable — the field will always return a value and can’t be null.
parent(parent resolver result),args(query arguments),context(shared app state),info(execution metadata).- A single endpoint lets the client describe exactly what data it needs. The server provides one URL; the query determines the response shape.
- With nested resolvers, fetching N parent items triggers N additional queries for children. Solved with DataLoader to batch child queries.
- GraphQL responses include both
data(partial results) anderrors(failed resolvers). Clients should check for both.
Challenge: Write a GraphQL schema and resolvers for a simple Durga Antivirus Pro API with User, Device, and Scan types. The User type should have a devices field that returns all devices owned by that user. Write a query that fetches a user with their devices and latest scan for each device.
FAQ
Try It Yourself
# Create a GraphQL server in 5 lines
mkdir graphql-demo && cd graphql-demo
npm init -y
npm install apollo-server graphql
cat > server.js << 'EOF'
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`type Query { hello: String }`;
const resolvers = { Query: { hello: () => 'Hello GraphQL!' } };
new ApolloServer({ typeDefs, resolvers }).listen(4000)
.then(() => console.log('Server running at http://localhost:4000'));
EOF
node server.jsVisit http://localhost:4000 and run { hello } in GraphiQL.
What’s Next
| Topic | Description |
|---|---|
| Types & Schema Design | Objects, scalars, enums, and relationships |
| Queries & Resolvers Deep Dive | Arguments, context, and data batching |
| Mutations & Input Types | Creating and modifying data |
| RESTful APIs | Compare GraphQL with REST for your use case |
What’s Next
Congratulations on completing this Graphql Introduction tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro