Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(api): add `clerk api --fapi` for the public Frontend API by rafa-thayto · Pull Request #345 · clerk/cli · GitHub
Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(api): add `clerk api --fapi` for the public Frontend API by rafa-thayto · Pull Request #345 · clerk/cli · GitHub
Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(api): add `clerk api --fapi` for the public Frontend API by rafa-thayto · Pull Request #345 · clerk/cli · GitHub
Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(api): add `clerk api --fapi` for the public Frontend API by rafa-thayto · Pull Request #345 · clerk/cli · GitHub
Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(api): add `clerk api --fapi` for the public Frontend API by rafa-thayto · Pull Request #345 · clerk/cli · GitHub
Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(api): add `clerk api --fapi` for the public Frontend API by rafa-thayto · Pull Request #345 · clerk/cli · GitHub
Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(api): add `clerk api --fapi` for the public Frontend API by rafa-thayto · Pull Request #345 · clerk/cli · GitHub
Skip to content

feat(api): add clerk api --fapi for the public Frontend API - #345

Merged
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough
Jun 22, 2026
Merged

feat(api): add clerk api --fapi for the public Frontend API#345
rafa-thayto merged 4 commits into
mainfrom
feat/api-fapi-passthrough

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Summary

clerk api spoke only BAPI (default) and PLAPI (--platform). After a config change, the natural way to verify it took effect is the instance's public FAPI /v1/environment payload (what clerk-js actually consumes) — but that meant dropping to curl plus decoding the FAPI domain out of the publishable key by hand.

This adds --fapi:

clerk api --fapi /environment --app <id> --instance dev
  • Resolves the FAPI host from the instance's publishable key (looked up via --app/--instance or the linked project through the Platform API), reusing the existing lib/fapi.ts client and decodePublishableKey.
  • The request itself is unauthenticated — these endpoints are public.
  • Paths are /v1-normalized like the other modes, so both /environment and /v1/environment work.
  • --fapi and --platform are mutually exclusive.

Implementation reuses the request-target resolution: api() now builds a { baseUrl, runRequest } pair, which also de-duplicates the BAPI/PLAPI branches. The local error handler was broadened from BapiError to ApiError so FAPI error bodies still print to stdout for piping.

Test plan

  • New tests in src/commands/api/index.test.ts: host resolution + no auth header; --fapi/--platform conflict; FAPI error body printed to stdout with exit 1
  • Full api + completion + api-queries integration suites pass

Closes#332

@changeset-bot

changeset-botBot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efa6d41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new --fapi flag is added to the clerk api command, routing requests to the instance's public Frontend API. A shared ApiResponse interface is introduced in lib/fetch.ts and adopted by both bapiRequest and a new fapiRequest function in lib/fapi.ts. A new commands/api/fapi.ts module resolves the FAPI host from the instance's publishable key via Platform API. In commands/api/index.ts, validateFapiOptions() enforces mutual exclusion with --platform and warns on ignored --secret-key; resolveApiTarget() dispatches to the appropriate request executor. CLERK_JS_API_VERSION is bumped from "5" to "6". Tests and README documentation are updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'feat(api): add clerk api --fapi for the public Frontend API' clearly and specifically summarizes the main change: adding a --fapi flag to the clerk api command for Frontend API support.
Description check✅ PassedThe description comprehensively explains the feature being added, its purpose, usage, implementation details, and testing approach, all directly related to the changeset.
Linked Issues check✅ PassedThe PR fully implements the requirements from issue #332: adds 'clerk api --fapi' to resolve FAPI host from instance publishable key, supports --app/--instance parameters, makes unauthenticated requests to public endpoints, and enables config verification without external tools.
Out of Scope Changes check✅ PassedAll changes are scoped to implementing the --fapi feature: new FAPI request functionality, instance resolution logic, comprehensive tests, documentation updates, and refactoring to support multiple API targets without out-of-scope modifications.

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


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.

@wyattjohwyattjoh left a comment

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.

Clean, well-scoped addition that mirrors the existing BAPI/PLAPI passthrough patterns. A few follow-ups around dry-run semantics, a duplicated version constant, and silently-ignored flags.

Comment threadpackages/cli-core/src/commands/api/index.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/fapi.ts
Comment threadpackages/cli-core/src/commands/api/index.ts
Comment threadpackages/cli-core/src/commands/api/index.test.ts
clerk api spoke only BAPI and PLAPI, so verifying a config change against the
instance's public FAPI /v1/environment (what clerk-js consumes) meant dropping
to curl and decoding the FAPI domain out of the publishable key by hand.
Add --fapi: resolve the FAPI host from the instance's publishable key (via
--app/--instance or the linked project) and do an unauthenticated passthrough,
reusing the existing lib/fapi.ts client. --fapi and --platform are mutually
exclusive.
Closes#332
- Move dry-run check before resolveFapiHost so --fapi --dry-run avoids
the Platform API round-trip; shows <fapi-host> placeholder instead
- Import CLERK_JS_API_VERSION from lib/fapi.ts instead of redeclaring it
- Warn when --secret-key is provided with --fapi (key is ignored)
- Add tests: --fapi no-app NOT_LINKED error, /environment and
/v1/environment path normalization, --secret-key warning, --dry-run
no network call
@rafa-thayto
rafa-thaytoforce-pushed the feat/api-fapi-passthrough branch from 8f9d521 to 64f6f75CompareJune 18, 2026 12:19
Comment threadpackages/cli-core/src/lib/fapi.ts Outdated
Comment threadpackages/cli-core/src/commands/api/index.ts Outdated

@dmoernerdmoerner left a comment

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.

I think we should just be clearer that this only hits public endpoints.

Because it is possible to use the command line to hit FAPI with curl, and use auth, you just need to use a cookie jar. This is how I do it in my local skill for testing:

FAPI=""
jar=$(mktemp)# 1. Create a dev browser; capture its token.
tok=$(curl -s -c "$jar" -X POST "$FAPI/v1/dev_browser" -H "Origin: $FAPI"| jq -r '.token')# 2. Inspect what the instance has enabled (strategies, factors).
curl -s -b "$jar" -c "$jar""$FAPI/v1/environment?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.auth_config | {first_factors, identification_strategies, password}'# 3. The client state (sessions, sign_in, sign_up).
curl -s -b "$jar" -c "$jar""$FAPI/v1/client?__clerk_db_jwt=$tok" -H "Origin: $FAPI" \
| jq '.response | {sessions: (.sessions|length)}'

- Refactor fallback branch of resolveInstance to use
resolveFetchedApplicationInstance instead of hand-rolling
app.instances.find (addresses wyattjoh comment 3)
- Bump CLERK_JS_API_VERSION from "5" to "6" to match current clerk-js
major (addresses dmoerner comment 6)
- Clarify --fapi help text: "unauthenticated endpoints only" instead of
"no auth" to avoid implying it skips auth on authenticated endpoints
(addresses dmoerner comment 7)
Comment threadpackages/cli-core/src/commands/api/fapi.ts Outdated
Co-locate the FAPI passthrough request with the other FAPI helpers
(bootstrapDevBrowser, fetchUserSettings) in lib/fapi.ts, where it
already reached for decodePublishableKey and CLERK_JS_API_VERSION.
Promote the passthrough response shape to a shared `ApiResponse` type
in lib/fetch.ts so the moved function does not have to import upward
from commands/. commands/api/fapi.ts keeps the command-layer instance
and host resolution.
Addresses review feedback on #345.
Claude-Session: https://claude.ai/code/session_01QnfBw9qY7u19BvUWyfQGC6

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/cli-core/src/lib/fapi.ts`:
- Around line 137-140: The normalizeBapiPath function in the URL construction at
line 137 doesn't correctly handle paths that already contain query strings. When
options.path contains a query string like /v1?foo=bar, normalizeBapiPath can
double-normalize it incorrectly. Separate the path and query string components
of options.path before normalizing, apply normalizeBapiPath only to the path
portion, and then properly reconstruct the URL using the URL constructor so that
both the normalized path and query parameters are handled correctly.
🪄 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: CHILL

Plan: Pro

Run ID: 1a82c2ba-4088-4f74-b89d-9039b59ff605

📥 Commits

Reviewing files that changed from the base of the PR and between e1b5db3 and efa6d41.

📒 Files selected for processing (5)
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/fapi.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/lib/fapi.ts
  • packages/cli-core/src/lib/fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/api/index.ts

Comment on lines +137 to +140
const url = new URL(`https://${options.fapiHost}${normalizeBapiPath(options.path)}`);
if (!url.searchParams.has("_clerk_js_version")) {
url.searchParams.set("_clerk_js_version", CLERK_JS_API_VERSION);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle /v1 paths with query strings correctly.

At Line 137, using normalizeBapiPath() can mis-normalize /v1?foo=bar into /v1/v1?foo=bar because the helper only treats / or end-of-string as valid after v1.

Suggested fix (in packages/cli-core/src/lib/bapi-command.ts)
- if (!/^\/v1(?:\/|$)/.test(normalized)) normalized = `/v1${normalized}`;+ if (!/^\/v1(?:\/|$|\?)/.test(normalized)) normalized = `/v1${normalized}`;
🤖 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 `@packages/cli-core/src/lib/fapi.ts` around lines 137 - 140, The
normalizeBapiPath function in the URL construction at line 137 doesn't correctly
handle paths that already contain query strings. When options.path contains a
query string like /v1?foo=bar, normalizeBapiPath can double-normalize it
incorrectly. Separate the path and query string components of options.path
before normalizing, apply normalizeBapiPath only to the path portion, and then
properly reconstruct the URL using the URL constructor so that both the
normalized path and query parameters are handled correctly.

@rafa-thayto
rafa-thayto merged commit 694188c into mainJun 22, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the feat/api-fapi-passthrough branch June 22, 2026 19:31
@github-actionsgithub-actionsBot mentioned this pull request Jun 22, 2026
Sign up for freeto 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.

Feature: fetch an instance's public FAPI /v1/environment for config verification

3 participants

@rafa-thayto@wyattjoh@dmoerner