73 Building an Auto-docs System in graphql-go
73 Building an Auto-docs System in graphql-go
As an organization grows, one thing that typically expands quickly (and often poorly) is API documentation. GraphQL offers an advantage with its built-in schema introspection, yet good documentation still needs to be enriched with business explanations and real-world examples. In this article, I want to walk you through building an auto-docs system —automated documentation— for a GraphQL API based on graphql-go. With this approach, we aim to reduce the gap between code and documentation: whatever changes in the code is automatically reflected in the docs.
Why Auto-Docs?
Auto-docs for GraphQL is nothing new. Platforms like Apollo, GraphQL Voyager, and GraphiQL already provide plenty of exploration tools. However, challenges arise when we:
- Want to embed the documentation in our internal style (our own developer portal).
- Need to add business details, remarks, or scenarios.
- Want to provide up-to-date documentation (not a copy-paste of last year’s schema).
If you use graphql-go
, the introspection function is already available—but the result is still raw data that isn’t very informative for end-users or developers.
Auto-Docs System Architecture
Before implementation, let’s lay out a simple flow diagram below:
flowchart TD
A[Source Code] --> B(GraphQL Schema & Resolvers)
B --> C[Introspection Query]
C --> D[Schema JSON]
D --> E[Docs Generator]
E --> F[Human-friendly HTML/Markdown Docs]
A simple explanation:
- Source Code: you define the schema and resolvers in Go.
- Introspection Query: the tool runs an introspection query against the
*graphql.Schema. - Docs Generator: a builder script that reads the introspection result + custom annotations from your code.
- Output: friendly documentation in HTML/Markdown that can be deployed.
Let’s break it down step by step.
Setting up the graphql-go Schema
Typically, you’d set up a schema like this:
1import "github.com/graphql-go/graphql"
2
3var userType = graphql.NewObject(graphql.ObjectConfig{
4 Name: "User",
5 Fields: graphql.Fields{
6 "id": &graphql.Field{
7 Type: graphql.String,
8 Description: "Unique identifier of the user",
9 },
10 "name": &graphql.Field{
11 Type: graphql.String,
12 Description: "The user's full name",
13 },
14 },
15})
16
17var rootQuery = graphql.NewObject(graphql.ObjectConfig{
18 Name: "Query",
19 Fields: graphql.Fields {
20 "user": &graphql.Field{
21 Type: userType,
22 Description: "Find a user by ID",
23 Args: graphql.FieldConfigArgument{
24 "id": &graphql.ArgumentConfig{
25 Type: graphql.NewNonNull(graphql.String),
26 },
27 },
28 Resolve: /* implement resolver... */
29 },
30 },
31})
32
33var schema, _ = graphql.NewSchema(graphql.SchemaConfig{
34 Query: rootQuery,
35})Note: The Description on each type/field is key to making our documentation informative.
Running Introspection to Obtain the Schema
To build auto-docs, we first need introspection data.
Create a GraphQL endpoint that accepts the following introspection query:
1query IntrospectionQuery {
2 __schema {
3 types {
4 name
5 description
6 fields {
7 name
8 description
9 args {
10 name
11 description
12 type {
13 name
14 kind
15 }
16 }
17 type {
18 name
19 kind
20 }
21 }
22 }
23 }
24}For example, in Go:
1params := graphql.Params{
2 Schema: schema,
3 RequestString: introspectionQuery,
4}
5
6result := graphql.Do(params)
7resultJSON, _ := json.MarshalIndent(result, "", " ")
8fmt.Println(string(resultJSON))The output is JSON that you can consume for the next steps.
Building the Auto-Docs Generator
The next key part is the generator—a script that turns the introspection JSON into clean Markdown/HTML files. Here is a simple example in Go that converts the introspection output into Markdown:
1package main
2
3import (
4 "encoding/json"
5 "io/ioutil"
6 "os"
7)
8
9type IntrospectionResult struct {
10 Data struct {
11 Schema struct {
12 Types []struct {
13 Name string `json:"name"`
14 Description string `json:"description"`
15 Fields []struct {
16 Name string `json:"name"`
17 Description string `json:"description"`
18 } `json:"fields"`
19 } `json:"types"`
20 } `json:"__schema"`
21 } `json:"data"`
22}
23
24func main() {
25 // Read the introspection result file
26 data, _ := ioutil.ReadFile("schema.json")
27 var result IntrospectionResult
28 json.Unmarshal(data, &result)
29
30 f, _ := os.Create("DOCS.md")
31 defer f.Close()
32
33 f.WriteString("# API Documentation\n")
34 for _, t := range result.Data.Schema.Types {
35 if t.Description != "" && len(t.Fields) > 0 {
36 f.WriteString("## " + t.Name + "\n" + t.Description + "\n")
37 for _, field := range t.Fields {
38 f.WriteString("- **" + field.Name + "**: " + field.Description + "\n")
39 }
40 f.WriteString("\n")
41 }
42 }
43}The result is a Markdown document that auto-updates whenever the schema changes.
Adding Custom Annotations (Simulation)
You may want to add examples, formats, or other remarks. For this, our team typically uses a pattern of embedding custom tags into the Description:
1var userType = graphql.NewObject(graphql.ObjectConfig{
2 Name: "User",
3 Fields: graphql.Fields{
4 "email": &graphql.Field{
5 Type: graphql.String,
6 Description: `The user's main email.
7@format: email
8@example: user@example.com
9@remark: Only verified emails shown.`,
10 },
11 },
12})Then, in the docs generator, you can do a simple parse by splitting on @ to group that information. This lets you scale your documentation without having to change the schema structure.
Simulation Result and Documentation Table
Let’s simulate the docs output:
| Field | Description | Format | Example | Remark |
|---|---|---|---|---|
| The user’s main email | user@example.com | Only verified emails shown. | ||
| name | The user’s full name | (none) | (none) | (none) |
This is far more helpful for frontend developers or QA than reading raw introspection alone.
Auto-Docs CI/CD Pipeline
Best practice: this auto-docs process should be automated within a CI/CD pipeline.
graph TD
CodeBase --> Build
Build --> Execute_Introspection
Execute_Introspection --> Generate_Docs
Generate_Docs --> DeployDocs[Deploy to Docs Portal]
- Build: Build the code & schema.
- Execute_Introspection: Automate dumping the schema JSON.
- Generate_Docs: Run the generator to output HTML/Markdown.
- Deploy: Push to the documentation portal (could be GitHub Pages, S3, a web portal, etc.).
This way, the docs are always up-to-date, with no need for manual copy-pasting.
Conclusion: Auto-Docs, Simple but Powerful
The road to good API documentation isn’t complete with auto-docs alone. This approach does not replace manual documentation best practices, especially for business explanations, edge-case scenarios, and relationships between resources.
That said, from experience, an auto-docs system like this:
- Cuts the lead time for updating docs when the schema changes,
- Reduces confusion between teams,
- Reduces bugs caused by documentation being out of sync with the implementation.
Integrating an auto-docs system is highly feasible with graphql-go without adding heavy dependencies. This framework can even serve as a foundation for a more advanced developer portal in the future.
Have experience or other auto-docs tips? Share them in the comments! 🚀