Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions src/app/llms.txt/render-llms-txt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import { pagesBySection } from "@/components/learn-aggregator/learn-pages"

const SITE_URL = "https://graphql.org"

interface LlmsLink {
title: string
url: string
description: string
}

interface LlmsSection {
title: string
links: LlmsLink[]
}

const sections: LlmsSection[] = [
{
title: "Start Here",
links: [
{
title: "GraphQL.org",
url: `${SITE_URL}/`,
description:
"The official website for GraphQL documentation, resources, and community information.",
},
{
title: "Learn GraphQL",
url: `${SITE_URL}/learn/`,
description:
"A guided path through GraphQL fundamentals, best practices, and schema governance.",
},
{
title: "GraphQL Specification",
url: "https://spec.graphql.org/",
description: "The normative definition of the GraphQL language.",
},
{
title: "Frequently Asked Questions",
url: `${SITE_URL}/faq/`,
description:
"Answers to common questions about GraphQL, its specification, and its ecosystem.",
},
],
},
learnSection("Learn: Core Concepts", "getting-started"),
learnSection("Learn: Best Practices", "best-practices"),
learnSection("Learn: Schema Governance", "schema-governance"),
{
title: "Implementations and Resources",
links: [
{
title: "Resource Hub",
url: `${SITE_URL}/resources/`,
description:
"Curated GraphQL tools, videos, articles, and learning resources.",
},
{
title: "Frontend Resources",
url: `${SITE_URL}/resources/frontend/`,
description:
"GraphQL clients, frontend techniques, videos, and articles.",
},
{
title: "Backend Resources",
url: `${SITE_URL}/resources/backend/`,
description: "GraphQL server implementations and production guidance.",
},
{
title: "Federation Resources",
url: `${SITE_URL}/resources/federation/`,
description:
"Resources for composing and operating distributed GraphQL schemas.",
},
{
title: "Security Resources",
url: `${SITE_URL}/resources/security/`,
description: "Guidance and tools for securing GraphQL APIs.",
},
{
title: "AI Resources",
url: `${SITE_URL}/resources/ai/`,
description:
"Patterns, tools, and implementations for using GraphQL with AI systems.",
},
{
title: "Monitoring Resources",
url: `${SITE_URL}/resources/monitoring/`,
description:
"Tools and guidance for monitoring GraphQL performance, usage, and schema changes.",
},
{
title: "Tools and Libraries",
url: `${SITE_URL}/community/tools-and-libraries/`,
description:
"Community-maintained GraphQL clients, servers, tools, and services organized by language and purpose.",
},
{
title: "GraphQL.js",
url: "https://www.graphql-js.org/docs/",
description:
"Documentation for the JavaScript reference implementation of GraphQL.",
},
],
},
{
title: "Project and Community",
links: [
{
title: "How GraphQL Is Developed",
url: `${SITE_URL}/community/contribute/how-graphql-is-developed/`,
description:
"How the specification, working groups, implementations, and tools evolve.",
},
{
title: "Project Governance",
url: `${SITE_URL}/community/contribute/governance/`,
description:
"Technical governance of the GraphQL project and its relationship to the GraphQL Foundation.",
},
{
title: "Project Meetings",
url: `${SITE_URL}/community/contribute/project-meetings/`,
description:
"Working group and subcommittee meetings for GraphQL development.",
},
{
title: "Contributing to GraphQL",
url: `${SITE_URL}/community/contribute/essential-links/`,
description:
"Essential links for participating in the GraphQL project.",
},
{
title: "Official Channels",
url: `${SITE_URL}/community/official-channels/`,
description: "Official GraphQL community and support channels.",
},
{
title: "GraphQL Foundation",
url: `${SITE_URL}/community/foundation/`,
description:
"The neutral foundation supporting the GraphQL community and ecosystem.",
},
],
},
{
title: "Optional",
links: [
{
title: "GraphQL Blog",
url: `${SITE_URL}/blog/`,
description:
"Project announcements, technical articles, and community news.",
},
{
title: "GraphQL Blog RSS Feed",
url: `${SITE_URL}/blog/rss.xml`,
description: "RSS feed for GraphQL blog posts.",
},
{
title: "Community Events",
url: `${SITE_URL}/community/events/`,
description: "GraphQL community events and meetups.",
},
{
title: "Community Events RSS Feed",
url: `${SITE_URL}/community/events/rss.xml`,
description: "RSS feed for GraphQL community events.",
},
{
title: "GraphQLConf 2026",
url: `${SITE_URL}/conf/2026/`,
description: "The official GraphQL conference.",
},
{
title: "GraphQL Days",
url: `${SITE_URL}/day/`,
description: "GraphQL community events held around the world.",
},
{
title: "Brand Guidelines",
url: `${SITE_URL}/brand/`,
description: "Guidance for using the GraphQL name and visual identity.",
},
{
title: "Code of Conduct",
url: `${SITE_URL}/codeofconduct/`,
description: "The code of conduct governing the GraphQL community.",
},
],
},
]

export function renderLlmsTxt(): string {
const introduction = [
"# GraphQL",
"",
"> GraphQL is a query language for APIs and a runtime for executing queries using a type system defined for your data.",
"",
"GraphQL.org is the official website for GraphQL. The GraphQL Specification is normative; the Learn materials explain the language and provide non-normative guidance for building and operating GraphQL APIs.",
"",
"GraphQL is language-neutral. GraphQL.js is the JavaScript reference implementation, not the GraphQL language itself. Listings of third-party tools, libraries, services, and training resources are informational and are not endorsements.",
]

return [...introduction, ...sections.flatMap(formatSection), ""].join("\n")
}

function learnSection(
title: string,
section: keyof typeof pagesBySection,
): LlmsSection {
return {
title,
links: pagesBySection[section].map(page => ({
title: page.title,
url: new URL(page.href, SITE_URL).href,
description: page.description,
})),
}
}

function formatSection({ title, links }: LlmsSection): string[] {
return [
"",
`## ${title}`,
"",
...links.map(link => `- [${link.title}](${link.url}): ${link.description}`),
]
}
60 changes: 60 additions & 0 deletions src/app/llms.txt/route.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { strict as assert } from "node:assert"
import { describe, it } from "node:test"

import { pagesBySection } from "@/components/learn-aggregator/learn-pages"

import { renderLlmsTxt } from "./render-llms-txt"
import { dynamic, GET } from "./route"

describe("llms.txt", () => {
it("is generated as a static plain-text response", async () => {
assert.strictEqual(dynamic, "force-static")

const response = GET()

assert.strictEqual(response.status, 200)
assert.strictEqual(
response.headers.get("content-type"),
"text/plain; charset=utf-8",
)
assert.strictEqual(await response.text(), renderLlmsTxt())
})

it("follows the llms.txt structure and includes every Learn page", () => {
const content = renderLlmsTxt()

assert.match(content, /^# GraphQL\n\n>/)
assert.match(content, /\n## Start Here\n/)
assert.match(content, /\n## Learn: Core Concepts\n/)
assert.match(content, /\n## Learn: Best Practices\n/)
assert.match(content, /\n## Learn: Schema Governance\n/)
assert.match(content, /\n## Implementations and Resources\n/)
assert.match(content, /\n## Project and Community\n/)
assert.match(content, /\n## Optional\n/)

for (const pages of Object.values(pagesBySection)) {
for (const page of pages) {
const url = new URL(page.href, "https://graphql.org").href
assert.ok(content.includes(`- [${page.title}](${url}):`))
}
}
})

it("uses absolute canonical URLs instead of known redirect routes", () => {
const urls = Array.from(
renderLlmsTxt().matchAll(/\]\((https?:\/\/[^)]+)\)/g),
match => match[1],
)

assert.ok(urls.length > 0)
assert.ok(urls.every(url => URL.canParse(url)))
assert.ok(!urls.includes("https://graphql.org/code/"))
assert.ok(!urls.includes("https://graphql.org/community/"))
assert.ok(!urls.includes("https://graphql.org/foundation/"))
assert.ok(!urls.includes("https://graphql.org/graphql-js/"))
assert.ok(
urls.includes("https://graphql.org/community/tools-and-libraries/"),
)
assert.ok(urls.includes("https://www.graphql-js.org/docs/"))
})
})
11 changes: 11 additions & 0 deletions src/app/llms.txt/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { renderLlmsTxt } from "./render-llms-txt"

export const dynamic = "force-static"

export function GET() {
return new Response(renderLlmsTxt(), {
headers: {
"Content-Type": "text/plain; charset=utf-8",
},
})
}