vue-clerk icon indicating copy to clipboard operation
vue-clerk copied to clipboard

Usage outside of components?

Open hloehrmann opened this issue 1 year ago • 10 comments

Hi, is it possible to somehow use the composables outside of vue components / setup functions? Like in other composables / guards etc.? I'm getting a Error: Clerk provider not found when for example trying to get a token to use it in my apollo composable as a bearer token.

Seems like in this context the clerk instance isn't provided under the VUE_CLERK key because vue's provide/inject mechanism doesn't work there. Any workarounds on this? Some other libraries facing this issue have a method to provide the client outside of vue components for this usecase.

For example Vue Apollo has a provideApolloClient method: https://v4.apollo.vuejs.org/guide-composable/setup.html#usage-outside-of-setup

Thanks!

hloehrmann avatar Feb 02 '24 10:02 hloehrmann

Thanks for the provideApolloClient reference! I might be able to copy the logic and will get back to you.

wobsoriano avatar Feb 02 '24 18:02 wobsoriano

I think right now useAuth() and useClerk() are relying on computed() to request and update query results from the clerk interface. Maybe need to add a function use Promise to return the clerk status.

fred26s avatar Feb 05 '24 08:02 fred26s

Also interested, would be a great addition as for now, it is not possible to use in router guards.

theodem avatar Feb 05 '24 14:02 theodem

@theodem I believe it's possible to do that starting with Vue 3.3? Check this out https://router.vuejs.org/guide/advanced/navigation-guards.html#Global-injections-within-guards

wobsoriano avatar Feb 05 '24 21:02 wobsoriano

@theodem I believe it's possible to do that starting with Vue 3.3? Check this out https://router.vuejs.org/guide/advanced/navigation-guards.html#Global-injections-within-guards

I can confirm that the vue-clerk composables work in navigation guards as those allow global injections and therefore the useClerkProvide composable (which all others rely on) doesn't throw an error.

hloehrmann avatar Feb 06 '24 07:02 hloehrmann

I might be able to copy the logic and will get back to you.

Hey @wobsoriano, do you have some kind of roadmap when this could be implemented? I know this is a part time project, just need to know how to prioritize the tasks on our end :-)

Thanks for your effort!

hloehrmann avatar Feb 07 '24 07:02 hloehrmann

@hloehrmann pretty busy at work atm so probably this weekend.

It would also help me if you could create a basic stackblitz/codesandbox example of what you're trying to achieve. I understand your point, but it'll help me to visualize! No need to add apollo, just basic composables :)

wobsoriano avatar Feb 07 '24 15:02 wobsoriano

@wobsoriano this weekend sounds awesome!

I made a demo repo for you: https://github.com/hloehrmann/vue-clerk-demo

Just checkout, run npm i and set your VITE_CLERK_PUBLISHABLE_KEY in a .env. There are two views: /home using vue-clerk within the script setup tag, and /product trying to use it within another composable.

To reproduce the error just uncommend line 12 in ProductsView.vue and you should (hopefully) get the following error:

Error: Clerk provider not found
    at useClerkProvide (vue-clerk.js?v=6c72b03c:181:11)
    at useAuth (vue-clerk.js?v=6c72b03c:661:35)
    at useApolloDummy.ts:3:22

hloehrmann avatar Feb 08 '24 08:02 hloehrmann

@hloehrmann I'm looking at the provideApolloClient implementation and it looks like they're setting some global variables which I'm not a fan of 🤔.

Anyway in that specific repo you sent, you could initialize a new Clerk instance and do something like this:

import Clerk from '@clerk/clerk-js/headless'

const clerk = new Clerk(publishableKey)

const getProducts = () => {
	const products = []

	const token = await clerk.session.getToken()
	console.log(token)

	return products;
}

wobsoriano avatar Feb 08 '24 23:02 wobsoriano

@wobsoriano I know I could initialize a new client but thought it would be way more convenient to stick to the provided composables.

FYI: in your example you still would need to wait till clerk/headless is loaded, like this:

import Clerk from '@clerk/clerk-js/headless'

const clerk = new Clerk(publishableKey)

const getProducts = () => {
        await clerk.load();

	const products = []
  
	const token = await clerk.session.getToken()
	console.log(token)

	return products;
}

hloehrmann avatar Feb 14 '24 11:02 hloehrmann

Found this new function available in Vue 3.3+ that'll let you execute a callback with the current app as injection context:

import { useAuth } from 'vue-clerk'
import { app } from '../main'

const getProducts = async () => {
	const { getToken } = app.runWithContext(() => useAuth())
	
	const token = await getToken.value()

	// do something with token
}

export function useApolloDummy() {
	return { getProducts }
}

You need to have access to the main app, though. Not sure if that's doable with Nuxt.

You can also just access Clerk from the window object and do:

const token = await window.Clerk.session.getToken()

Update: Sorry this took so long 😄

wobsoriano avatar Jun 12 '24 19:06 wobsoriano