W
Wasp-lang•5mo ago
JLegendz

useQuery server side?

I am trying to use one of my queries inside of my websocket server but when I do, the context.user doesn't exist. Even if I make sure that I'm calling this query after the user is authenticated. I'm not sure why context.user isn't populated, but it got me to thinking if maybe I should only be using the queries from the client side? Surely there's a way to do it server side though? I have this query: app/src/server/queries.ts
export const getSubscriptionsByUser: GetSubscriptionsByUser<void, Subscription[]> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

return context.entities.Subscription.findMany({
where: {
userId: context.user.id,
},
include: {
listing: true,
},
});
};
export const getSubscriptionsByUser: GetSubscriptionsByUser<void, Subscription[]> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

return context.entities.Subscription.findMany({
where: {
userId: context.user.id,
},
include: {
listing: true,
},
});
};
and in my ws-server.ts I'm attempting to use it like this.
import { getListingSubscribers, getSubscriptionsByUser } from './queries'; // Import the getListingSubscribers function

//Skipping a bunch of unrelated code...

socket.on('userAuthenticated', async () => {
console.log('User authenticated:', username);

// Check if the user is authenticated before trying to rejoin rooms
if (socket.data.user) {
try {
//TODO There is an error here. context.user doesn't exist and so we can't fetch subscriptions here.
// It's all happening before the user is fully authenticated apparently.
const subscriptions = await getSubscriptionsByUser(undefined, context);
subscriptions.forEach(sub => {
const room = `listing_${sub.listingId}`;
socket.join(room);
console.log(`${username} rejoined room ${room}`);
});
} catch (error) {
console.error('Error rejoining rooms:', error);
}
} else {
console.warn('User not authenticated, skipping room rejoining.');
}
});
import { getListingSubscribers, getSubscriptionsByUser } from './queries'; // Import the getListingSubscribers function

//Skipping a bunch of unrelated code...

socket.on('userAuthenticated', async () => {
console.log('User authenticated:', username);

// Check if the user is authenticated before trying to rejoin rooms
if (socket.data.user) {
try {
//TODO There is an error here. context.user doesn't exist and so we can't fetch subscriptions here.
// It's all happening before the user is fully authenticated apparently.
const subscriptions = await getSubscriptionsByUser(undefined, context);
subscriptions.forEach(sub => {
const room = `listing_${sub.listingId}`;
socket.join(room);
console.log(`${username} rejoined room ${room}`);
});
} catch (error) {
console.error('Error rejoining rooms:', error);
}
} else {
console.warn('User not authenticated, skipping room rejoining.');
}
});
The end goal of all this is to emit a signal when the user is authenticated, so that I can make sure that context.user exists, and then I can add the user back into any websocket rooms they belong to as a way to persist that connection between server restarts during development. I always get a 401 no matter what though, and context.user just doesn't exist. Any suggestions here? I'm not sure what I'm doing wrong.
4 Replies
kapa.ai
kapa.ai•5mo ago
Hi @JLegendz. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information provided, it seems like you're trying to use a query on the server-side, specifically within a WebSocket server. However, the context.user is not being populated as expected. From the knowledge sources, it's clear that there's a difference in how you should import and use queries and actions on the server-side compared to the client-side. According to this GitHub issue (https://github.com/wasp-lang/wasp/issues/1909), you should import the actions and queries on the server from wasp/server/operations rather than wasp/client/operations. Also, when calling these operations on the server-side, you need to provide the context as the first argument and then the arguments as the second argument. This might be why your context.user is not being populated as expected. However, it's important to note that the usage of queries and actions within a WebSocket server is not explicitly covered in the provided knowledge sources. There's a mention in this GitHub issue (https://github.com/wasp-lang/wasp/issues/1133) about considering how the auth middleware should behave with WebSockets, but it doesn't provide a concrete solution. Given this, it might be beneficial to revisit the way you're handling authentication within your WebSocket server and ensure that the context is being correctly populated. If the issue persists, I would recommend reaching out to the Wasp community or maintainers for more specific guidance. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @JLegendz, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
martinsos
martinsos•5mo ago
@JLegendz your thinking is going in the right direction here, you should be able to call queries from the server. Let me ping @sodic , he was just last week working on this quite a bit (fixing some stuff), he will probably be able to provide a well rounded answer.
JLegendz
JLegendz•5mo ago
Sounds good. Thank you. I just placed a TODO in my code to come back to this when I could get a little help.
Filip
Filip•5mo ago
Hey @JLegendz, as mentioned in the issue Kappa linked, the first class support for calling operations on the backend is unfortunately currently broken. Sorry 😅 I've recently fixed it but we haven't released it yet: https://github.com/wasp-lang/wasp/pull/2044 Until we release it, you'll have to import your query directly from ./queries and build the context yourself. This should be possible by combining socket.data.user with the appropriate entities to build the context for the query. For example:
import { getSubscriptionsByUser } from './queries';

// inside your socket callback
getSubscriptionsByUser(null, {
user: context.data.user,
entities: {
// all entities your getSubscriptionByUser needs.
// You can find them in the context object inside the socket callback.
},
}
import { getSubscriptionsByUser } from './queries';

// inside your socket callback
getSubscriptionsByUser(null, {
user: context.data.user,
entities: {
// all entities your getSubscriptionByUser needs.
// You can find them in the context object inside the socket callback.
},
}
If you're using TypeScript, the type checker will tell you the exact type you need to send. As for this:
TODO There is an error here. context.user doesn't exist and so we can't fetch subscriptions here. It's all happening before the user is fully authenticated apparently.
That's not quire right. context.user isn't supposed to exist here. In websockets, the context only contains your app's entities. The user is under context.data.user. Check the docs for reference: https://wasp-lang.dev/docs/advanced/web-sockets#websocketfn-function I admit it's a little inconsistent with the rest of the framework, and we'll probably be changing that as well. One question, TypeScript should also tell you about what's in the context object. Is our IDE support broken or are you not using typescript? Hope this helps and sorry for all the weirdness 🙂
Want results from more Discord servers?
Add your server