wobsoriano / vue-clerk

Unofficial Vue + Clerk integration.
https://vue-clerk.com
MIT License
108 stars 8 forks source link

Usage outside of components? #19

Closed hloehrmann closed 3 weeks ago

hloehrmann commented 5 months ago

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!

wobsoriano commented 5 months ago

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

fred26s commented 5 months ago

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.

theodem commented 5 months ago

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

wobsoriano commented 5 months ago

@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

hloehrmann commented 5 months ago

@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 commented 5 months ago

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!

wobsoriano commented 5 months ago

@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 :)

hloehrmann commented 4 months ago

@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
wobsoriano commented 4 months ago

@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;
}
hloehrmann commented 4 months ago

@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;
}
wobsoriano commented 3 weeks ago

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 😄