@@ -2,9 +2,10 @@ import type { Program } from "../../cli-program.ts";
22import { getAuthToken } from "../../lib/plapi.ts" ;
33import { getBapiBaseUrl , getPlapiBaseUrl } from "../../lib/environment.ts" ;
44import { normalizeBapiPath , resolveBapiSecretKey } from "../../lib/bapi-command.ts" ;
5- import { bapiRequest } from "./bapi.ts" ;
5+ import { bapiRequest , type BapiResponse } from "./bapi.ts" ;
6+ import { fapiRequest , resolveFapiHost } from "./fapi.ts" ;
67import {
7- BapiError ,
8+ ApiError ,
89 ERROR_CODE ,
910 UserAbortError ,
1011 isPromptExitError ,
@@ -25,12 +26,43 @@ export interface ApiOptions {
2526 secretKey ?: string ;
2627 instance ?: string ;
2728 platform ?: boolean ;
29+ fapi ?: boolean ;
2830 dryRun ?: boolean ;
2931 yes ?: boolean ;
3032}
3133
3234const MUTATING_METHODS = new Set ( [ "POST" , "PUT" , "PATCH" , "DELETE" ] ) ;
3335
36+ type RunRequest = ( req : { method : string ; path : string ; body ?: string } ) => Promise < BapiResponse > ;
37+
38+ /** Resolve the API surface (base URL + request executor) from the flags. */
39+ async function resolveApiTarget (
40+ options : ApiOptions ,
41+ ) : Promise < { baseUrl : string ; runRequest : RunRequest } > {
42+ if ( options . fapi ) {
43+ if ( options . platform ) {
44+ throwUsageError (
45+ "--fapi and --platform cannot be combined." ,
46+ undefined ,
47+ ERROR_CODE . USAGE_ERROR ,
48+ ) ;
49+ }
50+ const fapiHost = await resolveFapiHost ( options ) ;
51+ const baseUrl = `https://${ fapiHost } ` ;
52+ return { baseUrl, runRequest : ( req ) => fapiRequest ( { ...req , fapiHost } ) } ;
53+ }
54+
55+ if ( options . platform ) {
56+ const secretKey = await getAuthToken ( ) ;
57+ const baseUrl = getPlapiBaseUrl ( ) ;
58+ return { baseUrl, runRequest : ( req ) => bapiRequest ( { ...req , secretKey, baseUrl } ) } ;
59+ }
60+
61+ const secretKey = await resolveBapiSecretKey ( options ) ;
62+ const baseUrl = getBapiBaseUrl ( ) ;
63+ return { baseUrl, runRequest : ( req ) => bapiRequest ( { ...req , secretKey, baseUrl } ) } ;
64+ }
65+
3466export async function api (
3567 endpoint : string | undefined ,
3668 filter : string | undefined ,
@@ -61,17 +93,8 @@ export async function api(
6193 // 2. Determine HTTP method
6294 const method = ( options . method ?? ( body ? "POST" : "GET" ) ) . toUpperCase ( ) ;
6395
64- // 3. Resolve authentication
65- let secretKey : string ;
66- let baseUrl : string ;
67-
68- if ( options . platform ) {
69- secretKey = await getAuthToken ( ) ;
70- baseUrl = getPlapiBaseUrl ( ) ;
71- } else {
72- secretKey = await resolveBapiSecretKey ( options ) ;
73- baseUrl = getBapiBaseUrl ( ) ;
74- }
96+ // 3. Resolve the request target (base URL + executor)
97+ const { baseUrl, runRequest } = await resolveApiTarget ( options ) ;
7598
7699 // 4. Dry run
77100 if ( options . dryRun ) {
@@ -97,13 +120,7 @@ export async function api(
97120 // 6. Execute request
98121 try {
99122 const response = await withSpinner ( "Executing request..." , ( ) =>
100- bapiRequest ( {
101- method,
102- path : endpoint ,
103- secretKey,
104- body : body ?? undefined ,
105- baseUrl,
106- } ) ,
123+ runRequest ( { method, path : endpoint , body : body ?? undefined } ) ,
107124 ) ;
108125
109126 if ( options . include ) {
@@ -112,10 +129,10 @@ export async function api(
112129 printBody ( response . body ) ;
113130 closeStatus = "success" ;
114131 } catch ( error ) {
115- // Handle BapiError locally to print the raw API response body to stdout
132+ // Handle API errors locally to print the raw response body to stdout
116133 // (for piping), rather than propagating to the global error handler.
117- if ( error instanceof BapiError ) {
118- if ( options . include ) {
134+ if ( error instanceof ApiError ) {
135+ if ( options . include && error . headers ) {
119136 printHeaders ( error . status , error . headers ) ;
120137 }
121138 prettyPrint ( error . body ) ;
@@ -216,6 +233,10 @@ export function registerApi(program: Program): void {
216233 . option ( "--secret-key <key>" , "Override the secret key" )
217234 . option ( "--instance <id>" , "Instance to target (dev, prod, or instance ID)" )
218235 . option ( "--platform" , "Use Platform API instead of Backend API" )
236+ . option (
237+ "--fapi" ,
238+ "Use the instance's public Frontend API (no auth; host resolved from the publishable key)" ,
239+ )
219240 . option ( "--dry-run" , "Show the request without executing it" )
220241 . option ( "--yes" , "Skip confirmation for mutating requests" )
221242 . setExamples ( [
@@ -226,6 +247,10 @@ export function registerApi(program: Program): void {
226247 command : 'clerk api /users -d \'{"first_name":"Alice"}\'' ,
227248 description : "POST with a JSON body" ,
228249 } ,
250+ {
251+ command : "clerk api --fapi /environment --app <id> --instance dev" ,
252+ description : "GET the public FAPI environment payload" ,
253+ } ,
229254 ] )
230255 . action ( api ) ;
231256}
0 commit comments