Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(clerk-js): Track fapi requests triggered by UI components #5299

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

panteliselef
Copy link
Member

@panteliselef panteliselef commented Mar 7, 2025

Description

This PR decorates all requests towards the Frontend API with a new query param _clerk_ui_triggered=true which indicates that the request originated from our UI components.

The solution proposed by this PR, uses a proxy to wrap all resources (e.g. user and client) before passing them to their respective react context. The proxy will detect any function usage and will flag it as "used by UI", so that fapiClient can use this to attach the new query param. This solution allows to not make any changes to public API or affect the implementation of our resources.

Attention: This solution does not guarantee that the detection will work every time since it uses a stack to determine if the request should be flagged as "used by UI". If a request from a custom flow fires while there is at least one pending request triggered by a UI component, the former will be wrongly flagged as "used by UI". This is acceptable because we are using the query param for analytics, and such errors will not affect the performance or the behaviour of the application.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Copy link

changeset-bot bot commented Mar 7, 2025

🦋 Changeset detected

Latest commit: 777380f

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

This PR includes changesets to release 3 packages
Name Type
@clerk/clerk-js Minor
@clerk/chrome-extension Patch
@clerk/clerk-expo Patch

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

Copy link

vercel bot commented Mar 7, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
clerk-js-sandbox ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 7, 2025 5:45pm


export const usageByUIComponents = createStore(0);

const isThenable = (value: unknown): value is Promise<unknown> => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const isThenable = (value: unknown): value is Promise<unknown> => {
const isPromise = (value: unknown): value is Promise<unknown> => {

🙃 I'd suggest updating the name to match the type predicate as well, thenable was a bit hard to understand at a first glance

return !!value && typeof (value as any).then === 'function';
};

export const makeUICaller = <T>(resource: T): T => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
export const makeUICaller = <T>(resource: T): T => {
/** @internal **/
export const makeUICaller = <T>(resource: T): T => {

🙃 We could add JSDocs to indicate for external contributors that this is a tooling for internal usage

const clientCtx = React.useMemo(() => ({ value: client }), [client]);
const sessionCtx = React.useMemo(() => ({ value: session }), [session]);
const userCtx = React.useMemo(() => ({ value: user }), [user]);
const clerkCtx = React.useMemo(() => ({ value: makeUICaller(clerk) }), []);
Copy link
Member

@LauraBeatris LauraBeatris Mar 10, 2025

Choose a reason for hiding this comment

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

💡Not against this approach, mostly curious regarding other options - have you thought of forwarding a property to Clerk if it loads on a React context, and then accessing this on the FAPI client?

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you share more about this. Clerk is already loaded when we mount the providers and the UI components.

Copy link
Member

Choose a reason for hiding this comment

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

Once the CoreClerkContextWrapper loads, it sets a property in Clerk that can be accessed on the fapiClient

if (clerk.hasUIListeners) {
   searchParams.append('_clerk_ui_triggered', 'true');
 }

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this increases the margin of faulty detections by a lot, since all requests triggered outside of a UI component will be flagged incorrectly while a UI component is mounted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants