Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 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
25 changes: 25 additions & 0 deletions .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,32 @@
- uses: ./.github/actions/setup_node_environment
- run: pnpm test

openapi-sync-check:
name: "OpenAPI Spec Sync Check"
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_node_environment

- name: Generate OpenAPI spec from production
run: pnpm --filter ensapi openapi:generate

- name: Format generated spec with Biome
run: pnpm biome format --write docs/docs.ensnode.io/openapi.json

- name: Verify OpenAPI spec matches production
run: |
if git diff --exit-code docs/docs.ensnode.io/openapi.json; then
echo "✅ OpenAPI spec is in sync with production"
else
echo "❌ OpenAPI spec is out of sync with production"
echo ""
echo "The committed openapi.json differs from the production API."
echo "Run 'pnpm --filter ensapi openapi:generate' and commit the changes."
exit 1
fi

integrity-check:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium test

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
name: "Integrity Check"
runs-on: blacksmith-4vcpu-ubuntu-2204
services:
Expand Down
3 changes: 2 additions & 1 deletion apps/ensapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"test": "vitest",
"lint": "biome check --write .",
"lint:ci": "biome ci",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"openapi:generate": "tsx scripts/generate-openapi.ts"
},
"dependencies": {
"@ensdomains/ensjs": "^4.0.2",
Expand Down
51 changes: 51 additions & 0 deletions apps/ensapi/scripts/generate-openapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env tsx

/**
* Generate OpenAPI spec from a running ENSApi instance.
*
* Usage:
* pnpm openapi:generate # Uses default URL (production)
* pnpm openapi:generate http://localhost:3223 # Uses custom URL
* ENSAPI_URL=http://localhost:3223 pnpm openapi:generate
*
* Output:
* Writes openapi.json to the docs directory for Mintlify to consume.
* Run `pnpm biome format --write docs/docs.ensnode.io/openapi.json` after to format.
*/

import { writeFileSync } from "node:fs";
import { resolve } from "node:path";

const DEFAULT_ENSAPI_URL = "https://api.alpha.ensnode.io";
const OUTPUT_PATH = resolve(import.meta.dirname, "../../../docs/docs.ensnode.io/openapi.json");
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import.meta.dirname property is not supported in all Node.js versions. It was introduced in Node.js 20.11.0. Consider using __dirname or new URL('.', import.meta.url).pathname for better compatibility, or ensure your Node.js version requirement is documented.

Suggested change
import { resolve } from "node:path";
const DEFAULT_ENSAPI_URL = "https://api.alpha.ensnode.io";
const OUTPUT_PATH = resolve(import.meta.dirname, "../../../docs/docs.ensnode.io/openapi.json");
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const DEFAULT_ENSAPI_URL = "https://api.alpha.ensnode.io";
const __dirname = dirname(fileURLToPath(import.meta.url));
const OUTPUT_PATH = resolve(__dirname, "../../../docs/docs.ensnode.io/openapi.json");

Copilot uses AI. Check for mistakes.

async function main() {
// Get URL from argument or environment variable
const ensapiUrl = process.argv[2] || process.env.ENSAPI_URL || DEFAULT_ENSAPI_URL;
const openapiUrl = `${ensapiUrl}/openapi.json`;

console.log(`Fetching OpenAPI spec from: ${openapiUrl}`);

const response = await fetch(openapiUrl);

if (!response.ok) {
console.error(`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`);
process.exit(1);
}

const spec = await response.json();

// Pretty-print the JSON for readability in git diffs
const content = `${JSON.stringify(spec, null, 2)}\n`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my other comment that suggested we might apply formatting within the API route handler such that it's not necessary to perform additional formatting of the response.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting is correct for the API response as JSON.stirngify, so we should really only be using Biome in our CI check to validate they're equal by formatting them both there. Or do I not understand the request here.


writeFileSync(OUTPUT_PATH, content, "utf-8");

console.log(`OpenAPI spec written to: ${OUTPUT_PATH}`);
console.log(`Spec version: ${spec.info?.version}`);
console.log(`Paths: ${Object.keys(spec.paths || {}).length}`);
}

main().catch((error) => {
console.error("Error generating OpenAPI spec:", error);
process.exit(1);
});
60 changes: 42 additions & 18 deletions docs/docs.ensnode.io/README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,58 @@
# ENSNode Documentation
# ENSNode API Documentation

[docs.ensnode.io](https://docs.ensnode.io) runs on [Mintlify](https://mintlify.com).
[docs.ensnode.io](https://docs.ensnode.io) hosts the ENSApi reference documentation.

## Local Development
Learn more about [ENSNode](https://ensnode.io) from [the ENSNode docs](https://ensnode.io/docs/).

### Getting Started
## Architecture

The documentation serves API reference from two sources:

| Section | Source | Purpose |
| -------------------- | ------------------ | ------------------------------------------- |
| **API Reference** | Production API URL | Always reflects the live deployed API |
| **Preview** (hidden) | `./openapi.json` | PR preview deployments for upcoming changes |

When you change API routes or schemas, update the committed `openapi.json` to preview changes in Mintlify's PR deployments.

## OpenAPI Spec Management

1. Clone the repository:
We use a combination of runtime URLs and committed files to keep API docs in sync across environments. This setup achieves:

```bash
git clone https://github.com/namehash/ensnode.git
```
- Production API docs match the production deployment, even when production lags behind `main`
- Non-API docs stay in sync with `main` through normal Git flow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest ideas about non-API docs move out of a section that's dedicated to API docs.

- Each branch has its own `openapi.json`, validated by CI
- PR previews show upcoming API changes before merge

2. Navigate to the docs directory:
### Generating the Spec

```bash
cd ensnode/docs/docs.ensnode.io
```
```bash
# Generate from production API
pnpm --filter ensapi openapi:generate
```

3. Start the local development server:
### CI Validation

```bash
pnpm mint dev
```
CI runs an `openapi-sync-check` job that compares the committed `openapi.json` against production. If they differ, the check fails.

Update the committed spec when:

- You've changed API routes or schemas (generate from your local instance)
- Production was updated (generate from production)

## Local Development

### Getting Started

1. `git clone https://github.com/namehash/ensnode.git`
2. `cd docs/docs.ensnode.io`
3. `pnpm mint dev`
4. Open [http://localhost:3000](http://localhost:3000) in your browser

### Troubleshooting

- If a page loads as a 404, make sure you are running in a folder with a valid `docs.json`.
- Run `pnpm mint --help` to read more details about Mintlify CLI.
- If a page loads as a 404, ensure you're running in a folder with a valid `docs.json`
- Run `pnpm mint --help` for more Mintlify CLI details

## Publishing Changes

Expand All @@ -38,3 +61,4 @@ Changes pushed to the main branch are automatically deployed to production.
## Resources

- [Mintlify documentation](https://mintlify.com/docs)
- [ENSNode documentation](https://ensnode.io/docs/)
8 changes: 7 additions & 1 deletion docs/docs.ensnode.io/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
},
{
"group": "API Reference",
"openapi": "https://gist.githubusercontent.com/notrab/94b637e77468cbddd895d7933ce88f64/raw/12cb5ed183558a9bdda5d1c7004db6c794dbd13e/green-ensnode-openapi.json"
"openapi": "https://api.alpha.ensnode.io/openapi.json"
},
{
"group": "Preview",
"pages": ["ensapi/preview"],
"openapi": "./openapi.json",
"hidden": true
}
]
}
Expand Down
9 changes: 9 additions & 0 deletions docs/docs.ensnode.io/ensapi/preview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: API Preview
sidebarTitle: Preview
description: Preview upcoming API changes from the current branch.
---

This page shows the OpenAPI specification from the current branch, which may include unreleased API changes.
Comment on lines +4 to +7
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Preview page description mentions "current branch" but Mintlify PR previews are generated from the PR's head branch, not necessarily the "current" branch from a local development perspective. Consider clarifying this as "the branch in this PR" or "this branch" to be more precise about what preview is being shown.

Suggested change
description: Preview upcoming API changes from the current branch.
---
This page shows the OpenAPI specification from the current branch, which may include unreleased API changes.
description: Preview upcoming API changes from this branch.
---
This page shows the OpenAPI specification for this branch (for pull requests, the PR's head branch), which may include unreleased API changes.

Copilot uses AI. Check for mistakes.

For the production API documentation, see the [API Reference](/ensapi).
Loading