GraphQL Project â Build a Complete Full-Stack Application
In this tutorial, you will learn about GraphQL Project. We cover key concepts, practical examples, and best practices to help you master this topic.
This project walks through building a complete full-stack GraphQL application â a Durga Antivirus Pro dashboard with schema design, resolvers, React frontend, and deployment.
What You'll Learn
You will build a production-ready GraphQL application from scratch: design the schema, implement Apollo Server with authentication and subscriptions, build a React dashboard with Apollo Client, and deploy to production.
Why This Project Matters
Theory is important, but building a complete GraphQL application ties everything together â schema design, resolvers, authentication, caching, subscriptions, testing, and deployment. DodaTech's engineering team built the Durga Antivirus Pro dashboard as a GraphQL application, and this project mirrors the real architecture.
flowchart TB
A["React Dashboard\n(Apollo Client)"] --> B["Apollo Server\n(GraphQL API)"]
B --> C["SQL Database\n(Users, Devices)"]
B --> D["Redis\n(Cache, Subscriptions)"]
B --> E["External APIs\n(Threat Intel)"]
A --> F["WebSocket\n(Subscriptions)"]
F --> B
style A fill:#dbeafe,stroke:#2563eb
style B fill:#fef3c7,stroke:#d97706
Prerequisites: All previous GraphQL tutorials. Node.js and React experience.
Project Structure
dodatech-graphql/
server/
src/
schema/
typeDefs.graphql
resolvers.js
context.js
loaders.js
server.js
package.json
client/
src/
components/
Dashboard.jsx
ThreatList.jsx
DeviceList.jsx
hooks/
useQueries.js
App.jsx
package.json
Step 1: Schema Design
# server/src/schema/typeDefs.graphql
enum Severity { LOW MEDIUM HIGH CRITICAL }
enum DeviceStatus { ONLINE OFFLINE QUARANTINED }
enum UserRole { ADMIN ANALYST VIEWER }
type User {
id: ID!
email: String!
displayName: String!
role: UserRole!
devices: [Device!]!
createdAt: DateTime!
}
type Device {
id: ID!
name: String!
os: String!
status: DeviceStatus!
user: User!
threats: [Threat!]!
scans: [Scan!]!
lastScan: DateTime
}
type Threat {
id: ID!
name: String!
severity: Severity!
detectedAt: DateTime!
resolvedAt: DateTime
device: Device!
}
type Scan {
id: ID!
device: Device!
status: ScanStatus!
threatsFound: Int!
startedAt: DateTime!
completedAt: DateTime
}
enum ScanStatus { PENDING RUNNING COMPLETED FAILED }
type Query {
me: User
devices(status: DeviceStatus): [Device!]!
device(id: ID!): Device
threats(severity: Severity, limit: Int = 20): [Threat!]!
threat(id: ID!): Threat
search(query: String!): [SearchResult!]!
}
union SearchResult = Device | Threat | User
input CreateDeviceInput { name: String! os: String! }
input UpdateDeviceInput { name: String os: String status: DeviceStatus }
type Mutation {
login(email: String!, password: String!): AuthPayload!
createDevice(input: CreateDeviceInput!): Device!
updateDevice(id: ID!, input: UpdateDeviceInput!): Device!
deleteDevice(id: ID!): Boolean!
reportThreat(name: String!, severity: Severity!, deviceId: ID!): Threat!
resolveThreat(id: ID!): Threat!
startScan(deviceId: ID!): Scan!
}
type AuthPayload {
token: String!
user: User!
}
type Subscription {
threatDetected(deviceId: ID): Threat!
scanCompleted(deviceId: ID): Scan!
deviceStatusChanged: Device!
}
Step 2: Apollo Server Implementation
// server/src/server.js
const { ApolloServer } = require('apollo-server');
const { loadFilesSync } = require('@graphql-tools/load-files');
const { mergeTypeDefs } = require('@graphql-tools/merge');
const typeDefs = mergeTypeDefs(loadFilesSync('src/schema/*.graphql'));
const resolvers = require('./resolvers');
const { createContext } = require('./context');
const { authDirectiveTransformer } = require('./directives/auth');
let schema = makeExecutableSchema({ typeDefs, resolvers });
schema = authDirectiveTransformer(schema);
const server = new ApolloServer({
schema,
context: createContext,
csrfPrevention: true,
introspection: process.env.NODE_ENV !== 'production',
cache: 'bounded',
plugins: [
responseCachePlugin(),
],
validationRules: [
depthLimit(7),
costAnalysis({ maximumCost: 1000, defaultCost: 1 }),
],
});
server.listen(4000).then(({ url }) => {
console.log(`DodaTech GraphQL API at ${url}`);
});
Step 3: React Client with Apollo
// client/src/App.jsx
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { createHttpLink } from '@apollo/client/link/http';
import { WebSocketLink } from '@apollo/client/link/ws';
import { split } from '@apollo/client/link';
import { getMainDefinition } from '@apollo/client/utilities';
import { Dashboard } from './components/Dashboard';
const httpLink = createHttpLink({ uri: 'http://localhost:4000/graphql' });
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token');
return {
headers: { ...headers, authorization: token ? `Bearer ${token}` : '' },
};
});
const wsLink = new WebSocketLink({
uri: 'ws://localhost:4000/graphql',
options: {
reconnect: true,
connectionParams: { authToken: localStorage.getItem('token') },
},
});
const link = split(
({ query }) => {
const def = getMainDefinition(query);
return def.kind === 'OperationDefinition' && def.operation === 'subscription';
},
wsLink,
authLink.concat(httpLink)
);
const client = new ApolloClient({ link, cache: new InMemoryCache() });
function App() {
return (
<ApolloProvider client={client}>
<Dashboard />
</ApolloProvider>
);
}
// client/src/components/Dashboard.jsx
import { useQuery, gql } from '@apollo/client';
const DASHBOARD_QUERY = gql`
query Dashboard {
me { id displayName email role }
devices { id name os status }
threats(severity: CRITICAL, limit: 10) {
id name severity detectedAt device { id name }
}
}
`;
function Dashboard() {
const { loading, error, data } = useQuery(DASHBOARD_QUERY);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorBanner error={error} />;
return (
<div className="dashboard">
<UserProfile user={data.me} />
<DeviceGrid devices={data.devices} />
<ThreatFeed threats={data.threats} />
<ScanProgress subscription />
</div>
);
}
Step 4: Real-Time Subscriptions
const THREAT_SUBSCRIPTION = gql`
subscription OnThreatDetected($deviceId: ID) {
threatDetected(deviceId: $deviceId) {
id name severity detectedAt device { id name }
}
}
`;
function ThreatFeed({ threats }) {
const { data: subscriptionData } = useSubscription(
THREAT_SUBSCRIPTION,
{ variables: { deviceId: selectedDeviceId } }
);
// Merge subscription updates into the feed
const allThreats = useMemo(() => {
const threatsList = [...threats];
if (subscriptionData?.threatDetected) {
threatsList.unshift(subscriptionData.threatDetected);
}
return threatsList.slice(0, 50);
}, [threats, subscriptionData]);
return (
<div className="threat-feed">
<h2>Live Threat Feed</h2>
{allThreats.map(threat => (
<ThreatCard key={threat.id} threat={threat} />
))}
</div>
);
}
Step 5: Testing
// server/src/__tests__/resolvers.test.js
describe('Mutation.reportThreat', () => {
it('creates a threat and publishes subscription event', async () => {
const mockPubsub = { publish: jest.fn() };
const mockDevice = { id: 'dev-001', userId: 'user-001' };
const result = await resolvers.Mutation.reportThreat(
null,
{ name: 'Emotet', severity: 'CRITICAL', deviceId: 'dev-001' },
{ user: { id: 'user-001' }, db: mockDb, pubsub: mockPubsub }
);
expect(result.name).toBe('Emotet');
expect(result.severity).toBe('CRITICAL');
expect(mockPubsub.publish).toHaveBeenCalledWith(
'THREAT_DETECTED',
expect.objectContaining({ threatDetected: expect.objectContaining({ name: 'Emotet' }) })
);
});
});
Common Mistakes
1. Overcomplicating the Schema
Start with 3-4 core types and add more as needed. A schema with 50 types on day one is overwhelming and likely wrong.
2. Not Having a Seed Script
Without seed data, development is slow. Write a seed script that populates the database with realistic test data.
3. Ignoring Error States in the Frontend
Every query can fail. Show loading states, error states, and empty states for every component. Never assume data always exists.
4. Forgetting to Handle Subscription Cleanup
Subscriptions leak if not cleaned up on component unmount. Apollo Client handles this with useSubscription but verify cleanup in tests.
5. Deploying Without Security Hardening
Before deploying, disable introspection, add Rate Limiting, configure CORS, and set up query cost analysis. A public API without these is vulnerable.
Practice Questions
- What is the recommended project structure for a GraphQL application?
- How do you handle authentication in the frontend?
- How do subscriptions work in React?
- What should a seed script include?
- What security measures are needed before deploying?
Answers:
/server(schema, resolvers, context, loaders) and/client(components, hooks, cache config). Keep GraphQL operations in.graphqlfiles for codegen.- Store the JWT in localStorage, create an authLink that reads it and sets the Authorization header, and pass the token in Websocket connectionParams for subscriptions.
- Use Apollo's
useSubscriptionhook with the subscription query. New events merge into the reactive data. Clean up withuseEffectreturn. - A seed script creates realistic test data with proper relationships â users with devices, devices with threats and scans. It runs before tests and development.
- Disable introspection, enable CSRF prevention, configure CORS, add rate limiting, set query depth/cost limits, enable HTTPS, and implement authentication.
Challenge: Complete the full Durga Antivirus Pro GraphQL project. Build all resolvers with DataLoader, implement authentication with JWT, add real-time subscriptions with Redis Pub/Sub, build the React dashboard with Apollo Client, write unit and integration tests, and deploy to production with security hardening.
FAQ
Mini Project
Complete the Durga Antivirus Pro GraphQL project. Implement all schema types with proper resolvers and DataLoaders. Build the React frontend with dashboard, device management, threat feed, and scan controls. Add real-time subscriptions for threat alerts and scan completion. Write comprehensive tests. Deploy with security hardening.
What's Next
| Topic | Description |
|---|---|
| RESTful APIs | Compare GraphQL with REST patterns |
| gRPC Guide | High-performance microservice communication |
| GraphQL Introduction | Review core GraphQL concepts |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro