Asynchronous HTTP requests from within match handler

Hello, i have a question regarding firing asynchronous HTTP requests from within a match handler.

Say i want my match handler to confirm a player fulfills certain criteria that allow him to perform an action.
That action could for example be the player completing a quest. The player sends a message to the match handler requesting completion of the quest after having talked to an NPC, and Nakama makes sure the player is in the correct location to talk to the relevant NPC, has appropriate items in their inventory, has accepted the quest previously etc. Authoritatively confirming these things server-side is critical due to rewards being given to the player upon completion of the quest that may encourage cheating in order to obtain them.

Finishing the quest however also requires us to update data on a server separate from Nakama, which is accessed by multiple apps (social, web, etc.), not just the game-client, hence this data not being stored in Nakama. In this case, finishing a quest may update a users profile, or unlock a reward that can be redeemed for other valuables in the web-app.

So we send a request from Nakama to that server telling it to update the appropriate data. That server will confirm the request is valid and return a 200 response, which Nakama should receive and only then inform the player that the action was successful.

This means calling an HTTP route from within the Nakama match handlers matchLoop, which however can only happen synchronously using the httpRequest method. The alternative i have been using for this is to fire an event instead, which is then handled in Go (my match handler is written in TypeScript). This works fine for fire-and-forget requests, but if we want to handle the response, we need a way to pass that back to the match handler. I found that this is theoretically possible by using a function closure as described here and writing the response to a storage object, then checking that storage object in the match handler every tick (or every x ticks) until it has been created or changed (perhaps via bool flag added to the storage object), then reading it once it has.

This does seem like a pretty mad workaround though, and i expect that all those storage reads would not be the most performant thing to do either.
So i’m wondering, is there another way to handle HTTP requests asynchronously from within a match handler?

I do realize that the root of the problem lies with Nakama being dependent on a response from the second server to confirm an in-game action in the first place, and this could be resolved by instead storing the relevant data on the Nakama-server instead. That is an option i’m keeping open, however there is a sizeable existing architecture in place already, and not all relevant data stored on the second server can realistically be moved to Nakama. Having asynchronous communication between the two from within a match handler would make things a lot more flexible.

I’d appreciate any advice on this. Thank you!

This is not a solution but instead a workaround, instead of calling a HTTP request you can send the action directly to the match handler like you send a multiplayer message, and the authoritative match can do the necessary actions to confirm that the action the player is doing is good and save the necessary items in storage.

Hey @LucasGaspar , thanks for the reply.
The client is already only communicating with the match handler via multiplayer messages. HTTP requests in this scenario are only sent from Nakama to the external server to trigger database changes on that additional, Nest-based server.
I agree that Nakama saving the necessary data in storage directly would solve the problem as described in my last paragraph, but this would potentially require migrating a lot of data and systems currently handled by that Nest-server to Nakama.

1 Like

Sorry I misunderstood, I thought the information was saved in Nakama

No worries!

The conclusion i have come to for now is to mirror the relevant data stored on the external server in Nakama, update those storage objects immediately in the match handler and send them to the client, then fire an HTTP request via event to the external server to update the “real” data object there as well.

The response from that request is then handled in the event and overwrites the storage object modified by the match handler. The match handler itself won’t know about this second storage write, but this approach makes sure there is no de-sync between Nakama and the external server, and the next time a user request an action that relies on this data, i can be sure that Nakama is accessing the “real” state of that data when reading the storage object.

It’s not perfect, but seems like a decent compromise for this specific usecase.

If anyone has input regarding how to best handle asynchronous HTTP requests from a match handler that would still be useful of course and might help other people coming here via Google as well.

Hey @minos, one approach you could use is to spin up a goroutine to make your HTTP requests (or your event handler should also work), and then send the responses back directly on a channel that is listened by the match loop. That way you don’t have to worry about state sync.

Note that for this to work you would have to be using the Go runtime, in order to leverage these Go language constructs.

@ftkg Thank you, i’ll be looking into that suggestion. Rewriting my match handler in Go might be a bit of a task, but could perhaps be worth it in the long run. Good to know that this is another option. I’ll keep it in mind, thanks!