Note
To answer this question, the vue
/nuxt
parts of the code can be ignored. I am not entirely sure if this would work as it currently is implemented. I am mainly trying to get get some validation, that subscriptions/unsubscriptions I made, make some sense. I am having some issues interpreting the documentations, mostly because of lack of examples for this particular case.
Context
I have recently integrated my Nuxt3 application flows with Appwrite SDKs in order to login
, logout
, verify
and pretty much everything else. Middleware on the browser and on the server work as expected.
Problem
- My code constantly pokes Appwrite APIs to check if the user is authenticated before doing something. Works to some extent, but is probably not very performance friendly
- My code is not reactive, in other words, my interface does not change if a user changes state on the client side until, until I refresh the page.
I figured that to solve both of this problems, I should have a global store to hold account.get
data. I figured the ideal way of doing this without "pushing" and "popping" state everywhere, is to use real time features to listen to state changes from the Appwrite Realtime SDK (inspired on the existing nuxt/supabase
library approach - https://github.com/nuxt-modules/supabase/blob/8c035ac7c358934ef7c72fd4eb5b779da3c2f1ff/src/runtime/plugins/supabase.client.ts#L21).
Code snippet available below, in this thread.
import type { Models, RealtimeResponseEvent } from 'appwrite'
import { useAppwrite } from './useAppwrite'
type User = Models.User<Models.Preferences>
type UseAppwriteAuthStateChangedOptions = {
onAuthenticated: (payload: User) => void
onUnauthenticated: (payload: User) => void
onSubscribed?: () => void
onUnsubscribed?: () => void
}
const ACCOUNT_CHANNEL = 'account'
function isCreateSession(events: string[], _payload: unknown): _payload is User {
return events.includes('users.*.sessions.*.create')
}
function isDeleteSession(events: string[], _payload: unknown): _payload is User {
return events.includes('users.*.sessions.*.delete')
}
export function useAppwriteAuthStateChanged({
onAuthenticated,
onSubscribed,
onUnauthenticated,
onUnsubscribed,
}: UseAppwriteAuthStateChangedOptions) {
const appwrite = useAppwrite()
const unsubscribeCb = ref<() => void>(() => undefined)
function handleUpdate({ events, payload }: RealtimeResponseEvent<unknown>) {
if (isCreateSession(events, payload)) {
onAuthenticated(payload)
resubscribe()
} else if (isDeleteSession(events, payload)) {
onUnauthenticated(payload)
resubscribe()
}
}
function subscribe() {
unsubscribeCb.value = appwrite.client.subscribe(ACCOUNT_CHANNEL, handleUpdate)
onSubscribed?.()
}
function resubscribe() {
unsubscribe()
subscribe()
}
function unsubscribe() {
unsubscribeCb.value()
onUnsubscribed?.()
}
onMounted(() => subscribe())
onUnmounted(() => unsubscribe())
return {
unsubscribe,
}
}
@D5
Okay, so from what I understand, the main "issue" is that if the user changes something on his account, you want it to to be changed in real time too and the UI updated?
Yes, in a way.
That would be the point. For instance, eventually, I want to track verified state, among other possible properties.
For now, I was simply trying to track user
went from anonymous
to authenticated, e.g.: user logged in
, and from authenticated
to anonymous
, e.g.: logged out
If that's not needed for a specific propose, it's better not using realtime feature from an efficiency point. From what I understand, in this case is a must, so it will be somewhat this way:
client.subscribe('account', response => {
// Here you should put the events to change UI related things
console.log(response);
});```
The response will contain account data with new parameters
If you just want to track a specific property, for example, only listen for changes in verified state, unfortunately it's not possible at this moment (you need to listen to all account changes).
Alright, so at least the subscription part looks good. However, the docs says:
Realtime authenticates using an existing user session. If you authenticate after creating a subscription, the subscription will not receive updates for the newly authenticated user. You will need to re-create the subscription to work with the new user.
So I assumed, I also need unsubscribe
and subscribe
again if the user logs in, and logouts, am I right? If so, I will take this code for a spin later tonight 🚀
From what I understand, the connection will throw an error when the user logs out (and the session gets deleted)
I would unsubscribe prior to logout, logout and once logged in, subscribe again
Storing the user in the global state is the right approach. I wouldn't use realtime, though because that seems complicated/overkill.
Recommended threads
- self-hosted auth: /v1/account 404 on saf...
Project created in React/Next.js, Appwrite version 1.6.0. Authentication works in all browsers except Safari (ios), where an attempt to connect to {endpoint}/v1...
- delete document problems
i don't know what's going on but i get an attribute "tournamentid" not found in the collection when i try to delet the document... but this is just the document...
- Update User Error
```ts const { users, databases } = await createAdminClient(); const session = await getLoggedInUser(); const user = await users.get(session.$id); if (!use...