Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .changeset/petite-peaches-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ensnode/ensnode-schema": minor
---

Split database schemas into Ponder Schema, ENSIndexer Schema, and ENSNode Schema.
5 changes: 5 additions & 0 deletions .changeset/vast-comics-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ensapi": minor
---

Updated ENSDb connections to be always read-only.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Updated ENSDb connections to be always read-only.
Updated ENSApi's connections to ENSDb to be always read-only.

11 changes: 7 additions & 4 deletions apps/ensapi/src/lib/db.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import config from "@/config";

import * as schema from "@ensnode/ensnode-schema";
import * as ensIndexerSchema from "@ensnode/ensnode-schema/ensindexer";

import { makeDrizzle } from "@/lib/handlers/drizzle";
import { makeReadOnlyDrizzle } from "@/lib/handlers/drizzle";

export const db = makeDrizzle({
/**
* Read-only Drizzle instance for ENSDb queries to ENSIndexer Schema
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
* Read-only Drizzle instance for ENSDb queries to ENSIndexer Schema
* Read-only Drizzle instance for ENSApi's queries to the ENSIndexer Schema in ENSDb

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How should we think about which db schemas ENSApi reads from? My understanding is it should read from both the ENSNode and ENSIndexer schemas. But here it's only reading from ENSIndexer. Why is that?

Assuming ENSApi really does read from multiple schemas than that would suggest it's wrong to call this variable something generic such as db because this db is only for the ENSIndexer schema specifically.

Please invest more effort into naming and communicating ideas.

*/
export const db = makeReadOnlyDrizzle({
databaseUrl: config.databaseUrl,
Copy link
Copy Markdown
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 related comment about renaming things in a follow-up issue.

I'd also like to see databaseUrl be renamed to ensDbUrl. This idea should be applied everywhere across our monorepo.

Eager to make all our terminology use be 100% precise, consistent, and aligned everywhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

databaseSchema: config.databaseSchemaName,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please create a follow-up issue for us to rename all the environment variable names / other variable names to be more precise and consistent in how they relate to our improved architecture.

For example: We should stop using the generic databaseSchema terminology. This begs the question: "database schema for what exactly???". The solution is we should use more precise terminology such as ensindexerSchema or ENSINDEXER_SCHEMA.

Suggest to create a follow-up issue for this and action it in a separate PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

schema,
schema: ensIndexerSchema,
});
24 changes: 18 additions & 6 deletions apps/ensapi/src/lib/handlers/drizzle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { setDatabaseSchema } from "@ponder/client";
import { drizzle } from "drizzle-orm/node-postgres";
import { parseIntoClientConfig } from "pg-connection-string";

import { makeLogger } from "@/lib/logger";

Expand All @@ -8,21 +9,32 @@ type Schema = { [name: string]: unknown };
const logger = makeLogger("drizzle");

/**
* Makes a Drizzle DB object.
* Makes a read-only Drizzle DB object.
*/
export const makeDrizzle = <SCHEMA extends Schema>({
export const makeReadOnlyDrizzle = <SCHEMA extends Schema>({
schema,
databaseUrl,
databaseSchema,
}: {
schema: SCHEMA;
databaseUrl: string;
databaseSchema: string;
databaseSchema?: string;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This code is confusing. Why is there one variable named schema and another named databaseSchema. This is a bad code smell. It's not clear which is which or why one is different than the other.

We really need you to put more effort into this!!!

}) => {
// monkeypatch schema onto tables
setDatabaseSchema(schema, databaseSchema);
// monkeypatch schema onto tables if databaseSchema is provided
if (databaseSchema) {
setDatabaseSchema(schema, databaseSchema);
}

return drizzle(databaseUrl, {
const parsedConfig = parseIntoClientConfig(databaseUrl);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Naming here is bad.

const existingOptions = parsedConfig.options || "";
const readOnlyOption = "-c default_transaction_read_only=on";

return drizzle({
connection: {
...parsedConfig,
// Combine existing options from URL with read-only requirement
options: existingOptions ? `${existingOptions} ${readOnlyOption}` : readOnlyOption,
},
schema,
casing: "snake_case",
logger: {
Expand Down
6 changes: 0 additions & 6 deletions apps/ensindexer/src/lib/ensdb-client/drizzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// We currently duplicate the makeDrizzle function, as we don't have
// a shared package for backend code yet. When we do, we can move
// this function to the shared package and import it in both places.
import { setDatabaseSchema } from "@ponder/client";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we remove the @ponder/client import from ensindexer now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, apps/ensapi/src/lib/handlers/drizzle.ts file still uses that import and it's required for working with database objects from the ENSIndexer Schema.

import { drizzle } from "drizzle-orm/node-postgres";

type Schema = { [name: string]: unknown };
Expand All @@ -13,14 +12,9 @@ type Schema = { [name: string]: unknown };
export const makeDrizzle = <SCHEMA extends Schema>({
schema,
databaseUrl,
databaseSchema,
}: {
schema: SCHEMA;
databaseUrl: string;
databaseSchema: string;
}) => {
// monkeypatch schema onto tables
setDatabaseSchema(schema, databaseSchema);

return drizzle(databaseUrl, { schema, casing: "snake_case" });
};
4 changes: 4 additions & 0 deletions apps/ensindexer/src/lib/ensdb-client/ensdb-client.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export const databaseUrl = "postgres://user:pass@localhost:5432/ensdb";

export const databaseSchemaName = "public";

// This is the same as the default value of config.databaseSchemaName,
// which is used as the ensIndexerRef for multi-tenancy in ENSDb.
export const ensIndexerRef = databaseSchemaName;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Fix the bad naming.


export const publicConfig = {
databaseSchemaName,
ensRainbowPublicConfig: {
Expand Down
56 changes: 16 additions & 40 deletions apps/ensindexer/src/lib/ensdb-client/ensdb-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from "vitest";

import { ensNodeMetadata } from "@ensnode/ensnode-schema";
import * as ensNodeSchema from "@ensnode/ensnode-schema/ensnode";
import {
deserializeCrossChainIndexingStatusSnapshot,
EnsNodeMetadataKeys,
Expand Down Expand Up @@ -50,26 +50,20 @@ describe("EnsDbClient", () => {
describe("getEnsDbVersion", () => {
it("returns undefined when no record exists", async () => {
// arrange
const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);

// act & assert
await expect(client.getEnsDbVersion()).resolves.toBeUndefined();

expect(selectMock).toHaveBeenCalledTimes(1);
expect(fromMock).toHaveBeenCalledWith(ensNodeMetadata);
expect(fromMock).toHaveBeenCalledWith(ensNodeSchema.ensNodeMetadata);
});

it("returns value when one record exists", async () => {
// arrange
selectResult.current = [{ value: "0.1.0" }];

const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);

// act & assert
await expect(client.getEnsDbVersion()).resolves.toBe("0.1.0");
Expand All @@ -81,10 +75,7 @@ describe("EnsDbClient", () => {
// arrange
selectResult.current = [{ value: "0.1.0" }, { value: "0.1.1" }];

const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);

// act & assert
await expect(client.getEnsDbVersion()).rejects.toThrowError(/ensdb_version/i);
Expand All @@ -94,10 +85,7 @@ describe("EnsDbClient", () => {
describe("getEnsIndexerPublicConfig", () => {
it("returns undefined when no record exists", async () => {
// arrange
const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);

// act & assert
await expect(client.getEnsIndexerPublicConfig()).resolves.toBeUndefined();
Expand All @@ -108,10 +96,7 @@ describe("EnsDbClient", () => {
const serializedConfig = serializeEnsIndexerPublicConfig(ensDbClientMock.publicConfig);
selectResult.current = [{ value: serializedConfig }];

const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);

// act & assert
await expect(client.getEnsIndexerPublicConfig()).resolves.toStrictEqual(
Expand All @@ -125,10 +110,7 @@ describe("EnsDbClient", () => {
// arrange
selectResult.current = [{ value: ensDbClientMock.serializedSnapshot }];

const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);
const expected = deserializeCrossChainIndexingStatusSnapshot(
ensDbClientMock.serializedSnapshot,
);
Expand All @@ -141,22 +123,20 @@ describe("EnsDbClient", () => {
describe("upsertEnsDbVersion", () => {
it("writes the database version metadata", async () => {
// arrange
const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);

// act
await client.upsertEnsDbVersion("0.2.0");

// assert
expect(insertMock).toHaveBeenCalledWith(ensNodeMetadata);
expect(insertMock).toHaveBeenCalledWith(ensNodeSchema.ensNodeMetadata);
expect(valuesMock).toHaveBeenCalledWith({
ensIndexerRef: ensDbClientMock.ensIndexerRef,
key: EnsNodeMetadataKeys.EnsDbVersion,
value: "0.2.0",
});
expect(onConflictDoUpdateMock).toHaveBeenCalledWith({
target: ensNodeMetadata.key,
target: [ensNodeSchema.ensNodeMetadata.ensIndexerRef, ensNodeSchema.ensNodeMetadata.key],
set: { value: "0.2.0" },
});
});
Expand All @@ -165,17 +145,15 @@ describe("EnsDbClient", () => {
describe("upsertEnsIndexerPublicConfig", () => {
it("serializes and writes the public config", async () => {
// arrange
const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);
const expectedValue = serializeEnsIndexerPublicConfig(ensDbClientMock.publicConfig);

// act
await client.upsertEnsIndexerPublicConfig(ensDbClientMock.publicConfig);

// assert
expect(valuesMock).toHaveBeenCalledWith({
ensIndexerRef: ensDbClientMock.ensIndexerRef,
key: EnsNodeMetadataKeys.EnsIndexerPublicConfig,
value: expectedValue,
});
Expand All @@ -185,10 +163,7 @@ describe("EnsDbClient", () => {
describe("upsertIndexingStatusSnapshot", () => {
it("serializes and writes the indexing status snapshot", async () => {
// arrange
const client = new EnsDbClient(
ensDbClientMock.databaseUrl,
ensDbClientMock.databaseSchemaName,
);
const client = new EnsDbClient(ensDbClientMock.databaseUrl, ensDbClientMock.ensIndexerRef);
const snapshot = deserializeCrossChainIndexingStatusSnapshot(
ensDbClientMock.serializedSnapshot,
);
Expand All @@ -199,6 +174,7 @@ describe("EnsDbClient", () => {

// assert
expect(valuesMock).toHaveBeenCalledWith({
ensIndexerRef: ensDbClientMock.ensIndexerRef,
key: EnsNodeMetadataKeys.EnsIndexerIndexingStatus,
value: expectedValue,
});
Expand Down
72 changes: 33 additions & 39 deletions apps/ensindexer/src/lib/ensdb-client/ensdb-client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import { eq, sql } from "drizzle-orm/sql";
import { and, eq, sql } from "drizzle-orm/sql";

import { ensNodeMetadata } from "@ensnode/ensnode-schema";
import * as ensNodeSchema from "@ensnode/ensnode-schema/ensnode";
import {
type CrossChainIndexingStatusSnapshot,
deserializeCrossChainIndexingStatusSnapshot,
Expand All @@ -20,21 +20,12 @@ import {

import { makeDrizzle } from "./drizzle";

/**
* ENSDb Client Schema
*
* Includes schema definitions for {@link EnsDbClient} queries and mutations.
*/
const schema = {
ensNodeMetadata,
};

/**
* Drizzle database
*
* Allows interacting with Postgres database for ENSDb, using Drizzle ORM.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I find it confusing exactly what this class has responsibility for.

Is it for everything in an ENSDb, or is it only for the ENSNode schema in an ENSDb?

Please make all the naming and use of language 100% precise and clear.

*/
interface DrizzleDb extends NodePgDatabase<typeof schema> {}
interface DrizzleDb extends NodePgDatabase<typeof ensNodeSchema> {}

/**
* ENSDb Client
Expand All @@ -53,16 +44,22 @@ export class EnsDbClient implements EnsDbClientQuery, EnsDbClientMutation {
*/
private db: DrizzleDb;

/**
* ENSIndexer reference string for multi-tenancy in ENSDb.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Be more precise. Multi-tenancy of what exactly?

Always write for an assumed audience that isn't familiar with all the context yet.

*/
private ensIndexerRef: string;

/**
* @param databaseUrl connection string for ENSDb Postgres database
* @param databaseSchemaName Postgres schema name for ENSDb tables
* @param ensIndexerRef reference string for ENSIndexer instance (used for multi-tenancy in ENSDb)
*/
constructor(databaseUrl: string, databaseSchemaName: string) {
constructor(databaseUrl: string, ensIndexerRef: string) {
this.db = makeDrizzle({
databaseSchema: databaseSchemaName,
databaseUrl,
schema,
schema: ensNodeSchema,
});
Comment on lines +56 to 60
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Mar 12, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ship the ensnode schema/PK migration before rolling this out.

This client now hard-depends on the ensnode schema and on ON CONFLICT (ensIndexerRef, key), but the PR notes say migration management for that schema is deferred to a follow-up PR and local testing required manual schema creation. On any existing ENSDb that has not been migrated yet, the first read/write here will fail at runtime. Please land the schema + composite-PK migration in the same release, or explicitly block deployment of this client until the database is migrated.

Also applies to: 182-190

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ensindexer/src/lib/ensdb-client/ensdb-client.ts` around lines 56 - 60,
The code in the constructor uses makeDrizzle with ensNodeSchema and the runtime
depends on the composite primary key (ON CONFLICT (ensIndexerRef, key)), so
ensure the database migration that creates the ensnode table and adds the
composite PK is shipped together with this client; add or reference the
migration that creates the ensnode schema and the composite primary key
(ensIndexerRef,key) before enabling/rolling out this code, or add an explicit
startup check in the constructor (or in the init path used by constructor) that
verifies the ensnode table and composite index exist and fails fast with a clear
error if not, preventing deployment against unmigrated ENSDb (also apply the
same fix to the other usages referenced around the code block at symbols
handling reads/writes in lines ~182-190).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's the plan, PRs #1753 and #1754 will address missing pieces.

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.

@tk-o, understood — thanks for the context! Tracking the follow-up in #1753 and #1754 sounds like a solid plan. I'll keep an eye on those PRs to confirm the ensnode schema creation and the composite PK migration land before this client is deployed against production ENSDb instances.


🧠 Learnings used
Learnt from: CR
Repo: namehash/ensnode PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T05:28:59.644Z
Learning: Applies to apps/ensindexer/**/*.ts : Use Ponder as the indexer framework for blockchain indexing

Learnt from: tk-o
Repo: namehash/ensnode PR: 1715
File: apps/ensindexer/ponder/src/api/handlers/ensnode-api.ts:21-25
Timestamp: 2026-03-04T11:40:35.846Z
Learning: In `apps/ensindexer/ponder/src/api/handlers/ensnode-api.ts`, the guard `if (typeof publicConfig === "undefined") { throw new Error("Unreachable: ...") }` in the `/config` handler is intentionally unreachable. The `EnsDbWriterWorker` populates the ENSIndexer public config into ENSDb at startup (fail-fast), so the HTTP layer is guaranteed to only serve requests after the config is available. The `throw` is a defensive invariant, not a real error path.

Learnt from: tk-o
Repo: namehash/ensnode PR: 1639
File: packages/ensnode-sdk/src/ensapi/api/indexing-status/zod-schemas.ts:21-76
Timestamp: 2026-02-16T17:53:46.139Z
Learning: In the ENSNode SDK (`packages/ensnode-sdk`), schema builder functions exported from `zod-schemas.ts` files (e.g., `makeEnsApiIndexingStatusResponseSchema`) are considered internal API, not public API. These can have breaking changes without requiring deprecated aliases, even when exported via the `internal` entry point.

Learnt from: Goader
Repo: namehash/ensnode PR: 1663
File: packages/ens-referrals/src/v1/award-models/rev-share-limit/metrics.ts:74-96
Timestamp: 2026-02-24T15:53:06.633Z
Learning: In TypeScript code reviews, prefer placing invariants on type aliases only when the invariant is context-independent or reused across multiple fields. If an invariant depends on surrounding rules or object semantics (e.g., field-specific metrics), keep the invariant as a field JSDoc instead. This guideline applies to TS files broadly (e.g., the repo's v1/award-models and similar modules).

Learnt from: CR
Repo: namehash/ensnode PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T05:28:59.644Z
Learning: Applies to **/*.ts : Use Drizzle as the ORM for database interactions


this.ensIndexerRef = ensIndexerRef;
}

/**
Expand Down Expand Up @@ -147,15 +144,21 @@ export class EnsDbClient implements EnsDbClientQuery, EnsDbClientMutation {
*
* @returns selected record in ENSDb.
* @throws when more than one matching metadata record is found
* (should be impossible given the PK constraint on 'key')
* (should be impossible given the composite PK constraint on
* 'ensIndexerRef' and 'key').
*/
private async getEnsNodeMetadata<EnsNodeMetadataType extends SerializedEnsNodeMetadata>(
metadata: Pick<EnsNodeMetadataType, "key">,
): Promise<EnsNodeMetadataType["value"] | undefined> {
const result = await this.db
.select()
.from(ensNodeMetadata)
.where(eq(ensNodeMetadata.key, metadata.key));
.from(ensNodeSchema.ensNodeMetadata)
.where(
and(
eq(ensNodeSchema.ensNodeMetadata.ensIndexerRef, this.ensIndexerRef),
eq(ensNodeSchema.ensNodeMetadata.key, metadata.key),
),
);

if (result.length === 0) {
return undefined;
Expand All @@ -176,25 +179,16 @@ export class EnsDbClient implements EnsDbClientQuery, EnsDbClientMutation {
private async upsertEnsNodeMetadata<
EnsNodeMetadataType extends SerializedEnsNodeMetadata = SerializedEnsNodeMetadata,
>(metadata: EnsNodeMetadataType): Promise<void> {
await this.db.transaction(async (tx) => {
// Ponder live-query triggers insert into live_query_tables.
// Because this worker writes outside the Ponder runtime connection pool,
// the temp table must be ensured to exist on this connection. Without this,
// the upsert would fail with "relation 'live_query_tables' does not exist" error.
await tx.execute(
sql`CREATE TEMP TABLE IF NOT EXISTS live_query_tables (table_name TEXT PRIMARY KEY)`,
);

await tx
.insert(ensNodeMetadata)
.values({
key: metadata.key,
value: metadata.value,
})
.onConflictDoUpdate({
target: ensNodeMetadata.key,
set: { value: metadata.value },
});
});
await this.db
.insert(ensNodeSchema.ensNodeMetadata)
.values({
ensIndexerRef: this.ensIndexerRef,
key: metadata.key,
value: metadata.value,
})
.onConflictDoUpdate({
target: [ensNodeSchema.ensNodeMetadata.ensIndexerRef, ensNodeSchema.ensNodeMetadata.key],
set: { value: metadata.value },
});
}
}
6 changes: 5 additions & 1 deletion apps/ensindexer/src/lib/ensdb-client/singleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import config from "@/config";

import { EnsDbClient } from "./ensdb-client";

// config.databaseSchemaName is unique per ENSIndexer instance and is used as the ensIndexerRef
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's big naming problems here. I should not have the responsibility to catch and fix these.

You should notice how there's 3 different terms being used for exactly the same idea here and fix it before sending to me for review.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

After reading feedback you shared, I see more clearly how this could just be ensIndexerSchemaName term.

// tenant key in the shared ENSNode schema (ensnode.*).
const ensIndexerRef = config.databaseSchemaName;

/**
* Singleton instance of {@link EnsDbClient} for use in ENSIndexer.
*/
export const ensDbClient = new EnsDbClient(config.databaseUrl, config.databaseSchemaName);
export const ensDbClient = new EnsDbClient(config.databaseUrl, ensIndexerRef);
Loading
Loading