Unlike client calls, server-to-server calls will never have a user ID, so for any functions you do not want accessible from clients just return an error if a user ID is found in the context.
Server
1
2
3
4
5
6
7
8
9
10
11
funcServerRPC(ctxcontext.Context,loggerruntime.Logger,db*sql.DB,nkruntime.NakamaModule,payloadstring)(string,error){userId,ok:=ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)ifok&&userId!=""{logger.Error("rpc was called by a user")return"",runtime.NewError("rpc is only callable via server to server",7)}// Valid server to server RPC call, continue executing the RPC...
return"<JsonResponse>",nil}
Server
1
2
3
4
5
6
7
8
9
constserverRpc : nkruntime.RpcFunction=function(ctx: nkruntime.Context,logger: nkruntime.Logger,nk: nkruntime.Nakama,payload: string):string|void{if(ctx.userId!=""){logger.error("rpc was called by a user");returnnull;}// Valid server to server RPC call, continue executing the RPC...
return"<JsonResponse>";}
Server
1
2
3
4
5
6
7
8
9
localserver_rpc=function(context,payload)ifcontext.user_idandnotcontext.user_id==""thennk.logger_error("rpc was called by a user")returnnilend-- Valid server to server RPC call, continue executing the RPC...return"<JsonResponse>"end
Additionally, it can be useful to create HTTP REST handlers which can be used by web services and ease integration into custom server environments.
This can be achieved by using an RPC hook and the Runtime HTTP Key to authenticate with the server:
funcHttpHandler(ctxcontext.Context,loggerruntime.Logger,db*sql.DB,nkruntime.NakamaModule,payloadstring)(string,error){varmessageinterface{}iferr:=json.Unmarshal([]byte(payload),&message);err!=nil{return"",err}logger.Info("Message: %v",message)response,err:=json.Marshal(map[string]interface{}{"message":message})iferr!=nil{return"",err}returnstring(response),nil}// Register as an RPC function, this call should be in InitModule.
iferr:=initializer.RegisterRpc("http_handler_path",HttpHandler);err!=nil{logger.Error("Unable to register: %v",err)returnerr}
Server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
letcustomFuncRpc: nkruntime.RpcFunction=function(ctx: nkruntime.Context,logger: nkruntime.Logger,nk: nkruntime.Nakama,payload: string){logger.info('payload: %q',payload);if(ctx.userId){// Reject non server-to-server call
throwError('Cannot invoke this function from user session');}letmessage=JSON.parse(payload);logger.info('Message: %q',message);returnJSON.stringify({message: message});}// Register as an after hook for the appropriate feature, this call should be in InitModule.
initializer.registerRpc("custom_rpc_func_id",customFuncRpc);
The registered RPC Functions can be invoked with any HTTP client of your choice. For example, with cURL you could execute the function with the server as follows.
Notice that the JSON payload is escaped and wrapped inside a string. This is by design due to gRPC not having a type that would map between a Protobuf type and a JSON object at the time the RPC API was designed. Support for JSON has since been added to gRPC but we have kept it this way to not break the API contract and ensure compatibility.
An unwrap query parameter is supported which allows you to invoke RPC functions with raw JSON data in the payload:
This is Power User feature, most developers will not need to register their own HTTP handlers. Be careful when registering your endpoints as you could accidentally overwrite existing Nakama endpoints if you use existing paths.
You can attach new HTTP handlers to specified paths on the main client API server endpoint.
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.
Code snippet for this language TypeScript has not been found. Please choose another language to show equivalent examples.
Server
1
2
3
4
5
iferr:=initializer.RegisterHttp("/test",func(whttp.ResponseWriter,r*http.Request){_,_=w.Write([]byte("You hit the new endpoint!"))});err!=nil{returnerr}