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/humble-pets-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ensapi": minor
---

Replaced ENSIndexer Public Config source, from ENSIndexer to 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.

Is it fair to also say that this change fully decouples ENSApi and ENSIndexer?

5 changes: 4 additions & 1 deletion apps/ensapi/.env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ ENSINDEXER_URL=http://localhost:42069
# It should be in the format of `postgresql://<username>:<password>@<host>:<port>/<database>`
#
# See https://ensnode.io/ensindexer/usage/configuration/ for additional information.
# NOTE that ENSApi does NOT need to define DATABASE_SCHEMA, as it is inferred from the connected ENSIndexer's Config.
DATABASE_URL=postgresql://dbuser:abcd1234@localhost:5432/my_database

# ENSDb: Database Schema name for ENSIndexer Schema
# Required. Should match the DATABASE_SCHEMA used by the connected ENSIndexer.
DATABASE_SCHEMA=public
Comment on lines +17 to +19
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
# ENSDb: Database Schema name for ENSIndexer Schema
# Required. Should match the DATABASE_SCHEMA used by the connected ENSIndexer.
DATABASE_SCHEMA=public
# Schema name for the "ENSIndexer Schema" in ENSDb that ENSApi should read indexed data from.
# Required. Should match the ENSINDEXER_SCHEMA environment variable in ENSIndexer that ENSApi should read indexed data from.
ENSINDEXER_SCHEMA=public

Comment on lines +17 to +19
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.

Why are we promoting the use of public as the default value for the ENSINDEXER_SCHEMA? This will create more pain for devs.

Better to name the default something like ensindexer_0. That both makes it more intuitive how it's from ENSIndexer and it also plants a seed in people's minds how they can just increment the number if they want to build a new ENSIndexer schema.

Please think more about the mental model of devs who are new to ENSNode: they have a big learning curve and we should be doing everything we can to help them.

Please apply this idea everywhere across our monorepo.


# ENSApi: RPC Configuration
# Required. ENSApi requires an HTTP RPC to the connected ENSIndexer's ENS Root Chain, which depends
# on ENSIndexer's NAMESPACE (ex: mainnet, sepolia, ens-test-env). This ENS Root Chain RPC
Expand Down
23 changes: 23 additions & 0 deletions apps/ensapi/src/config/config.schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@ vi.mock("@/lib/logger", () => ({
error: vi.fn(),
info: vi.fn(),
},
makeLogger: vi.fn(() => ({
error: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
warn: vi.fn(),
trace: vi.fn(),
})),
}));

const VALID_RPC_URL = "https://eth-sepolia.g.alchemy.com/v2/1234";

const BASE_ENV = {
DATABASE_URL: "postgresql://user:password@localhost:5432/mydb",
DATABASE_SCHEMA: "public",
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 related comment, even for test files.

ENSINDEXER_URL: "http://localhost:42069",
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 this be removed 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.

Not yet, but will be removed in PR #1716.

RPC_URL_1: VALID_RPC_URL,
} satisfies EnsApiEnvironment;
Expand All @@ -50,6 +58,20 @@ const ENSINDEXER_PUBLIC_CONFIG = {
},
} satisfies ENSIndexerPublicConfig;

// Mock EnsDbClient - must be defined after ENSINDEXER_PUBLIC_CONFIG since vi.mock is hoisted
// We'll use a simple class mock and configure it in beforeEach
const mockGetVersion = vi.fn().mockResolvedValue("1.0.0");
const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG);
const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(null);

vi.mock("@/lib/ensdb-client/ensdb-client", () => ({
EnsDbClient: class MockEnsDbClient {
getVersion = mockGetVersion;
Comment on lines +63 to +69
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The EnsDbClient mock doesn’t match the real EnsDbClientQuery interface: it defines getVersion instead of getEnsDbVersion, and getIndexingStatusSnapshot resolves to null instead of undefined. Aligning the mock with the interface will prevent future tests from silently diverging when those methods start being used.

Suggested change
const mockGetVersion = vi.fn().mockResolvedValue("1.0.0");
const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG);
const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(null);
vi.mock("@/lib/ensdb-client/ensdb-client", () => ({
EnsDbClient: class MockEnsDbClient {
getVersion = mockGetVersion;
const mockGetEnsDbVersion = vi.fn().mockResolvedValue("1.0.0");
const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG);
const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(undefined);
vi.mock("@/lib/ensdb-client/ensdb-client", () => ({
EnsDbClient: class MockEnsDbClient {
getEnsDbVersion = mockGetEnsDbVersion;

Copilot uses AI. Check for mistakes.
getEnsIndexerPublicConfig = mockGetEnsIndexerPublicConfig;
getIndexingStatusSnapshot = mockGetIndexingStatusSnapshot;
},
}));
Comment on lines +63 to +73
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.

Mock method name getVersion doesn't match actual implementation

The mock defines getVersion (line 69) but the real EnsDbClient exposes getEnsDbVersion(). The EnsDbClientQuery interface is the source of truth here, but the discrepancy can be misleading and won't surface as a type error because the mock class is untyped.

Suggested change
const mockGetVersion = vi.fn().mockResolvedValue("1.0.0");
const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG);
const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(null);
vi.mock("@/lib/ensdb-client/ensdb-client", () => ({
EnsDbClient: class MockEnsDbClient {
getVersion = mockGetVersion;
getEnsIndexerPublicConfig = mockGetEnsIndexerPublicConfig;
getIndexingStatusSnapshot = mockGetIndexingStatusSnapshot;
},
}));
vi.mock("@/lib/ensdb-client/ensdb-client", () => ({
EnsDbClient: class MockEnsDbClient {
getEnsDbVersion = mockGetVersion;
getEnsIndexerPublicConfig = mockGetEnsIndexerPublicConfig;
getIndexingStatusSnapshot = mockGetIndexingStatusSnapshot;
},
}));


const mockFetch = vi.fn();
vi.stubGlobal("fetch", mockFetch);

Comment on lines 75 to 77
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

fetch is still stubbed and configured in these tests, but buildConfigFromEnvironment no longer calls fetch after the switch to EnsDbClient. This makes the test setup misleading and reduces confidence that the new ENSDb path is being exercised; consider removing the fetch mocking and instead asserting EnsDbClient construction + getEnsIndexerPublicConfig() calls.

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -116,6 +138,7 @@ describe("buildConfigFromEnvironment", () => {

const TEST_ENV: EnsApiEnvironment = {
DATABASE_URL: BASE_ENV.DATABASE_URL,
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 to rename DATABASE_URL to ENSDB_URL everywhere.

We should be maximally aligning our terminology 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.

DATABASE_SCHEMA: BASE_ENV.DATABASE_SCHEMA,
ENSINDEXER_URL: BASE_ENV.ENSINDEXER_URL,
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 this be removed 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.

Not yet, but will be removed in PR #1716.

};

Expand Down
33 changes: 21 additions & 12 deletions apps/ensapi/src/config/config.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import packageJson from "@/../package.json" with { type: "json" };

import pRetry from "p-retry";
import { parse as parseConnectionString } from "pg-connection-string";
import { prettifyError, ZodError, z } from "zod/v4";

Expand All @@ -21,7 +20,7 @@ import {
import { ENSApi_DEFAULT_PORT } from "@/config/defaults";
import type { EnsApiEnvironment } from "@/config/environment";
import { invariant_ensIndexerPublicConfigVersionInfo } from "@/config/validations";
import { fetchENSIndexerConfig } from "@/lib/fetch-ensindexer-config";
import { EnsDbClient } from "@/lib/ensdb-client/ensdb-client";
import logger from "@/lib/logger";

export const DatabaseUrlSchema = z.string().refine(
Expand Down Expand Up @@ -77,6 +76,19 @@ const EnsApiConfigSchema = z

export type EnsApiConfig = z.infer<typeof EnsApiConfigSchema>;

/**
* Builds an instance of {@link EnsDbClient} using environment variables.
*
* @returns instance of {@link EnsDbClient}
* @throws Error with formatted validation messages if environment parsing fails
*/
function buildEnsDbClientFromEnvironment(env: EnsApiEnvironment): EnsDbClient {
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.

Why is this function in this file? This seems like bad code organization.

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.

I can move it to a separate file, but it's also a temporary measure that will be resolved with PR #1716, when there will be no need to call ensDbClient when building configuration from environment.

Copy link
Copy Markdown
Contributor Author

@tk-o tk-o Mar 13, 2026

Choose a reason for hiding this comment

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

Here, I'm just updating this bit, where ENSIndexer client configuration was parsed from env, and then ENSIndexer client object would be created from it.

If you like, I can replace buildEnsDbClientFromEnvironment function with inline code, just as the original.

const ensIndexerUrl = EnsIndexerUrlSchema.parse(env.ENSINDEXER_URL);
const ensIndexerPublicConfig = await pRetry(() => fetchENSIndexerConfig(ensIndexerUrl), {
retries: 3,
onFailedAttempt: ({ error, attemptNumber, retriesLeft }) => {
logger.info(
`ENSIndexer Config fetch attempt ${attemptNumber} failed (${error.message}). ${retriesLeft} retries left.`,
);
},
});

@lightwalker-eth advice appreciated.

const databaseUrl = DatabaseUrlSchema.parse(env.DATABASE_URL);
const ensIndexerSchemaName = DatabaseSchemaNameSchema.parse(env.DATABASE_SCHEMA);

return new EnsDbClient(databaseUrl, ensIndexerSchemaName);
Comment on lines +87 to +89
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

DATABASE_SCHEMA is parsed into ensIndexerSchemaName and passed as the ensIndexerRef argument to EnsDbClient. Since ensIndexerRef represents the ENSIndexer instance identifier (often the schema name), consider renaming ensIndexerSchemaName to ensIndexerRef (and/or clarifying via comments) to avoid confusing it with ensIndexerPublicConfig.databaseSchemaName used later for queries.

Suggested change
const ensIndexerSchemaName = DatabaseSchemaNameSchema.parse(env.DATABASE_SCHEMA);
return new EnsDbClient(databaseUrl, ensIndexerSchemaName);
// NOTE: This is the ENSIndexer instance identifier (often the schema name), used as `ensIndexerRef` in EnsDbClient.
const ensIndexerRef = DatabaseSchemaNameSchema.parse(env.DATABASE_SCHEMA);
return new EnsDbClient(databaseUrl, ensIndexerRef);

Copilot uses AI. Check for mistakes.
}

/**
* Builds the EnsApiConfig from an EnsApiEnvironment object, fetching the EnsIndexerPublicConfig.
*
Expand All @@ -85,16 +97,13 @@ export type EnsApiConfig = z.infer<typeof EnsApiConfigSchema>;
*/
export async function buildConfigFromEnvironment(env: EnsApiEnvironment): Promise<EnsApiConfig> {
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 needs refactoring / renaming.

It's not appropriate to call this function buildConfigFromEnvironment.

Copy link
Copy Markdown
Contributor Author

@tk-o tk-o Mar 13, 2026

Choose a reason for hiding this comment

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

@lightwalker-eth this week, we discussed how managing configuration objects in ENSApi has to be changed.

I suggest we address the refactoring buildConfigFromEnvironment function in PR #1716. The PR 1716 needs PRs 1752, 1753, and 1754 to be merged first.

try {
const ensIndexerUrl = EnsIndexerUrlSchema.parse(env.ENSINDEXER_URL);

const ensIndexerPublicConfig = await pRetry(() => fetchENSIndexerConfig(ensIndexerUrl), {
retries: 3,
onFailedAttempt: ({ error, attemptNumber, retriesLeft }) => {
logger.info(
`ENSIndexer Config fetch attempt ${attemptNumber} failed (${error.message}). ${retriesLeft} retries left.`,
);
},
});
const ensDbClient = buildEnsDbClientFromEnvironment(env);
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.

In my mind, there should only ever be a single ensDbClient ever created in ENSApi.

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.

PR #1716 wil address required refactoring of how ENSApi configuration is applied. For now, we need to create ensDbClient to fetch EnsIndexerPublicConfig. The target state that PR 1716 will introduce is that there will be no need to call ensDbClient when building configuration from environment.


const ensIndexerPublicConfig = await ensDbClient.getEnsIndexerPublicConfig();

Copy link
Copy Markdown
Contributor

@vercel vercel bot Mar 12, 2026

Choose a reason for hiding this comment

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

Database connection pool created in buildConfigFromEnvironment() is never closed, causing resource leak

Fix on Vercel

if (!ensIndexerPublicConfig) {
throw new Error("Failed to load EnsIndexerPublicConfig from ENSDb.");
}
Comment on lines +100 to +106
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Previously the ENSIndexer public config load had retry/backoff; the ENSDb read now happens once and any transient DB startup error will immediately exit the process. Consider reintroducing a small retry (or a clearer classification of “missing row” vs “DB connectivity/permission error”) around getEnsIndexerPublicConfig() to avoid flakiness during deploy/startup ordering.

Copilot uses AI. Check for mistakes.
Comment on lines 98 to +106
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

PR description implies ENSApi won’t reference an ENSIndexer instance directly, but this function still builds a config that includes ensIndexerUrl (and ENSApi still has codepaths that use it). If full decoupling isn’t part of this PR, consider tightening the PR description/scope so it matches what’s actually changed here (public config source only).

Copilot uses AI. Check for mistakes.
Comment on lines 99 to +106
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.

No retry logic for DB query at startup

The previous implementation used pRetry with 3 retries (and informative log messages) when fetching the ENSIndexer config from the ENSIndexer HTTP endpoint. The new code performs a single database query with no retry: if the database is temporarily unavailable at startup (e.g., during a rolling deploy), ENSApi will immediately log an error and call process.exit(1), requiring an operator to manually restart the service.

Consider wrapping the ensDbClient.getEnsIndexerPublicConfig() call in similar retry logic, for example:

import pRetry from "p-retry";

const ensIndexerPublicConfig = await pRetry(
  () => ensDbClient.getEnsIndexerPublicConfig(),
  {
    retries: 3,
    onFailedAttempt: ({ error, attemptNumber, retriesLeft }) => {
      logger.info(
        `ENSIndexer Public Config DB read attempt ${attemptNumber} failed (${error.message}). ${retriesLeft} retries left.`,
      );
    },
  },
);


const rpcConfigs = buildRpcConfigsFromEnv(env, ensIndexerPublicConfig.namespace);

Expand Down
2 changes: 1 addition & 1 deletion apps/ensapi/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
* their state in `process.env`. This interface is intended to be the source type which then gets
* mapped/parsed into a structured configuration object like `EnsApiConfig`.
*/
export type EnsApiEnvironment = Omit<DatabaseEnvironment, "DATABASE_SCHEMA"> &
export type EnsApiEnvironment = DatabaseEnvironment &
EnsIndexerUrlEnvironment &
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.

Remove this 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.

Not yet, but will be removed in PR #1716.

RpcEnvironment &
PortEnvironment &
Expand Down
139 changes: 139 additions & 0 deletions apps/ensapi/src/lib/ensdb-client/ensdb-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import { and, eq } from "drizzle-orm/sql";

import * as ensNodeSchema from "@ensnode/ensnode-schema/ensnode";
import {
type CrossChainIndexingStatusSnapshot,
deserializeCrossChainIndexingStatusSnapshot,
deserializeEnsIndexerPublicConfig,
type EnsDbClientQuery,
type EnsIndexerPublicConfig,
EnsNodeMetadataKeys,
type SerializedEnsNodeMetadata,
type SerializedEnsNodeMetadataEnsDbVersion,
type SerializedEnsNodeMetadataEnsIndexerIndexingStatus,
type SerializedEnsNodeMetadataEnsIndexerPublicConfig,
} from "@ensnode/ensnode-sdk";

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

/**
* Drizzle database
*
* Allows interacting with Postgres database for ENSDb, using Drizzle ORM.
*/
interface DrizzleDb extends NodePgDatabase<typeof ensNodeSchema> {}

/**
* ENSDb Client
*
* This client exists to provide an abstraction layer for interacting with ENSDb.
* It enables ENSIndexer and ENSApi to decouple from each other, and use
* ENSDb as the integration point between the two (via ENSDb Client).
*
* Enables querying ENSDb data, such as:
* - ENSDb version
* - ENSIndexer Public Config,
* - Indexing Status Snapshot.
*/
export class EnsDbClient implements EnsDbClientQuery {
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.

We're getting so many ideas badly mixed up. This is taking way too much of my time to identify all the problems and suggest solutions. It's very inappropriate to push all of these problems on me.

Issues here include how you're mixing up the idea of ENSDb with the ENSNode Schema in ENSDb.

/**
* Drizzle database instance for ENSDb.
*
* This is a read-only Drizzle instance, since ENSApi should not be
* performing any mutations on the database.
*/
private db: DrizzleDb;

/**
* ENSIndexer reference string for multi-tenancy in ENSDb.
*/
private ensIndexerRef: string;

/**
* @param databaseUrl connection string for ENSDb Postgres database
* @param ensIndexerRef reference string for ENSIndexer instance (used for multi-tenancy in ENSDb)
*/
constructor(databaseUrl: string, ensIndexerRef: string) {
this.db = makeReadOnlyDrizzle({
Copy link
Copy Markdown
Contributor

@vercel vercel bot Mar 12, 2026

Choose a reason for hiding this comment

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

EnsDbClient constructor doesn't pass databaseSchema to makeReadOnlyDrizzle(), causing queries to execute against the wrong schema

Fix on Vercel

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.

Passing the name of target database schema is only required while working with ENSIndexer schema, as the schema name is dynamic in this case. Otherwise, we don't have to pass the schema name, as it's bound to the database object definition.

databaseUrl,
schema: ensNodeSchema,
});

this.ensIndexerRef = ensIndexerRef;
}
Comment on lines +57 to +64
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.

Database connection pool is never closed

makeReadOnlyDrizzle creates a node-postgres connection pool under the hood. When buildConfigFromEnvironment creates an EnsDbClient to fetch the startup config, it makes a single query and then discards the client — but the underlying pg.Pool is never explicitly ended. In Node.js, a pg.Pool holds open TCP connections to Postgres until it is told to end(), so the pool will stay alive for the rest of the process lifetime after the config load.

Since buildConfigFromEnvironment runs once at startup (and the EnsDbClient is not reused), the simplest fix is to expose a close() method and call it after the query:

// in EnsDbClient
async close(): Promise<void> {
  await (this.db as any).$client?.end();
}
// in buildConfigFromEnvironment
const ensDbClient = buildEnsDbClientFromEnvironment(env);
try {
  const ensIndexerPublicConfig = await ensDbClient.getEnsIndexerPublicConfig();
  ...
} finally {
  await ensDbClient.close();
}

Alternatively, consider sharing the EnsDbClient instance across the application lifetime (instead of creating a disposable one just for config loading) so the pool is reused rather than leaked.

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.

The connection must remain open for the process lifetime. It will be needed for loading current indexing status object while handling HTTP traffic.


/**
* @inheritdoc
*/
async getEnsDbVersion(): Promise<string | undefined> {
const record = await this.getEnsNodeMetadata<SerializedEnsNodeMetadataEnsDbVersion>({
key: EnsNodeMetadataKeys.EnsDbVersion,
});

return record;
}

/**
* @inheritdoc
*/
async getEnsIndexerPublicConfig(): Promise<EnsIndexerPublicConfig | undefined> {
const record = await this.getEnsNodeMetadata<SerializedEnsNodeMetadataEnsIndexerPublicConfig>({
key: EnsNodeMetadataKeys.EnsIndexerPublicConfig,
});

if (!record) {
return undefined;
}

return deserializeEnsIndexerPublicConfig(record);
}

/**
* @inheritdoc
*/
async getIndexingStatusSnapshot(): Promise<CrossChainIndexingStatusSnapshot | undefined> {
const record = await this.getEnsNodeMetadata<SerializedEnsNodeMetadataEnsIndexerIndexingStatus>(
{
key: EnsNodeMetadataKeys.EnsIndexerIndexingStatus,
},
);

if (!record) {
return undefined;
}

return deserializeCrossChainIndexingStatusSnapshot(record);
}

/**
* Get ENSNode metadata record
*
* @returns selected record in ENSDb.
* @throws when more than one matching metadata record is found
* (should be impossible given the PK constraint on 'key')
*/
private async getEnsNodeMetadata<EnsNodeMetadataType extends SerializedEnsNodeMetadata>(
metadata: Pick<EnsNodeMetadataType, "key">,
): Promise<EnsNodeMetadataType["value"] | undefined> {
const result = await this.db
.select()
.from(ensNodeSchema.ensNodeMetadata)
.where(
and(
eq(ensNodeSchema.ensNodeMetadata.ensIndexerRef, this.ensIndexerRef),
eq(ensNodeSchema.ensNodeMetadata.key, metadata.key),
),
);

if (result.length === 0) {
return undefined;
}

if (result.length === 1 && result[0]) {
return result[0].value as EnsNodeMetadataType["value"];
}

throw new Error(`There must be exactly one ENSNodeMetadata record for '${metadata.key}' key`);
}
}
17 changes: 0 additions & 17 deletions apps/ensapi/src/lib/fetch-ensindexer-config.ts

This file was deleted.

2 changes: 1 addition & 1 deletion terraform/modules/ensindexer/main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
locals {
common_variables = {
# Common configuration
"DATABASE_SCHEMA" = { value = var.database_schema },
"DATABASE_URL" = { value = var.ensdb_url },
"ALCHEMY_API_KEY" = { value = var.alchemy_api_key }
"QUICKNODE_API_KEY" = { value = var.quicknode_api_key }
Expand Down Expand Up @@ -28,7 +29,6 @@ resource "render_web_service" "ensindexer" {
}

env_vars = merge(local.common_variables, {
"DATABASE_SCHEMA" = { value = var.database_schema },
"ENSRAINBOW_URL" = { value = var.ensrainbow_url },
"LABEL_SET_ID" = { value = var.ensindexer_label_set_id },
"LABEL_SET_VERSION" = { value = var.ensindexer_label_set_version },
Expand Down
Loading