Skip to content

docs: extend ENS Unigraph examples#2264

Open
tk-o wants to merge 8 commits into
mainfrom
docs/extend-ens-unigraph-examples
Open

docs: extend ENS Unigraph examples#2264
tk-o wants to merge 8 commits into
mainfrom
docs/extend-ens-unigraph-examples

Conversation

@tk-o
Copy link
Copy Markdown
Member

@tk-o tk-o commented Jun 5, 2026

Lite PR

Tip: Review docs on the ENSNode PR process

Summary

  • Refactored ENS Unigraph examples
    • Split code snippets and results into separate files to enable simpler structure of the example pages (simple import and passing props to the actual UI component displaying rich preview of the given example).
  • Added multiple examples presenting various capabilities of the ENS Unigraph indexed data model.

Why

  • There were just a few examples, and we want to demonstrate more use cases for the ENS Unigraph.

Testing

  • All queries (both SQL and SDK forms) were tested with a local ENSApi instance to ensure completeness and correctness.

Notes for Reviewer (Optional)

  • Anything non-obvious or worth a heads-up.

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

@tk-o tk-o requested a review from a team as a code owner June 5, 2026 15:16
Copilot AI review requested due to automatic review settings June 5, 2026 15:16
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 5, 2026

⚠️ No Changeset found

Latest commit: 0a0b6f2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Jun 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
enskit-react-example.ensnode.io Ready Ready Preview, Comment Jun 5, 2026 7:17pm
ensnode.io Ready Ready Preview, Comment Jun 5, 2026 7:17pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
admin.ensnode.io Skipped Skipped Jun 5, 2026 7:17pm
ensrainbow.io Skipped Skipped Jun 5, 2026 7:17pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 5, 2026

Worried about impact? Review this PR in Change Stack to explore blast radius before you approve or request changes.

Review Change Stack

Warning

Review limit reached

@tk-o, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 53 minutes and 49 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: cddc4a34-7e24-48a2-a8d6-c7f2ecac4b85

📥 Commits

Reviewing files that changed from the base of the PR and between 8b2ce94 and 0a0b6f2.

📒 Files selected for processing (2)
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/indexing-status.mdx
  • docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts
📝 Walkthrough

Walkthrough

This PR consolidates Unigraph SQL example documentation by extracting code snippets and expected results from individual MDX files into shared TypeScript data modules, centralizing the example definitions, updating the documentation pages to reference these shared modules, and adding new example documentation pages with navigation links.

Changes

Unigraph Example Documentation System

Layer / File(s) Summary
Type contracts and output utilities
docs/ensnode.io/src/data/unigraph-examples/types.ts, docs/ensnode.io/src/data/unigraph-examples/utils.ts
CodeExample and QueryExample TypeScript interfaces define the structure for shared example payloads; outputSource utility generates HTML-formatted environment labels (V2 Sepolia, Alpha, Alpha Sepolia) for result notes.
Shared example data modules
docs/ensnode.io/src/data/unigraph-examples/account-domains.ts, domain-by-name.ts, domain-events.ts, domains-fuzzy-search-by-name.ts, expiring-registrations.ts, indexing-status.ts, latest-registrations.ts, subdomains-by-parent-name.ts
Eight TypeScript modules export QueryExample-typed objects containing SQL and Drizzle SDK code snippets plus static expected results, enabling reuse across documentation pages.
Example renderer component updates
docs/ensnode.io/src/components/molecules/unigraph-static-example/UnigraphStaticExample.astro
UnigraphStaticExample now renders result notes via set:html directive using optional resultNote from example data with fallback to a default message.
Documentation pages for examples
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx, domain-by-name.mdx, indexing-status.mdx, domain-events.mdx, domains-fuzzy-search-by-name.mdx, expiring-registrations.mdx, latest-registrations.mdx, subdomains-by-parent-name.mdx
Three existing example pages refactored to import shared modules; five new pages added for domain events, fuzzy search, expiring registrations, latest registrations, and subdomains, each rendering with shared example data and schema intro components.
Navigation sidebar and integration documentation
docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts, docs/ensnode.io/src/content/docs/docs/integrate/index.mdx, docs/ensnode.io/src/components/molecules/EnsDbReaderIntro.astro
Sidebar navigation extended with six new example links under ENS Unigraph SQL; ENSNode Plugins label capitalized; ENSDb Writers/Readers/Plugins descriptions expanded in integration docs; Connect example reference updated in reader component.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • namehash/ensnode#2247: Related updates to UnigraphStaticExample.astro for note rendering and example UI refinement.
  • namehash/ensnode#2202: Updates to EnsDbReaderIntro.astro and example documentation component wording.
  • namehash/ensnode#2171: Prior modifications to the "ENS Unigraph SQL" sidebar section in integrate.ts.

Suggested labels

docs

🐰 From a curious rabbit, watching code bloom,
Examples once scattered, now share one room,
Types guide the way, utilities shine,
Documentation flows in patterns so fine,
Navigation leads where SQL examples align!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'docs: extend ENS Unigraph examples' accurately and concisely describes the main change—expanding ENS Unigraph documentation examples with new examples and refactored structure.
Description check ✅ Passed The PR description follows the repository template with all required sections (Summary, Why, Testing, Pre-Review Checklist) completed with appropriate detail and testing confirmation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/extend-ens-unigraph-examples

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tk-o tk-o changed the title Docs/extend ens unigraph examples docs: extend ENS Unigraph examples Jun 5, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 5, 2026

Greptile Summary

This PR extends the ENS Unigraph documentation by adding six new example pages (Domain Events, Fuzzy Search, Subdomains, Account Domains, Latest Registrations, Expiring Registrations) and refactors existing examples to move inline code snippets and results into dedicated TypeScript data files, simplifying the MDX pages.

  • Code snippets and result fixtures are extracted into src/data/unigraph-examples/*.ts, each exporting a QueryExample with SQL and SDK variants; the MDX pages now just import and pass these to UnigraphStaticExample.
  • UnigraphStaticExample.astro switches result-note rendering to set:html so that the hyperlink inside outputSource() is rendered as HTML rather than escaped text.
  • Two data files have cross-tab inconsistencies: domains-fuzzy-search-by-name.ts shows 'reverse' results under an SDK snippet that queries 'vitalik', and subdomains-by-parent-name.ts shows different domain types for the same name between the SQL and SDK tabs.

Confidence Score: 3/5

Two example data files ship with results that contradict the accompanying code snippet, which would mislead developers reading the docs.

The fuzzy-search example shows 'reverse' query results under SDK code that searches for 'vitalik', and the subdomains example lists different domain types for the same name across the SQL and SDK tabs. Both are incorrect example outputs visible to any developer reading the docs pages.

docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts and docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts need their result fixtures corrected before merge.

Important Files Changed

Filename Overview
docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts SDK example uses q="vitalik" but displayed results match the SQL tab's "reverse" search term — the two tabs show inconsistent outputs.
docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts Fifth result row disagrees between SQL (ENSv1Domain) and SDK (ENSv2Domain) tabs for sfmpfv44d0mig.eth with different registry IDs.
docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts SQL and SDK queries are consistent; previously flagged issues (missing SELECT columns, incorrect imports) are resolved in this version.
docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts Double-wrapped indexingStatus issue from prior review is resolved; sql.result and sdk.result now correctly represent query output.
docs/ensnode.io/src/data/unigraph-examples/expiring-registrations.ts JSDoc corrected; SQL and SDK examples are consistent and results match the query structure.
docs/ensnode.io/src/data/unigraph-examples/domain-events.ts New example for fetching domain events; SQL and SDK results are consistent and the query structure looks correct.
docs/ensnode.io/src/components/molecules/unigraph-static-example/UnigraphStaticExample.astro Switched resultNote rendering from children to set:html to support the anchor tag in the outputSource helper; content is hardcoded so no XSS risk.
docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts Added sidebar entries for six new example pages; "Expiring Registrations" typo from prior review is fixed.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[MDX Example Page] -->|imports| B[unigraph-examples/*.ts]
    B -->|exports QueryExample| C{sql / sdk}
    C -->|sql.codeSnippet\nsql.result\nsql.resultNote| D[UnigraphStaticExample.astro]
    C -->|sdk.codeSnippet\nsdk.result\nsdk.resultNote| D
    D --> E[UnigraphExampleSQLTab\ncode + SqlResultTable + set:html note]
    D --> F[UnigraphExampleEnsDbSdkTab\ncode + SqlResultTable + set:html note]
    G[utils.outputSource] -->|HTML string with link| B
Loading

Reviews (4): Last reviewed commit: "Apply AI PR feedback" | Re-trigger Greptile

Comment thread docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts Outdated
Comment thread docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/account-domains.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR expands the ENS Unigraph SQL examples in the docs by moving example payloads into dedicated data modules and adding several new example pages that import those modules for rendering.

Changes:

  • Introduced a small QueryExample/CodeExample type contract and added multiple new Unigraph example data files (SQL + ensdb-sdk snippets + static results).
  • Added new MDX pages for each example and refactored existing pages to import examples from @data/unigraph-examples/....
  • Updated the Integrate sidebar to include the expanded Unigraph examples navigation.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
docs/ensnode.io/src/data/unigraph-examples/types.ts Adds shared TS types for example payloads.
docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts Adds subdomains-by-parent SQL/SDK example + result fixture.
docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts Adds latest-registrations SQL/SDK example + result fixture.
docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts Adds indexing-status SQL/SDK example + result fixture.
docs/ensnode.io/src/data/unigraph-examples/expiring-registrations.ts Adds expiring-registrations SQL/SDK example + result fixture.
docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts Adds pg_trgm-based fuzzy name search example + result fixture.
docs/ensnode.io/src/data/unigraph-examples/domain-events.ts Adds domain events join example + result fixture.
docs/ensnode.io/src/data/unigraph-examples/domain-by-name.ts Adds canonical-name lookup example + result fixture.
docs/ensnode.io/src/data/unigraph-examples/account-domains.ts Adds “domains owned by address” example + result fixture.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/subdomains-by-parent-name.mdx New example page wiring the data module into the UI component.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/latest-registrations.mdx New example page wiring the data module into the UI component.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/indexing-status.mdx Refactors existing page to import the new data module.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/expiring-registrations.mdx New example page wiring the data module into the UI component.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domains-fuzzy-search-by-name.mdx New example page wiring the data module into the UI component.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-events.mdx New example page wiring the data module into the UI component.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx Refactors existing page to import the new data module.
docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx Refactors existing page to import the new data module.
docs/ensnode.io/src/content/docs/docs/integrate/index.mdx Updates integration option descriptions.
docs/ensnode.io/src/components/molecules/EnsDbReaderIntro.astro Minor copy tweak referencing the “Connect” example.
docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts Adds sidebar links for the expanded Unigraph example set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts
Comment thread docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/account-domains.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts
Comment thread docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts`:
- Around line 206-207: Correct the typo in the sidebar entry by changing the
label string "Exipring Registrations" to "Expiring Registrations"; locate the
object where label: "Exipring Registrations" appears (the same entry with link:
"/docs/integrate/unigraph/examples/expiring-registrations") and update the label
value to the spelled-correct form.

In
`@docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-events.mdx`:
- Line 17: The description paragraph starting with "Fetch recent events for a
Domain by its canonical name..." is missing the consistent setup reference;
update that paragraph (in the content of domain-events.mdx) to append the
sentence "See [Connect](/docs/integrate/unigraph/examples) for setup." so it
matches the other example pages (search for the exact paragraph text or the
file's title/section to locate where to insert this reference).

In `@docs/ensnode.io/src/data/unigraph-examples/account-domains.ts`:
- Around line 98-119: The example uses asc(...) in orderBy but never imports it;
update the import line in the codeSnippet to include asc (i.e., import { and,
eq, asc } from "drizzle-orm") so the call to asc in the accountDomains query
resolves correctly—modify the import near the top of the snippet where and and
eq are imported.

In `@docs/ensnode.io/src/data/unigraph-examples/expiring-registrations.ts`:
- Around line 3-5: Update the module JSDoc to describe expiring registrations
rather than recent events; replace the current summary line ("Example query for
fetching recent events for a Domain by its canonical name.") with a concise
description matching the file intent such as "Example query for fetching
expiring registrations for a Domain" (or similar phrasing) in the top comment of
expiring-registrations.ts so the documentation matches the query implemented in
this module.

In `@docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts`:
- Around line 119-130: The example outputs incorrectly nest indexingStatus one
level too deep; update the result objects so they use
indexingStatusSnapshot.indexingStatus instead of { indexingStatus:
indexingStatusSnapshot } (i.e., set result to { indexingStatus:
indexingStatusSnapshot.indexingStatus }) for both occurrences referenced around
indexingStatusSnapshot and the SDK snippet constants to match the SQL/SDK
examples.

In `@docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts`:
- Around line 8-24: The SQL in the codeSnippet currently selects five columns
(d.canonical_name, r.start, r.expiry, d.owner_id, d.id AS domain_id) with LIMIT
15 but the documented result payload contains additional registration columns
and only five rows; make them consistent by either (A) expanding the SELECT in
the codeSnippet to include the extra registration fields shown in the result
(e.g., add r.transaction_hash, r.registration_index, r.type, etc.) and keep
LIMIT 5 if the example intends five rows, or (B) update the result block to
match the current SQL (only the five selected columns and 15 rows); locate the
codeSnippet string in latest-registrations.ts and change the SELECT column list
and LIMIT (or the result block) accordingly so both the query and the documented
result match exactly.
- Around line 89-127: The code snippet is missing the desc import and the SQL
example's selected columns and LIMIT don't match the shown result objects;
update the drizzle import to include desc (alongside and, asc, eq, ne, lte, sql)
so the call to desc(ensIndexerSchema.registration.start) is valid, and then make
the SELECT and LIMIT in the SQL example consistent with the JavaScript query and
example output—either change the SQL SELECT to return the same fields as
recentRegistrations (canonicalName, expiry, start,
registrationType/registration.type, ownerId, domainId) and set LIMIT 5, or alter
the JS select to include grace_period/base/premium/registration_type and use
LIMIT 15 to match the displayed result—ensure recentRegistrations, desc, and the
SQL SELECT/LIMIT are aligned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 246d12cd-2c73-4f09-9279-aed3c5020bc8

📥 Commits

Reviewing files that changed from the base of the PR and between c8267e4 and d4e91df.

📒 Files selected for processing (20)
  • docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts
  • docs/ensnode.io/src/components/molecules/EnsDbReaderIntro.astro
  • docs/ensnode.io/src/content/docs/docs/integrate/index.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-events.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domains-fuzzy-search-by-name.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/expiring-registrations.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/indexing-status.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/latest-registrations.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/subdomains-by-parent-name.mdx
  • docs/ensnode.io/src/data/unigraph-examples/account-domains.ts
  • docs/ensnode.io/src/data/unigraph-examples/domain-by-name.ts
  • docs/ensnode.io/src/data/unigraph-examples/domain-events.ts
  • docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts
  • docs/ensnode.io/src/data/unigraph-examples/expiring-registrations.ts
  • docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts
  • docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts
  • docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts
  • docs/ensnode.io/src/data/unigraph-examples/types.ts

Comment thread docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/account-domains.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts Outdated
Comment thread docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts
Comment thread docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts Outdated
Comment thread apps/ensapi/src/handlers/api/router.ts Outdated
Comment thread apps/ensapi/src/handlers/api/test/test-queries-api.ts Outdated
Copilot AI review requested due to automatic review settings June 5, 2026 17:55
@tk-o tk-o force-pushed the docs/extend-ens-unigraph-examples branch from 66e5135 to 2b41753 Compare June 5, 2026 17:55
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io June 5, 2026 17:55 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io June 5, 2026 17:55 Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts (1)

98-104: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Align SDK field selection with the SQL example.

The SQL snippet (lines 8-17) selects grace_period, base, and premium from the registration table, but the SDK .select() block omits these fields. This creates an inconsistency between the two examples—users comparing SQL vs SDK approaches will see different output shapes.

🔄 Suggested alignment
 const recentRegistrations = await ensDb
   .select({
     canonicalName: ensIndexerSchema.domain.canonicalName,
     expiry: ensIndexerSchema.registration.expiry,
     start: ensIndexerSchema.registration.start,
+    gracePeriod: ensIndexerSchema.registration.gracePeriod,
+    base: ensIndexerSchema.registration.base,
+    premium: ensIndexerSchema.registration.premium,
     registrationType: ensIndexerSchema.registration.type,
     ownerId: ensIndexerSchema.domain.ownerId,
     domainId: ensIndexerSchema.domain.id,
   })

Then update the SDK result array (lines 135-181) to include gracePeriod, base, and premium in each row to match the SQL result payload.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts` around
lines 98 - 104, The SDK .select(...) call currently selects canonicalName,
expiry, start, registrationType, ownerId, and domainId but omits the
registration fields selected in the SQL example; update the .select invocation
to also include ensIndexerSchema.registration.grace_period,
ensIndexerSchema.registration.base, and ensIndexerSchema.registration.premium
(mapped to SDK names like gracePeriod, base, premium as needed) so the SDK
projection matches the SQL example, and then update the example SDK result array
(the result rows shown later) to add gracePeriod, base, and premium to each row
so the returned payload shape mirrors the SQL output.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts`:
- Around line 165-207: The /fuzzy-search route handler lacks input validation
and error handling; add a Zod schema and apply the validate() middleware to
parse/validate query params (q as non-empty string and limit as a positive
integer with a sensible default) for the app.get("/fuzzy-search", ...) route,
then wrap the database calls that use di.context.ensDb (the sqlResult and
drizzleResult queries) in a try-catch and call the existing errorResponse helper
in the catch to return a formatted error and avoid throwing raw errors to the
client.
- Around line 211-285: The /expiring route handler (app.get("/expiring")) lacks
input validation and error handling: create a Zod schema for start, end, and
limit and apply the validate() middleware to the route (use the same param names
used in the handler), then wrap the database calls that use ensDb and
ensIndexerSchema in a try-catch and return errors via the errorResponse helper;
on validation success parse and pass the validated start/end/limit into the
query and keep returning jsonBigInt on success.
- Around line 60-122: The /subdomains handler lacks input validation, error
handling, explicit missing-parent responses, and contains a debug console.log;
update the app.get("/subdomains", async (c) => { ... }) route to: validate query
params using a Zod schema (name:string, limit:number) via the validate()
middleware, wrap the database work that uses ensDb and ensIndexerSchema in a
try-catch and return errors using the errorResponse helper on catch, remove the
console.log(subdomains) call, and when parentDomain or
parentDomain.subregistryId is falsy return a clear errorResponse (e.g., 404 or
validation-style error) instead of falling through to an implicit undefined
return. Ensure you reference the existing symbols parentDomain and subdomains
when implementing these checks.
- Around line 392-445: The /domain-events route handler is missing input
validation and error handling; add a Zod schema for the query params (validate
name as string and limit as optional positive integer with a max), apply the
validate() middleware to this route so the handler uses the validated values
instead of raw c.req.query, and wrap the DB calls (the RAW SQL block and the
DRIZZLE select chain) in a try-catch that returns the standardized
errorResponse(...) on failure (use the same errorResponse helper used elsewhere
in this app); refer to the route handler for "/domain-events", the local
variables name and limit, and the DB usage via ensDb, ensIndexerSchema to locate
where to apply the validate() middleware and try-catch.
- Around line 26-57: The GET /domain-by-name handler is missing input
validation, lacks error handling, and contains a debug console.log; add a Zod
schema for the "name" query param and call the existing validate() middleware
(from apps/ensapi/src/lib/handlers/validate.ts) to validate c.req.query before
running DB logic, wrap the DB calls in a try-catch and on error return the
shared errorResponse(...) helper (from
apps/ensapi/src/lib/handlers/error-response.ts) with the caught error, and
remove the console.log(vitalik) statement; keep using the same
functions/variables in the handler (app.get("/domain-by-name"), ensDb,
ensIndexerSchema, jsonBigInt) but ensure validation runs first and DB errors are
caught and mapped to errorResponse.
- Around line 125-162: Replace the hardcoded owner and limit in the
app.get("/account-domains", async (c) => { ... }) handler by reading
c.req.query("owner") and c.req.query("limit"), add a Zod schema for those query
params and apply the existing validate() middleware for this route, wrap the
database calls (ensDb.execute and ensDb.select(...) usages referencing
ensIndexerSchema) in a try-catch and call the shared errorResponse helper on
failure, and ensure the successful response still returns jsonBigInt({ owner,
sql: sqlResult.rows, drizzle: drizzleResult }) after parsing/normalizing the
validated limit to a number.
- Around line 288-355: The /recent-registrations route handler is missing input
validation and error handling; add a Zod schema for the query param (limit) and
apply the validate() middleware before this handler, and wrap the DB calls
(ensDb.execute and the Drizzle query producing sqlResult and drizzleResult) in a
try-catch that returns the centralized errorResponse helper on failure; ensure
the validated limit is used (instead of raw parseInt) and that any caught error
is passed to errorResponse so the route fails gracefully.
- Around line 358-389: The /permissions handler returns inconsistent data and
lacks validation/error handling: validate the incoming user query using the
project's Zod schema via the validate() middleware, wrap the DB calls (the raw
SQL execute returning sqlResult and the Drizzle select producing drizzleResult)
in a try-catch and call the errorResponse helper on failure, and return
sqlResult.rows (not sqlResult) alongside drizzleResult in the jsonBigInt
response; update the app.get("/permissions") handler to perform these changes
around the existing ensDb/ensIndexerSchema usage so consumers always receive the
same array format and errors are handled.

---

Outside diff comments:
In `@docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts`:
- Around line 98-104: The SDK .select(...) call currently selects canonicalName,
expiry, start, registrationType, ownerId, and domainId but omits the
registration fields selected in the SQL example; update the .select invocation
to also include ensIndexerSchema.registration.grace_period,
ensIndexerSchema.registration.base, and ensIndexerSchema.registration.premium
(mapped to SDK names like gracePeriod, base, premium as needed) so the SDK
projection matches the SQL example, and then update the example SDK result array
(the result rows shown later) to add gracePeriod, base, and premium to each row
so the returned payload shape mirrors the SQL output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 829bd0dd-5833-4726-892a-6bdd6fa62b2f

📥 Commits

Reviewing files that changed from the base of the PR and between d4e91df and 66e5135.

📒 Files selected for processing (11)
  • apps/ensapi/src/handlers/api/router.ts
  • apps/ensapi/src/handlers/api/test/test-queries-api.ts
  • docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-by-name.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-events.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/expiring-registrations.mdx
  • docs/ensnode.io/src/data/unigraph-examples/account-domains.ts
  • docs/ensnode.io/src/data/unigraph-examples/expiring-registrations.ts
  • docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts
  • docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts

Comment on lines +26 to +57
app.get("/domain-by-name", async (c) => {
const name = c.req.query("name") || "vitalik.eth";
const { ensDb, ensIndexerSchema } = di.context;

// RAW SQL
const sqlResult = await ensDb.execute(sql`
SELECT
id,
type,
canonical_name,
canonical_node,
owner_id
FROM ${ensIndexerSchema.domain} d
WHERE d.canonical_name = ${name} AND d.canonical = true;
`);

// DRIZZLE
const [vitalik] = await ensDb
.select({
id: ensIndexerSchema.domain.id,
type: ensIndexerSchema.domain.type,
canonicalName: ensIndexerSchema.domain.canonicalName,
canonicalNode: ensIndexerSchema.domain.canonicalNode,
ownerId: ensIndexerSchema.domain.ownerId,
})
.from(ensIndexerSchema.domain)
.where(sql`${ensIndexerSchema.domain.canonicalName} = ${name}`);

console.log(vitalik);

return jsonBigInt({ name, sql: sqlResult.rows[0], drizzle: vitalik });
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add input validation and error handling; remove debug logging.

This endpoint has three critical issues:

  1. Missing input validation: As per coding guidelines, use existing Hono validation middleware (Zod schemas + validate() from apps/ensapi/src/lib/handlers/validate.ts) for validating API requests. The name parameter should be validated.

  2. No error handling: Database queries can fail but there's no try-catch block or error handling. Use the shared errorResponse helper from apps/ensapi/src/lib/handlers/error-response.ts for error responses.

  3. Debug logging: Remove the console.log(vitalik) statement on line 54.

As per coding guidelines for apps/ensapi/**/*.ts files: validation middleware and errorResponse helper should be used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts` around lines 26 - 57,
The GET /domain-by-name handler is missing input validation, lacks error
handling, and contains a debug console.log; add a Zod schema for the "name"
query param and call the existing validate() middleware (from
apps/ensapi/src/lib/handlers/validate.ts) to validate c.req.query before running
DB logic, wrap the DB calls in a try-catch and on error return the shared
errorResponse(...) helper (from apps/ensapi/src/lib/handlers/error-response.ts)
with the caught error, and remove the console.log(vitalik) statement; keep using
the same functions/variables in the handler (app.get("/domain-by-name"), ensDb,
ensIndexerSchema, jsonBigInt) but ensure validation runs first and DB errors are
caught and mapped to errorResponse.

Comment on lines +60 to +122
app.get("/subdomains", async (c) => {
const name = c.req.query("name") || "eth";
const limit = parseInt(c.req.query("limit") || "5", 10);
const { ensDb, ensIndexerSchema } = di.context;

// RAW SQL
const sqlResult = await ensDb.execute(sql`
WITH parent AS (
SELECT subregistry_id
FROM ${ensIndexerSchema.domain}
WHERE canonical_name = ${name}
AND canonical = true
)
SELECT
d.type,
d.canonical_name,
d.canonical_node,
d.id
FROM ${ensIndexerSchema.domain} d
JOIN parent p ON d.registry_id = p.subregistry_id
WHERE d.canonical = true
ORDER BY d.canonical_name
LIMIT ${limit};
`);

// DRIZZLE - two-step approach (find parent, then children)

// Two-step:
// 1) find parent domain,
// 2) query children by parent domain's subregistryId.
const [parentDomain] = await ensDb
.select({ subregistryId: ensIndexerSchema.domain.subregistryId })
.from(ensIndexerSchema.domain)
.where(
and(
sql`${ensIndexerSchema.domain.canonicalName} = ${name}`,
eq(ensIndexerSchema.domain.canonical, true),
),
);

if (parentDomain?.subregistryId) {
const subdomains = await ensDb
.select({
type: ensIndexerSchema.domain.type,
canonicalName: ensIndexerSchema.domain.canonicalName,
canonicalNode: ensIndexerSchema.domain.canonicalNode,
id: ensIndexerSchema.domain.id,
})
.from(ensIndexerSchema.domain)
.where(
and(
eq(ensIndexerSchema.domain.registryId, parentDomain.subregistryId),
eq(ensIndexerSchema.domain.canonical, true),
),
)
.orderBy(asc(ensIndexerSchema.domain.canonicalName))
.limit(limit);

console.log(subdomains);

return jsonBigInt({ name, sql: sqlResult.rows, drizzle: subdomains });
}
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add input validation, error handling, and explicit error response for missing parent.

Multiple issues:

  1. Missing input validation: Use Zod schemas with validate() middleware to validate name and limit parameters as per coding guidelines.

  2. No error handling: Wrap database queries in try-catch and use errorResponse helper.

  3. Debug logging: Remove console.log(subdomains) on line 118.

  4. Implicit undefined return: When parentDomain?.subregistryId is falsy (lines 100-122), the endpoint returns undefined implicitly. Return an explicit error response instead.

As per coding guidelines for apps/ensapi/**/*.ts files: validation middleware and errorResponse helper should be used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts` around lines 60 - 122,
The /subdomains handler lacks input validation, error handling, explicit
missing-parent responses, and contains a debug console.log; update the
app.get("/subdomains", async (c) => { ... }) route to: validate query params
using a Zod schema (name:string, limit:number) via the validate() middleware,
wrap the database work that uses ensDb and ensIndexerSchema in a try-catch and
return errors using the errorResponse helper on catch, remove the
console.log(subdomains) call, and when parentDomain or
parentDomain.subregistryId is falsy return a clear errorResponse (e.g., 404 or
validation-style error) instead of falling through to an implicit undefined
return. Ensure you reference the existing symbols parentDomain and subdomains
when implementing these checks.

Comment on lines +125 to +162
app.get("/account-domains", async (c) => {
const { ensDb, ensIndexerSchema } = di.context;
const owner = "0xffffffffff52d316b7bd028358089bc8066b8f80";
const limit = 10;

// RAW SQL
const sqlResult = await ensDb.execute(sql`
SELECT
d.type,
d.canonical_name,
d.canonical_node,
d.id,
d.owner_id
FROM ${ensIndexerSchema.domain} d
WHERE d.canonical = true
AND d.owner_id = ${owner}
ORDER BY d.canonical_name
LIMIT ${limit}
`);

// DRIZZLE
const drizzleResult = await ensDb
.select({
type: ensIndexerSchema.domain.type,
canonicalName: ensIndexerSchema.domain.canonicalName,
canonicalNode: ensIndexerSchema.domain.canonicalNode,
id: ensIndexerSchema.domain.id,
ownerId: ensIndexerSchema.domain.ownerId,
})
.from(ensIndexerSchema.domain)
.where(
and(eq(ensIndexerSchema.domain.ownerId, owner), eq(ensIndexerSchema.domain.canonical, true)),
)
.orderBy(asc(ensIndexerSchema.domain.canonicalName))
.limit(limit);

return jsonBigInt({ owner, sql: sqlResult.rows, drizzle: drizzleResult });
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Use query parameters instead of hardcoded values; add validation and error handling.

Multiple issues:

  1. Hardcoded values: Lines 127-128 hardcode owner and limit instead of reading from query parameters as suggested by the comment on line 124. Use c.req.query("owner") and c.req.query("limit") like other endpoints.

  2. Missing input validation: Use Zod schemas with validate() middleware to validate query parameters as per coding guidelines.

  3. No error handling: Wrap database queries in try-catch and use errorResponse helper.

As per coding guidelines for apps/ensapi/**/*.ts files: validation middleware and errorResponse helper should be used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts` around lines 125 -
162, Replace the hardcoded owner and limit in the app.get("/account-domains",
async (c) => { ... }) handler by reading c.req.query("owner") and
c.req.query("limit"), add a Zod schema for those query params and apply the
existing validate() middleware for this route, wrap the database calls
(ensDb.execute and ensDb.select(...) usages referencing ensIndexerSchema) in a
try-catch and call the shared errorResponse helper on failure, and ensure the
successful response still returns jsonBigInt({ owner, sql: sqlResult.rows,
drizzle: drizzleResult }) after parsing/normalizing the validated limit to a
number.

Comment on lines +165 to +207
app.get("/fuzzy-search", async (c) => {
const q = c.req.query("q") || "vitalik";
const limit = parseInt(c.req.query("limit") || "5", 10);
const { ensDb, ensIndexerSchema } = di.context;

// RAW SQL - pg_trgm similarity operator (%) and similarity() ranking
const sqlResult = await ensDb.execute(sql`SELECT
id,
type,
canonical_name,
canonical_node,
owner_id,
similarity(canonical_name, ${q}) as name_similarity
FROM ${ensIndexerSchema.domain}
WHERE canonical_name % ${q}
AND canonical = true
ORDER BY name_similarity DESC
LIMIT ${limit};
`);

// DRIZZLE - same pg_trgm query using inline SQL expressions
const drizzleResult = await ensDb
.select({
canonicalName: ensIndexerSchema.domain.canonicalName,
canonicalDepth: ensIndexerSchema.domain.canonicalDepth,
ownerId: ensIndexerSchema.domain.ownerId,
nameSimilarity: sql<number>`similarity(${ensIndexerSchema.domain.canonicalName}, ${q})`.as(
"name_similarity",
),
id: ensIndexerSchema.domain.id,
})
.from(ensIndexerSchema.domain)
.where(
and(
sql`${ensIndexerSchema.domain.canonicalName} % ${q}`,
eq(ensIndexerSchema.domain.canonical, true),
),
)
.orderBy(sql`name_similarity DESC`)
.limit(limit);

return jsonBigInt({ q, sql: sqlResult.rows, drizzle: drizzleResult });
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add input validation and error handling.

Missing:

  1. Input validation: Use Zod schemas with validate() middleware to validate q and limit parameters as per coding guidelines.

  2. Error handling: Wrap database queries in try-catch and use errorResponse helper.

As per coding guidelines for apps/ensapi/**/*.ts files: validation middleware and errorResponse helper should be used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts` around lines 165 -
207, The /fuzzy-search route handler lacks input validation and error handling;
add a Zod schema and apply the validate() middleware to parse/validate query
params (q as non-empty string and limit as a positive integer with a sensible
default) for the app.get("/fuzzy-search", ...) route, then wrap the database
calls that use di.context.ensDb (the sqlResult and drizzleResult queries) in a
try-catch and call the existing errorResponse helper in the catch to return a
formatted error and avoid throwing raw errors to the client.

Comment on lines +211 to +285
app.get("/expiring", async (c) => {
const start = parseInt(c.req.query("start") || "0", 10);
const end = parseInt(
c.req.query("end") || String(Math.floor(Date.now() / 1000) + 86400 * 90),
10,
);
const limit = parseInt(c.req.query("limit") || "5", 10);
const { ensDb, ensIndexerSchema } = di.context;

// ── RAW SQL ──
// Use explicit >= / <= instead of BETWEEN for clarity.

const sqlResult = await ensDb.execute(sql`SELECT
d.canonical_name,
r.start,
r.expiry,
r.grace_period,
d.owner_id,
d.id as domain_id
FROM ${ensIndexerSchema.registration} r
JOIN ${ensIndexerSchema.latestRegistrationIndex} lri
ON r.domain_id = lri.domain_id
AND r.registration_index = lri.registration_index
JOIN ${ensIndexerSchema.domain} d ON r.domain_id = d.id
WHERE r.expiry >= EXTRACT(EPOCH FROM NOW())
AND r.expiry <= EXTRACT(EPOCH FROM NOW() + INTERVAL '3 days')
AND d.canonical = true
ORDER BY r.expiry ASC
LIMIT ${limit};
`);

// ── DRIZZLE ORM ──
// gte / lte are the idiomatic Drizzle equivalent of >= / <=.
const drizzleResult = await ensDb
.select({
canonicalName: ensIndexerSchema.domain.canonicalName,
expiry: ensIndexerSchema.registration.expiry,
start: ensIndexerSchema.registration.start,
gracePeriod: ensIndexerSchema.registration.gracePeriod,
ownerId: ensIndexerSchema.domain.ownerId,
domainId: ensIndexerSchema.domain.id,
})
.from(ensIndexerSchema.registration)
.innerJoin(
ensIndexerSchema.latestRegistrationIndex,
and(
eq(
ensIndexerSchema.registration.domainId,
ensIndexerSchema.latestRegistrationIndex.domainId,
),
eq(
ensIndexerSchema.registration.registrationIndex,
ensIndexerSchema.latestRegistrationIndex.registrationIndex,
),
),
)
.innerJoin(
ensIndexerSchema.domain,
eq(ensIndexerSchema.registration.domainId, ensIndexerSchema.domain.id),
)
.where(
and(
gte(ensIndexerSchema.registration.expiry, sql`EXTRACT(EPOCH FROM NOW())`),
lte(
ensIndexerSchema.registration.expiry,
sql`EXTRACT(EPOCH FROM NOW() + INTERVAL '3 days')`,
),
eq(ensIndexerSchema.domain.canonical, true),
),
)
.orderBy(asc(ensIndexerSchema.registration.expiry))
.limit(limit);

return jsonBigInt({ start, end, sql: sqlResult.rows, drizzle: drizzleResult });
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add input validation and error handling.

Missing:

  1. Input validation: Use Zod schemas with validate() middleware to validate start, end, and limit parameters as per coding guidelines.

  2. Error handling: Wrap database queries in try-catch and use errorResponse helper.

As per coding guidelines for apps/ensapi/**/*.ts files: validation middleware and errorResponse helper should be used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts` around lines 211 -
285, The /expiring route handler (app.get("/expiring")) lacks input validation
and error handling: create a Zod schema for start, end, and limit and apply the
validate() middleware to the route (use the same param names used in the
handler), then wrap the database calls that use ensDb and ensIndexerSchema in a
try-catch and return errors via the errorResponse helper; on validation success
parse and pass the validated start/end/limit into the query and keep returning
jsonBigInt on success.

Comment on lines +288 to +355
app.get("/recent-registrations", async (c) => {
const limit = parseInt(c.req.query("limit") || "50", 10);
const { ensDb, ensIndexerSchema } = di.context;

// RAW SQL
const sqlResult = await ensDb.execute(sql`
SELECT
d.canonical_name,
r.start,
r.expiry,
r.grace_period,
r.base,
r.premium,
r.type as registration_type,
d.owner_id,
d.id as domain_id
FROM ${ensIndexerSchema.registration} r
JOIN ${ensIndexerSchema.latestRegistrationIndex} lri
ON r.domain_id = lri.domain_id
AND r.registration_index = lri.registration_index
JOIN ${ensIndexerSchema.domain} d ON r.domain_id = d.id
WHERE r.start <= EXTRACT(EPOCH FROM NOW())
AND d.canonical = true
AND r.type <> 'NameWrapper'
ORDER BY r.start DESC
LIMIT ${limit};
`);

// DRIZZLE
const drizzleResult = await ensDb
.select({
canonicalName: ensIndexerSchema.domain.canonicalName,
expiry: ensIndexerSchema.registration.expiry,
start: ensIndexerSchema.registration.start,
registrationType: ensIndexerSchema.registration.type,
ownerId: ensIndexerSchema.domain.ownerId,
domainId: ensIndexerSchema.domain.id,
})
.from(ensIndexerSchema.registration)
.innerJoin(
ensIndexerSchema.latestRegistrationIndex,
and(
eq(
ensIndexerSchema.registration.domainId,
ensIndexerSchema.latestRegistrationIndex.domainId,
),
eq(
ensIndexerSchema.registration.registrationIndex,
ensIndexerSchema.latestRegistrationIndex.registrationIndex,
),
),
)
.innerJoin(
ensIndexerSchema.domain,
eq(ensIndexerSchema.registration.domainId, ensIndexerSchema.domain.id),
)
.where(
and(
lte(ensIndexerSchema.registration.start, sql`EXTRACT(EPOCH FROM NOW())`),
eq(ensIndexerSchema.domain.canonical, true),
ne(ensIndexerSchema.registration.type, "NameWrapper"),
),
)
.orderBy(desc(ensIndexerSchema.registration.start))
.limit(limit);

return jsonBigInt({ limit, sql: sqlResult.rows, drizzle: drizzleResult });
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add input validation and error handling.

Missing:

  1. Input validation: Use Zod schemas with validate() middleware to validate limit parameter as per coding guidelines.

  2. Error handling: Wrap database queries in try-catch and use errorResponse helper.

As per coding guidelines for apps/ensapi/**/*.ts files: validation middleware and errorResponse helper should be used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts` around lines 288 -
355, The /recent-registrations route handler is missing input validation and
error handling; add a Zod schema for the query param (limit) and apply the
validate() middleware before this handler, and wrap the DB calls (ensDb.execute
and the Drizzle query producing sqlResult and drizzleResult) in a try-catch that
returns the centralized errorResponse helper on failure; ensure the validated
limit is used (instead of raw parseInt) and that any caught error is passed to
errorResponse so the route fails gracefully.

Comment on lines +358 to +389
app.get("/permissions", async (c) => {
const user = c.req.query("user") || "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
const { ensDb, ensIndexerSchema } = di.context;

// RAW SQL
const sqlResult = await ensDb.execute(sql`
SELECT pu.id, pu.chain_id, pu.address as contract_address,
pu.resource, pu.roles
FROM ${ensIndexerSchema.permissionsUser} pu
WHERE pu.user = ${user}
ORDER BY pu.chain_id, pu.address, pu.resource
`);

// DRIZZLE
const drizzleResult = await ensDb
.select({
id: ensIndexerSchema.permissionsUser.id,
chainId: ensIndexerSchema.permissionsUser.chainId,
contractAddress: ensIndexerSchema.permissionsUser.address,
resource: ensIndexerSchema.permissionsUser.resource,
roles: ensIndexerSchema.permissionsUser.roles,
})
.from(ensIndexerSchema.permissionsUser)
.where(sql`${ensIndexerSchema.permissionsUser.user} = ${user}`)
.orderBy(
asc(ensIndexerSchema.permissionsUser.chainId),
asc(ensIndexerSchema.permissionsUser.address),
asc(ensIndexerSchema.permissionsUser.resource),
);

return jsonBigInt({ user, sql: sqlResult, drizzle: drizzleResult });
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix inconsistent response format; add input validation and error handling.

Multiple issues:

  1. Inconsistent response format: Line 388 returns sqlResult instead of sqlResult.rows. All other endpoints return .rows to extract the result array. This inconsistency could confuse API consumers.

  2. Missing input validation: Use Zod schemas with validate() middleware to validate user parameter as per coding guidelines.

  3. No error handling: Wrap database queries in try-catch and use errorResponse helper.

🔧 Proposed fix for response format
- return jsonBigInt({ user, sql: sqlResult, drizzle: drizzleResult });
+ return jsonBigInt({ user, sql: sqlResult.rows, drizzle: drizzleResult });

As per coding guidelines for apps/ensapi/**/*.ts files: validation middleware and errorResponse helper should be used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ensapi/src/handlers/api/test/test-queries-api.ts` around lines 358 -
389, The /permissions handler returns inconsistent data and lacks
validation/error handling: validate the incoming user query using the project's
Zod schema via the validate() middleware, wrap the DB calls (the raw SQL execute
returning sqlResult and the Drizzle select producing drizzleResult) in a
try-catch and call the errorResponse helper on failure, and return
sqlResult.rows (not sqlResult) alongside drizzleResult in the jsonBigInt
response; update the app.get("/permissions") handler to perform these changes
around the existing ensDb/ensIndexerSchema usage so consumers always receive the
same array format and errors are handled.

Comment thread apps/ensapi/src/handlers/api/test/test-queries-api.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 5 comments.

Comment thread docs/ensnode.io/src/data/unigraph-examples/types.ts
Comment thread docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts Outdated
Comment thread docs/ensnode.io/src/components/molecules/EnsDbReaderIntro.astro
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 9 comments.

<SqlResultTable rows={sql.result} />

<StaticExampleNote class="px-5 pb-5">{sql.resultNote ?? defaultResultNote}</StaticExampleNote>
<StaticExampleNote class="px-5 pb-5" set:html={sql.resultNote ?? defaultResultNote} />
<StaticExampleNote class="px-5 pb-5">
{ensDbSdk.resultNote ?? defaultResultNote}
</StaticExampleNote>
<StaticExampleNote class="px-5 pb-5" set:html={ensDbSdk.resultNote ?? defaultResultNote} />
Comment on lines +1 to +5
export interface CodeExample {
codeSnippet: string;
result: unknown;
resultNote?: string;
}
Comment on lines +18 to +21
FROM "ensindexer_0".domains
WHERE __canonicalNamePrefix % 'reverse'
AND canonical = true
ORDER BY name_similarity DESC
Comment on lines +55 to +56
const q = "vitalik";
const limit = 5;
Performing SQL queries on the ENS Unigraph requires that you have the `unigraph` plugin activated in your ENSNode instance. [Learn more](/docs/services/ensindexer/usage/configuration)
:::

Fetch recent events for a Domain by its canonical name. This example joins the `events`, `domain_events`, and `domains` tables to retrieve domain events associated with the `vitalik.eth` name. See [Connect](/docs/integrate/unigraph/examples) for setup.
Comment on lines +23 to +26
JOIN parent p ON d.registry_id = p.subregistry_id
WHERE d.canonical = true
ORDER BY d.canonical_name
LIMIT 5;
Comment on lines +19 to +23
FROM "ensindexer_0".domains d
WHERE d.canonical = true
AND d.owner_id = '0xffffffffff52d316b7bd028358089bc8066b8f80'
ORDER BY d.canonical_name
LIMIT 10;`,
Comment on lines +120 to +123
),
)
.orderBy(asc(ensIndexerSchema.domain.canonicalName))
.limit(limit);
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts (1)

102-108: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider aligning SQL and SDK field selections for consistency.

The SQL example selects grace_period, base, and premium (lines 15-17), but the SDK example omits these fields. While each variant is internally consistent with its result, documentation examples are clearer when both SQL and SDK demonstrate the same fields.

📋 Optional enhancement to match SQL fields
 .select({
   canonicalName: ensIndexerSchema.domain.canonicalName,
   expiry: ensIndexerSchema.registration.expiry,
   start: ensIndexerSchema.registration.start,
+  gracePeriod: ensIndexerSchema.registration.gracePeriod,
+  base: ensIndexerSchema.registration.base,
+  premium: ensIndexerSchema.registration.premium,
   registrationType: ensIndexerSchema.registration.type,
   ownerId: ensIndexerSchema.domain.ownerId,
   domainId: ensIndexerSchema.domain.id,
 })

Then update the SDK result array to include those fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts` around
lines 102 - 108, The SDK .select block should include the same registration
pricing fields as the SQL example: add
ensIndexerSchema.registration.grace_period, ensIndexerSchema.registration.base,
and ensIndexerSchema.registration.premium to the .select call (alongside expiry,
start, and type), then update the SDK result array/object in the example to
include the corresponding grace_period, base, and premium entries so both SQL
and SDK examples present the same fields.
docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts (1)

55-55: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix the SDK query parameter to match the SQL example.

The SDK snippet declares const q = "vitalik"; on line 55, but the SQL example searches for 'reverse' (lines 16, 19) and both result arrays show domains matching "reverse" (e.g., "reverse", "addr.reverse"). Users copying the SDK snippet will query for "vitalik" but expect "reverse" results, making the example non-reproducible.

🔧 Proposed fix
-const q = "vitalik";
+const q = "reverse";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts`
at line 55, The SDK example defines const q = "vitalik" which doesn't match the
SQL example that searches for "reverse"; update the SDK query parameter (the
const q variable in this file) to use "reverse" so the SDK snippet returns the
same results as the SQL example and the shown result arrays like "reverse" and
"addr.reverse" are reproducible.
docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts (1)

28-135: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Minor data inconsistency between SQL and SDK result snapshots.

The 5th result row differs between SQL and SDK examples for domain "sfmpfv44d0mig.eth":

  • SQL result (lines 54-58): type: "ENSv1Domain", ID ending with ...0xb6fb...
  • SDK result (lines 129-134): type: "ENSv2Domain", ID ending with ...0x64c8...

While both may be valid domain records (if the name exists in both v1 and v2), documentation examples are more reliable when SQL and SDK variants return identical result sets for the same query. This discrepancy could stem from snapshots captured at different times or a subtle ordering difference (canonical_name vs __canonicalNamePrefix).

Consider re-capturing both snapshots from the same database state, or explicitly note in the documentation that result ordering for tied names may vary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts`
around lines 28 - 135, The SQL vs SDK example snapshots diverge for the
"sfmpfv44d0mig.eth" row (fields canonical_name/canonicalName and type/id
mismatch); re-run both the SQL and SDK queries from the same database state,
ensure the same ordering key (use __canonicalNamePrefix in both examples or
explicitly sort by canonical_name) and then update the two result snapshots so
the type and id for "sfmpfv44d0mig.eth" match (or add a short note explaining
that tied names may yield different domain versions); update the result arrays
(result in SQL block and result in SDK block) to be identical after re-capture.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts`:
- Line 55: The SDK example defines const q = "vitalik" which doesn't match the
SQL example that searches for "reverse"; update the SDK query parameter (the
const q variable in this file) to use "reverse" so the SDK snippet returns the
same results as the SQL example and the shown result arrays like "reverse" and
"addr.reverse" are reproducible.

In `@docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts`:
- Around line 102-108: The SDK .select block should include the same
registration pricing fields as the SQL example: add
ensIndexerSchema.registration.grace_period, ensIndexerSchema.registration.base,
and ensIndexerSchema.registration.premium to the .select call (alongside expiry,
start, and type), then update the SDK result array/object in the example to
include the corresponding grace_period, base, and premium entries so both SQL
and SDK examples present the same fields.

In `@docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts`:
- Around line 28-135: The SQL vs SDK example snapshots diverge for the
"sfmpfv44d0mig.eth" row (fields canonical_name/canonicalName and type/id
mismatch); re-run both the SQL and SDK queries from the same database state,
ensure the same ordering key (use __canonicalNamePrefix in both examples or
explicitly sort by canonical_name) and then update the two result snapshots so
the type and id for "sfmpfv44d0mig.eth" match (or add a short note explaining
that tied names may yield different domain versions); update the result arrays
(result in SQL block and result in SDK block) to be identical after re-capture.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 743b3c13-bdbf-4902-9e63-34fb17a09917

📥 Commits

Reviewing files that changed from the base of the PR and between 66e5135 and 8b2ce94.

📒 Files selected for processing (15)
  • docs/ensnode.io/config/integrations/starlight/sidebar-topics/integrate.ts
  • docs/ensnode.io/src/components/molecules/unigraph-static-example/UnigraphStaticExample.astro
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/account-domains.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/domain-events.mdx
  • docs/ensnode.io/src/content/docs/docs/integrate/unigraph/examples/expiring-registrations.mdx
  • docs/ensnode.io/src/data/unigraph-examples/account-domains.ts
  • docs/ensnode.io/src/data/unigraph-examples/domain-by-name.ts
  • docs/ensnode.io/src/data/unigraph-examples/domain-events.ts
  • docs/ensnode.io/src/data/unigraph-examples/domains-fuzzy-search-by-name.ts
  • docs/ensnode.io/src/data/unigraph-examples/expiring-registrations.ts
  • docs/ensnode.io/src/data/unigraph-examples/indexing-status.ts
  • docs/ensnode.io/src/data/unigraph-examples/latest-registrations.ts
  • docs/ensnode.io/src/data/unigraph-examples/subdomains-by-parent-name.ts
  • docs/ensnode.io/src/data/unigraph-examples/types.ts
  • docs/ensnode.io/src/data/unigraph-examples/utils.ts

<SqlResultTable rows={sql.result} />

<StaticExampleNote class="px-5 pb-5">{sql.resultNote ?? defaultResultNote}</StaticExampleNote>
<StaticExampleNote class="px-5 pb-5" set:html={sql.resultNote ?? defaultResultNote} />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

set:html directive applied to Astro component (StaticExampleNote) instead of HTML element, preventing proper rendering of HTML content

Fix on Vercel

sdk: {
codeSnippet: `import { and, eq, sql } from "drizzle-orm";

const q = "vitalik";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
const q = "vitalik";
const q = "reverse";

SDK example searches for "vitalik" but SQL example and all result data show searching for "reverse", causing internal inconsistency

Fix on Vercel

FROM "ensindexer_0".domains d
WHERE d.canonical = true
AND d.owner_id = '0xffffffffff52d316b7bd028358089bc8066b8f80'
ORDER BY d.canonical_name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
ORDER BY d.canonical_name
ORDER BY d.__canonical_name_prefix

SQL example orders by canonical_name instead of __canonical_name_prefix, defeating the purpose of the materialized column and missing the performance optimization

Fix on Vercel

FROM "ensindexer_0".domains d
JOIN parent p ON d.registry_id = p.subregistry_id
WHERE d.canonical = true
ORDER BY d.canonical_name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
ORDER BY d.canonical_name
ORDER BY d.__canonical_name_prefix

SQL example orders by canonical_name instead of __canonical_name_prefix, defeating the materialized column optimization and composite index

Fix on Vercel

@@ -0,0 +1,10 @@
export interface CodeExample {
codeSnippet: string;
result: unknown;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
result: unknown;
result: Record<string, unknown> | Array<Record<string, unknown>>;

CodeExample.result is typed as 'unknown', allowing invalid result shapes to pass type checking instead of catching them at compile time

Fix on Vercel

similarity(canonical_name, 'reverse') as name_similarity,
id
FROM "ensindexer_0".domains
WHERE __canonicalNamePrefix % 'reverse'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
WHERE __canonicalNamePrefix % 'reverse'
WHERE __canonical_name_prefix % 'reverse'

SQL snippet uses camelCase column name __canonicalNamePrefix instead of snake_case __canonical_name_prefix, causing query failures against the database.

Fix on Vercel

Performing SQL queries on the ENS Unigraph requires that you have the `unigraph` plugin activated in your ENSNode instance. [Learn more](/docs/services/ensindexer/usage/configuration)
:::

Fetch recent events for a Domain by its canonical name. This example joins the `events`, `domain_events`, and `domains` tables to retrieve domain events associated with the `vitalik.eth` name. See [Connect](/docs/integrate/unigraph/examples) for setup.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
Fetch recent events for a Domain by its canonical name. This example joins the `events`, `domain_events`, and `domains` tables to retrieve domain events associated with the `vitalik.eth` name. See [Connect](/docs/integrate/unigraph/examples) for setup.
Fetch recent events for a Domain by its canonical name. This example joins the `events`, `domain_events`, and `domains` tables to retrieve domain events associated with the `wrapnation.eth` name. See [Connect](/docs/integrate/unigraph/examples) for setup.

Documentation text mentions "vitalik.eth" but the actual example code uses "wrapnation.eth"

Fix on Vercel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants