Skip to content

fix: normalize regional API base URL and prevent invalid multi-region…#2942

Open
HarshMN2345 wants to merge 1 commit intomainfrom
fix/api-endpoint-double-region-subdomain
Open

fix: normalize regional API base URL and prevent invalid multi-region…#2942
HarshMN2345 wants to merge 1 commit intomainfrom
fix/api-endpoint-double-region-subdomain

Conversation

@HarshMN2345
Copy link
Copy Markdown
Member

@HarshMN2345 HarshMN2345 commented Mar 29, 2026

… hosts

What does this PR do?

(Provide a description of what this PR does.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Summary by CodeRabbit

  • Refactor
    • Enhanced API endpoint construction logic to improve support for multi-region configurations. Consolidated regional endpoint formatting into centralized utilities for better maintainability and consistency across the application.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

Walkthrough

A new utility module src/lib/helpers/apiEndpoint.ts is introduced to centralize regional API endpoint construction logic. The module exports three functions: stripLeadingRegionSubdomain() removes known region prefixes from hostnames, getRegionSubdomain() maps region identifiers to DNS subdomain prefixes, and buildRegionalV1Endpoint() constructs complete regional v1 endpoint URLs. The existing getApiEndpoint() function in src/lib/stores/sdk.ts is refactored to delegate endpoint construction to buildRegionalV1Endpoint(), removing inline logic and the local getSubdomain() helper.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: normalizing regional API base URLs and preventing invalid multi-region configurations through the introduction of shared endpoint-building utilities.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/api-endpoint-double-region-subdomain

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.

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.

🧹 Nitpick comments (1)
src/lib/helpers/apiEndpoint.ts (1)

71-85: Add focused tests for endpoint normalization edge cases.

Please add helper-level tests for: repeated prefixes, hostnames with ports, unknown region values, and isMultiRegion=false passthrough behavior to lock this logic down.

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

In `@src/lib/helpers/apiEndpoint.ts` around lines 71 - 85, Add focused unit tests
that exercise buildRegionalV1Endpoint and its helpers
(stripLeadingRegionSubdomain, getRegionSubdomain): 1) verify repeated region
prefixes are normalized (e.g., "us-us.example.com" -> single "us."), 2) ensure
hostnames with ports (e.g., "example.com:3000") preserve the port and produce
correct "/v1" path, 3) cover unknown/undefined region values to confirm
getRegionSubdomain returns the expected fallback and the endpoint remains valid,
and 4) assert that when isMultiRegion=false the function returns the hostname
unchanged (passthrough) with protocol and "/v1". Include assertions for both
protocol variants and edge-case inputs to lock down normalization behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/helpers/apiEndpoint.ts`:
- Around line 71-85: Add focused unit tests that exercise
buildRegionalV1Endpoint and its helpers (stripLeadingRegionSubdomain,
getRegionSubdomain): 1) verify repeated region prefixes are normalized (e.g.,
"us-us.example.com" -> single "us."), 2) ensure hostnames with ports (e.g.,
"example.com:3000") preserve the port and produce correct "/v1" path, 3) cover
unknown/undefined region values to confirm getRegionSubdomain returns the
expected fallback and the endpoint remains valid, and 4) assert that when
isMultiRegion=false the function returns the hostname unchanged (passthrough)
with protocol and "/v1". Include assertions for both protocol variants and
edge-case inputs to lock down normalization behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dae0f278-bde9-41db-89eb-598cb928dce8

📥 Commits

Reviewing files that changed from the base of the PR and between 00d9765 and 92ad5d0.

📒 Files selected for processing (2)
  • src/lib/helpers/apiEndpoint.ts
  • src/lib/stores/sdk.ts

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 29, 2026

Greptile Summary

This PR refactors the regional API endpoint logic out of sdk.ts into a new src/lib/helpers/apiEndpoint.ts helper, and fixes a real bug where switching between regions could produce double-prefixed hostnames (e.g. nyc.fra.cloud.appwrite.io/v1) because the original code only guarded against re-adding the same prefix, not a different one.

Key changes:

  • Extracts getSubdomain (renamed getRegionSubdomain) and the endpoint-building logic into a testable helper module
  • Adds stripLeadingRegionSubdomain which loops to remove any known region prefix before prepending the requested one — correctly handling same-region idempotency and cross-region switching
  • getApiEndpoint in sdk.ts becomes a thin wrapper calling buildRegionalV1Endpoint

Issues found:

  • buildRegionalV1Endpoint strips an existing region prefix even when called with no region (undefined). The original code left the hostname untouched in that case. This is a regression: on self-hosted deployments with PUBLIC_APPWRITE_MULTI_REGION=true where APPWRITE_ENDPOINT contains a region prefix, the no-region call (used for the initial console client setup at sdk.ts:67) will silently drop the region from the base URL.
  • No unit tests accompany the new helper, despite every other helper in src/lib/helpers/ having a co-located *.test.ts file (required by AGENTS.md).

Confidence Score: 4/5

The PR fixes a real cross-region bug but introduces a behavioral regression for the no-region case on multi-region instances — should be addressed before merging.

One P1 finding: when buildRegionalV1Endpoint is called with isMultiRegion=true and no region (which happens for the initial console client setup), it now strips an existing region prefix from the hostname where the original code preserved it. This could silently misconfigure the SDK endpoint in self-hosted multi-region deployments. A P2 finding (missing test file per project convention) also remains open.

src/lib/helpers/apiEndpoint.ts — the no-region guard in buildRegionalV1Endpoint needs attention

Important Files Changed

Filename Overview
src/lib/helpers/apiEndpoint.ts New helper extracting URL-building logic from sdk.ts; introduces stripLeadingRegionSubdomain to fix double-prefix bug, but creates a regression where calling buildRegionalV1Endpoint with no region on a multi-region instance incorrectly strips an existing region prefix from the hostname
src/lib/stores/sdk.ts Cleans up getApiEndpoint by delegating to the new helper; the inline getSubdomain switch is removed and constants imports are dropped — straightforward refactor with no logic issues introduced in this file

Reviews (1): Last reviewed commit: "fix: normalize regional API base URL and..." | Re-trigger Greptile

Comment on lines +77 to +84
if (!isMultiRegion) {
return `${protocol}//${hostname}/v1`;
}

const hostWithoutRegion = stripLeadingRegionSubdomain(hostname);
const subdomain = getRegionSubdomain(region);

return `${protocol}//${subdomain}${hostWithoutRegion}/v1`;
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.

P1 Region stripped when called without a region on multi-region deployments

When buildRegionalV1Endpoint is called with isMultiRegion = true but region = undefined (which happens for the initial const endpoint = getApiEndpoint() at sdk.ts:67), the function will:

  1. Strip any existing region prefix from hostname via stripLeadingRegionSubdomain
  2. Prepend getRegionSubdomain(undefined)''

This produces a hostname without any region prefix.

The original code avoided this problem because it only mutated the hostname when subdomain was non-empty (if (subdomain && hostname.startsWith(subdomain)) ...). With getSubdomain(undefined) returning '', the original hostname passed through unchanged.

Affected scenario: Any self-hosted instance with PUBLIC_APPWRITE_MULTI_REGION=true where APPWRITE_ENDPOINT (or the browser URL) contains a region prefix (e.g. fra.example.com). Calling getApiEndpoint() with the new code would strip fra. and route the console clients to example.com/v1 instead of the intended fra.example.com/v1.

A minimal guard fixes this:

if (!isMultiRegion) {
    return `${protocol}//${hostname}/v1`;
}

const subdomain = getRegionSubdomain(region);
if (!subdomain) {
    // No specific region requested — return the hostname as-is without stripping.
    return `${protocol}//${hostname}/v1`;
}

const hostWithoutRegion = stripLeadingRegionSubdomain(hostname);
return `${protocol}//${subdomain}${hostWithoutRegion}/v1`;

Comment on lines +1 to +85
import {
REGION_FRA,
REGION_NYC,
REGION_SFO,
REGION_SGP,
REGION_SYD,
REGION_TOR,
SUBDOMAIN_FRA,
SUBDOMAIN_NYC,
SUBDOMAIN_SFO,
SUBDOMAIN_SGP,
SUBDOMAIN_SYD,
SUBDOMAIN_TOR
} from '$lib/constants';

/** Ordered list of region DNS prefixes (e.g. `fra.`) for stripping from API hostnames. */
const REGION_SUBDOMAIN_PREFIXES: readonly string[] = [
SUBDOMAIN_FRA,
SUBDOMAIN_NYC,
SUBDOMAIN_SYD,
SUBDOMAIN_SFO,
SUBDOMAIN_SGP,
SUBDOMAIN_TOR
];

/**
* Removes leading Appwrite Cloud region label(s) from `hostname` (e.g. `fra.`).
* Strips repeatedly so a doubled prefix (`fra.fra.cloud...`) does not become
* `nyc.fra.cloud...` after prepending another region.
*/
export function stripLeadingRegionSubdomain(hostname: string): string {
let host = hostname;
let changed = true;
while (changed) {
changed = false;
for (const prefix of REGION_SUBDOMAIN_PREFIXES) {
if (host.startsWith(prefix)) {
host = host.slice(prefix.length);
changed = true;
break;
}
}
}
return host;
}

/** Region prefix (e.g. `fra.`) used before the API hostname when multi-region is enabled. */
export function getRegionSubdomain(region?: string): string {
switch (region) {
case REGION_FRA:
return SUBDOMAIN_FRA;
case REGION_SYD:
return SUBDOMAIN_SYD;
case REGION_NYC:
return SUBDOMAIN_NYC;
case REGION_SFO:
return SUBDOMAIN_SFO;
case REGION_SGP:
return SUBDOMAIN_SGP;
case REGION_TOR:
return SUBDOMAIN_TOR;
default:
return '';
}
}

/**
* Builds the `/v1` API base URL (protocol + host + `/v1`).
* When `isMultiRegion` is true, strips any known region prefix from the host, then prepends the requested region.
*/
export function buildRegionalV1Endpoint(
protocol: string,
hostname: string,
region: string | undefined,
isMultiRegion: boolean
): string {
if (!isMultiRegion) {
return `${protocol}//${hostname}/v1`;
}

const hostWithoutRegion = stripLeadingRegionSubdomain(hostname);
const subdomain = getRegionSubdomain(region);

return `${protocol}//${subdomain}${hostWithoutRegion}/v1`;
}
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.

P2 No unit tests for the new helper

The project convention (documented in AGENTS.md and reflected by every other helper) is to co-locate a *.test.ts file alongside each helper module. For example: array.test.ts, date.test.ts, string.test.ts, etc. live right next to their corresponding helpers.

apiEndpoint.ts introduces meaningful URL-manipulation logic (stripLeadingRegionSubdomain, getRegionSubdomain, buildRegionalV1Endpoint) but ships with no test file, making it harder to catch regressions.

Consider adding src/lib/helpers/apiEndpoint.test.ts with cases such as:

  • Multi-region disabled → hostname unchanged
  • Switching region when hostname already has a different prefix (fra.cloud.appwrite.io → region nycnyc.cloud.appwrite.io/v1)
  • Doubling the same prefix (fra.fra.cloud.appwrite.iofra.cloud.appwrite.io/v1)
  • No region requested (undefined) on a multi-region URL → hostname unchanged

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

1 participant