<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GraphQL Schema Architecture</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,sans-serif;
background:#0a0a0f;color:#e2e8f0;
display:flex;justify-content:center;align-items:flex-start;
min-height:100vh;padding:24px 16px;
}
.card{
width:100%;max-width:820px;
background:linear-gradient(145deg,rgba(15,15,25,0.95),rgba(10,10,18,0.98));
border:1px solid rgba(255,255,255,0.08);border-radius:16px;
overflow:hidden;
}
.card-header{
padding:28px 32px 20px;
border-bottom:1px solid rgba(255,255,255,0.06);
}
.card-header h1{font-size:1.35rem;font-weight:700;color:#f1f5f9;margin-bottom:6px}
.card-header p{font-size:0.82rem;color:#94a3b8;line-height:1.5}
.badge-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.badge{
font-size:0.68rem;padding:3px 10px;border-radius:20px;
background:rgba(34,211,238,0.1);color:#22d3ee;
border:1px solid rgba(34,211,238,0.2);
}
.body{padding:24px 32px 28px}
.section-title{
font-size:0.78rem;font-weight:700;color:#22d3ee;
text-transform:uppercase;letter-spacing:0.08em;margin-bottom:12px;
}
/* โโ Side-by-side approaches โโ */
.approaches{
display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:28px;
}
.approach{
background:rgba(0,0,0,0.3);border:1px solid rgba(255,255,255,0.06);
border-radius:12px;overflow:hidden;
}
.approach-header{
padding:12px 16px;
border-bottom:1px solid rgba(255,255,255,0.05);
display:flex;align-items:center;gap:8px;
}
.approach-header .dot{
width:8px;height:8px;border-radius:50%;
}
.approach-header h3{font-size:0.75rem;font-weight:600;color:#f1f5f9}
.approach-header .rec{
font-size:0.58rem;padding:2px 7px;border-radius:10px;
background:rgba(74,222,128,0.12);color:#4ade80;
border:1px solid rgba(74,222,128,0.25);margin-left:auto;
}
.tree{
padding:14px 16px;
font-family:'SF Mono',SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;
font-size:0.68rem;line-height:1.7;overflow-x:auto;white-space:pre;
color:#cbd5e1;
}
.tree .folder{color:#22d3ee;font-weight:600}
.tree .file{color:#e2e8f0}
.tree .comment{color:#64748b;font-style:italic}
/* โโ Schema-first vs Code-first โโ */
.compare{
display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:28px;
}
.compare-box{
background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06);
border-radius:10px;padding:14px 16px;
}
.compare-box h4{font-size:0.72rem;margin-bottom:6px;font-weight:600}
.compare-box h4.schema-first{color:#a78bfa}
.compare-box h4.code-first{color:#f472b6}
.compare-box p{font-size:0.66rem;color:#94a3b8;line-height:1.5}
.compare-box .tools{
margin-top:8px;font-size:0.62rem;color:#64748b;
}
.compare-box .tools strong{color:#94a3b8}
/* โโ Request flow โโ */
.flow{
display:flex;align-items:center;justify-content:center;flex-wrap:wrap;
gap:4px;margin-bottom:28px;
background:rgba(0,0,0,0.25);border:1px solid rgba(255,255,255,0.06);
border-radius:10px;padding:18px 14px;
}
.flow-step{
font-size:0.68rem;font-weight:600;
padding:6px 12px;border-radius:8px;
background:rgba(34,211,238,0.08);border:1px solid rgba(34,211,238,0.2);
color:#22d3ee;white-space:nowrap;
}
.flow-arrow{color:#475569;font-size:0.8rem}
/* โโ N+1 section โโ */
.n1-box{
background:rgba(250,204,21,0.04);border:1px solid rgba(250,204,21,0.15);
border-radius:10px;padding:16px 20px;margin-bottom:24px;
}
.n1-box h4{font-size:0.74rem;color:#facc15;margin-bottom:6px}
.n1-box p{font-size:0.68rem;color:#94a3b8;line-height:1.6}
.n1-box code{
font-family:'SF Mono',Consolas,monospace;font-size:0.65rem;
background:rgba(0,0,0,0.3);padding:2px 6px;border-radius:4px;color:#e2e8f0;
}
/* โโ Sources โโ */
.sources{
border-top:1px solid rgba(255,255,255,0.06);
padding:16px 32px;
}
.sources h4{font-size:0.68rem;color:#64748b;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.06em}
.sources a{
display:inline-block;font-size:0.7rem;color:#3b82f6;
text-decoration:none;margin-right:16px;margin-bottom:4px;
transition:color 0.2s;
}
.sources a:hover{color:#60a5fa;text-decoration:underline}
@media(max-width:640px){
.body{padding:18px 16px 22px}
.approaches,.compare{grid-template-columns:1fr}
.card-header{padding:20px 16px 16px}
.sources{padding:14px 16px}
.flow{gap:3px}
.flow-step{font-size:0.6rem;padding:5px 8px}
}
</style>
</head>
<body>
<div class="card">
<!-- Header -->
<div class="card-header">
<h1>GraphQL Schema Architecture</h1>
<p>Schema-first vs code-first approaches with domain-based module organization and resolver patterns.</p>
<div class="badge-row">
<span class="badge">GraphQL</span>
<span class="badge">Apollo</span>
<span class="badge">Yoga</span>
<span class="badge">TypeScript</span>
</div>
</div>
<div class="body">
<!-- Two approaches side by side -->
<div class="section-title">Project Structure</div>
<div class="approaches">
<!-- Domain-based -->
<div class="approach">
<div class="approach-header">
<div class="dot" style="background:#4ade80"></div>
<h3>Domain-based</h3>
<span class="rec">Recommended</span>
</div>
<div class="tree"><span class="folder">src/</span>
โโโ <span class="folder">modules/</span>
โ โโโ <span class="folder">users/</span>
โ โ โโโ <span class="file">schema.graphql</span>
โ โ โโโ <span class="file">resolvers.ts</span>
โ โ โโโ <span class="file">datasource.ts</span>
โ โ โโโ <span class="file">types.ts</span>
โ โโโ <span class="folder">posts/</span>
โ โ โโโ <span class="file">schema.graphql</span>
โ โ โโโ <span class="file">resolvers.ts</span>
โ โ โโโ <span class="file">datasource.ts</span>
โ โโโ <span class="file">index.ts</span> <span class="comment">(Merge schemas)</span>
โโโ <span class="file">context.ts</span> <span class="comment">(Auth, DB, loaders)</span>
โโโ <span class="file">server.ts</span> <span class="comment">(Apollo/Yoga setup)</span>
โโโ <span class="file">schema.ts</span> <span class="comment">(Combined schema)</span></div>
</div>
<!-- Role-based -->
<div class="approach">
<div class="approach-header">
<div class="dot" style="background:#f59e0b"></div>
<h3>Role-based</h3>
</div>
<div class="tree"><span class="folder">src/</span>
โโโ <span class="folder">typeDefs/</span>
โ โโโ <span class="file">user.graphql</span>
โ โโโ <span class="file">post.graphql</span>
โโโ <span class="folder">resolvers/</span>
โ โโโ <span class="file">userResolver.ts</span>
โ โโโ <span class="file">postResolver.ts</span>
โโโ <span class="folder">dataSources/</span>
โ โโโ <span class="file">userAPI.ts</span>
โโโ <span class="file">server.ts</span></div>
</div>
</div>
<!-- Schema-first vs Code-first -->
<div class="section-title">Schema-first vs Code-first</div>
<div class="compare">
<div class="compare-box">
<h4 class="schema-first">Schema-first (SDL)</h4>
<p>Write <code>.graphql</code> files defining types, then generate TypeScript types. Clear contract, great for team collaboration.</p>
<div class="tools"><strong>Tools:</strong> Apollo Server, GraphQL Yoga, graphql-tools</div>
</div>
<div class="compare-box">
<h4 class="code-first">Code-first (Programmatic)</h4>
<p>Define schema using TypeScript decorators or builder APIs. Single source of truth with full IDE support and no codegen step.</p>
<div class="tools"><strong>Tools:</strong> TypeGraphQL, Nexus, Pothos</div>
</div>
</div>
<!-- Request Flow -->
<div class="section-title">Request Flow</div>
<div class="flow">
<span class="flow-step">Query</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Parse</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Validate</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Resolver</span>
<span class="flow-arrow">→</span>
<span class="flow-step">DataSource</span>
<span class="flow-arrow">→</span>
<span class="flow-step">Response</span>
</div>
<!-- N+1 Problem -->
<div class="n1-box">
<h4>N+1 Problem & DataLoader</h4>
<p>Fetching 50 users with posts = <code>1 + 50</code> queries without batching. <strong style="color:#e2e8f0">DataLoader</strong> batches and deduplicates per-request: collects all IDs in a single tick, fires one batched query, then distributes results back to each resolver.</p>
</div>
</div>
<!-- Sources -->
<div class="sources">
<h4>Sources</h4>
<a href="https://graphql.org/learn/schema/" target="_blank" rel="noopener">GraphQL.org โ Schema & Types</a>
<a href="https://www.apollographql.com/docs/apollo-server/schema/schema" target="_blank" rel="noopener">Apollo Server โ Schema</a>
<a href="https://the-guild.dev/graphql/yoga-server" target="_blank" rel="noopener">GraphQL Yoga</a>
<a href="https://github.com/graphql/dataloader" target="_blank" rel="noopener">DataLoader</a>
</div>
</div>
</body>
</html>