A user represents an identity within the server. Every user is registered and has a profile for other users to find and become friends with or join groups and chat.
A user can own records, share public information with other users, and authenticate via a variety of different social providers.
The system owner identity is represented by a user account with a nil UUID (00000000-0000-0000-0000-000000000000).
When a user has a session you can retrieve their account. The profile contains a variety of information which includes various “linked” social providers.
Client
1
2
curl -X GET "http://127.0.0.1:7350/v2/account"\
-H 'authorization: Bearer <session token>'
Client
1
2
3
4
constaccount=awaitclient.getAccount(session);constuser=account.user;console.info("User id '%o' and username '%o'.",user.id,user.username);console.info("User's wallet:",account.wallet);
Client
1
2
3
4
varaccount=awaitclient.GetAccountAsync(session);varuser=account.User;System.Console.WriteLine("User id '{0}' username '{1}'",user.Id,user.Username);System.Console.WriteLine("User wallet: '{0}'",account.Wallet);
localresult=client.get_account()ifresult.errorthenprint(result.message)returnendlocaluser=result.userprint(("User id '%s' and username '%s'."):format(user.id,user.username))
Some information like wallet, device IDs and custom ID are private but part of the profile is visible to other users.
Public Field
Description
user.id
The unique identifier for the user.
user.username
A unique nickname for the user.
user.display_name
The display name for the user (empty by default).
user.avatar_url
A URL with a profile picture for the user (empty by default).
user.lang
The preferred language settings for the user (default is “en”).
user.location
The location of the user (empty by default).
user.timezone
The timezone of the user (empty by default).
user.metadata
A slot for custom information for the user - Read only from the client.
user.edge_count
Number of friends this user has.
user.facebook_id
Facebook identifier associated with this user.
user.google_id
Google identifier associated with this user.
user.gamecenter_id
GameCenter identifier associated with this user.
user.steam_id
Steam identifier associated with this user.
user.create_time
A timestamp for when the user was created.
user.update_time
A timestamp for when the user was last updated.
user.online
A boolean that indicates whether the user is currently online or not.
Private Field
Description
email
Email address associated with this user.
devices
List of device IDs associated with this user.
custom_id
Custom identifier associated with this user.
wallet
User’s wallet - Read only from the client.
verify_time
A timestamp for when the user was verified (currently only used by Facebook).
You can store additional fields for a user in user.metadata which is useful to share data you want to be public to other users. We recommend using user metadata to store very common fields which other users will need to see. For example, use metadata to enable users to show biographical details if desired, or to display their character name, level, and game statistics.
For all other information you can store records with public read permissions which other users can find.
Metadata is limited to 16KB per user. This can be set only via the script runtime, similar to the wallet.
The following example showcases using user metadata to store a VIP status which is then used as part of a before join tournament hook which only allows VIP members to join.
// Assuming a user metadata structure as follows
constmetadata={vip: true};// Add a before hook to only allow VIP users to join the vip_only tournament
letBeforeJoinTournament: nkruntime.BeforeHookFunction<JoinTournamentRequest>=function(ctx: nkruntime.Context,logger: nkruntime.Logger,nk: nkruntime.Nakama,data: nkruntime.JoinTournamentRequest):nkruntime.JoinTournamentRequest|void{constaccount=nk.accountGetId(ctx.userId)// Only do the following checks if the tournament id is `vip_only`
if(data.tournamentId!="vip_only"){returndata;}// Only continue with the Join Tournament if the actioning user is a vip
if(account.user.metadata["vip"]){returndata;}logger.warn("you must be a vip to join this tournament")returnnull;};// Register inside InitModule
initializer.registerBeforeJoinTournament(BeforeJoinTournament);
// Assuming a user metadata structure as follows
typeUserMetadatastruct{Vipbool`json:"vip"`}metadata:=&UserMetadata{Vip:true,}// Add a before hook to only allow VIP users to join the vip_only tournament
iferr:=initializer.RegisterBeforeJoinTournament(func(ctxcontext.Context,loggerruntime.Logger,db*sql.DB,nkruntime.NakamaModule,in*api.JoinTournamentRequest)(*api.JoinTournamentRequest,error){userId,ok:=ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)if!ok{logger.Error("invalid user")returnnil,runtime.NewError("invalid user",13)}// Get the user's metadata
account,err:=nk.AccountGetId(ctx,userId)iferr!=nil{logger.Error("error getting user account")returnnil,runtime.NewError("error getting user account",13)}// Only do the following checks if the tournament id is `vip_only`
ifin.TournamentId!="vip_only"{returnin,nil}// Only continue with the Join Tournament if the actioning user is a vip
varmetadataUserMetadataiferr:=json.Unmarshal([]byte(account.User.GetMetadata()),&metadata);err!=nil{logger.Error("error deserializing metadata")returnnil,runtime.NewError("error deserializing metadata",13)}ifmetadata.Vip{returnin,nil}returnnil,runtime.NewError("you must be a vip user to join this tournament",7)});err!=nil{logger.Error("unable to register before join tournament hook: %v",err)returnerr}
-- Assuming a user metadata structure as followslocalmetadata={["vip"]=true}-- Add a before hook to only allow VIP users to join the vip_only tournamentlocalfunctionbefore_join_tournament(context,payload)localaccount=nk.account_get_id(context.user_id)-- Only do the following checks if the tournament id is `vip_only`ifpayload.tournament_id~="vip_only"thenreturnpayloadend-- Only continue with the Join Tournament request if the actioning user is a vipifaccount.user.metadata["vip"]thenreturnpayloadendnk.logger_error("you must be a vip user to join this tournament")returnnilendnk.register_req_before(before_join_tournament,"JoinTournament")
Nakama has the concept of a virtual wallet and transaction ledger. Nakama allows developers to create, update and list changes to the user’s wallet. This operation has transactional guarantees and is only achievable with the script runtime.
With server-side code it’s possible to update the user’s wallet.
Server
1
2
3
4
5
6
7
8
9
10
11
12
13
localnk=require("nakama")localuser_id="8f4d52c7-bf28-4fcf-8af2-1d4fcf685592"localchangeset={coins=10,-- Add 10 coins to the user's wallet.gems=-5-- Remove 5 gems from the user's wallet.}localmetadata={game_result="won"}localupdated,previous=nk.wallet_update(user_id,changeset,metadata,true)
Server
1
2
3
4
5
6
7
8
9
10
11
12
userID:="8f4d52c7-bf28-4fcf-8af2-1d4fcf685592"changeset:=map[string]interface{}{"coins":10,// Add 10 coins to the user's wallet.
"gems":-5,// Remove 5 gems from the user's wallet.
}metadata:=map[string]interface{}{"game_result":"won",}updated,previous,err:=nk.WalletUpdate(ctx,userID,changeset,metadata,true)iferr!=nil{logger.WithField("err",err).Error("Wallet update error.")}
Server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
letuser_id='8f4d52c7-bf28-4fcf-8af2-1d4fcf685592';letchangeset={coins: 10,// Add 10 coins to the user's wallet.
gems:-5,// Remove 5 gems from the user's wallet.
};letmetadata={gameResult:'won'};letresult: nkruntime.WalletUpdateResult;try{result=nk.walletUpdate(user_id,changeset,metadata,true);}catch(error){// Handle error
}
The wallet is private to a user and cannot be seen by other users. You can fetch wallet information for a user via Fetch Account operation.
Nakama can report back user online indicators in two ways:
Fetch user information to see if they are online. The user presence will be detected if they have an active socket connection to the server and did not set appearOnline=false.
Publish and subscribe to user status presence updates. This will give you updates when the online status of the user changes (along side a custom message).
You can fetch one or more users by their IDs or handles. This is useful for displaying public profiles with other users.
Client
1
2
curl -X GET "http://127.0.0.1:7350/v2/user?ids=userid1&ids=userid2&usernames=username1&usernames=username2&facebook_ids=facebookid1"\
-H 'authorization: Bearer <session token>'
Client
1
2
3
4
5
constusers=awaitclient.getUsers(session,["user_id1"],["username1"],["facebookid1"]);users.foreach(user=>{console.info("User id '%o' and username '%o'.",user.id,user.username);});
Client
1
2
3
4
5
6
7
8
9
varids=new[]{"userid1","userid2"};varusernames=new[]{"username1","username2"};varfacebookIds=new[]{"facebookid1"};varresult=awaitclient.GetUsersAsync(session,ids,usernames,facebookIds);foreach(varuinresult.Users){System.Console.WriteLine("User id '{0}' username '{1}'",u.Id,u.Username);}
autosuccessCallback=[](constNUsers&users){for(auto&user:users.users){std::cout<<"User id '"<<user.id<<"' username "<<user.username<<std::endl;}};client->getUsers(session,{"user_id1"},{"username1"},{"facebookid1"},successCallback);
Client
1
2
3
4
5
6
7
8
List<String>ids=Arrays.asList("userid1","userid2");List<String>usernames=Arrays.asList("username1","username1");String[]facebookIds=newString[]{"facebookid1"};Usersusers=client.getUsers(session,ids,usernames,facebookIds).get();for(Useruser:users.getUsersList()){System.out.format("User id %s username %s",user.getId(),user.getUsername());}
Client
1
2
3
4
5
6
7
8
9
10
11
varids=["userid1","userid2"]varusernames=["username1","username2"]varfacebook_ids=["facebookid1"]varresult:NakamaAPI.ApiUsers=yield(client.get_users_async(session,ids,usernames,facebook_ids),"completed")ifresult.is_exception():print("An error occurred: %s"%result)returnforuinresult.users:print("User id '%s' username '%s'"%[u.id,u.username])
Client
1
2
3
4
5
6
7
8
9
10
11
varids=["userid1","userid2"]varusernames=["username1","username2"]varfacebook_ids=["facebookid1"]varresult:NakamaAPI.ApiUsers=awaitclient.get_users_async(session,ids,usernames,facebook_ids)ifresult.is_exception():print("An error occurred: %s"%result)returnforuinresult.users:print("User id '%s' username '%s'"%[u.id,u.username])
localids={"userid1","userid2"}localusernames={"username1","username2"}localfacebook_ids={"facebookid1"}localresult=client.get_users(ids,usernames,facebook_ids)ifresult.errorthenprint(result.message)returnendlocalusers=result.usersfor_,userinipairs(users)doprint(("User id '%s' and username '%s'."):format(user.id,user.username))end
You can also fetch one or more users in server-side code.
When a user is registered most of their profile is setup with default values. A user can update their own profile to change fields but cannot change any other user’s profile.
Client
1
2
3
4
5
6
7
curl -X PUT "http://127.0.0.1:7350/v2/account"\
-H 'authorization: Bearer <session token>'\
--data '{
"display_name": "My new name",
"avatar_url": "http://graph.facebook.com/avatar_url",
"location": "San Francisco"
}'
Client
1
2
3
4
5
awaitclient.updateAccount(session,{display_name:"My new name",avatar_url:"http://graph.facebook.com/avatar_url",location:"San Francisco"});
Client
1
2
3
4
conststringdisplayName="My new name";conststringavatarUrl="http://graph.facebook.com/avatar_url";conststringlocation="San Francisco";awaitclient.UpdateAccountAsync(session,null,displayName,avatarUrl,null,location);
Client
1
2
3
4
letdisplayName="My new name"letavatarUrl="http://graph.facebook.com/avatar_url"letlocation="San Francisco"awaitclient.updateAccount(session:session,username:nil,displayName:displayName,avatarUrl:avatarUrl,langTag:nil,location:location)
Client
1
2
3
4
5
6
7
8
9
10
11
finaldisplayName='My new name';finalavatarUrl='http://graph.facebook.com/avatar_url';finallocation='San Francisco';awaitclient.updateAccount(session:session,username:null,displayName:displayName,avatarUrl:avatarUrl,langTag:null,location:location,);
Client
1
client->updateAccount(session,opt::nullopt,"My new name","http://graph.facebook.com/avatar_url",opt::nullopt,"San Francisco");
Client
1
2
3
4
StringdisplayName="My new name";StringavatarUrl="http://graph.facebook.com/avatar_url";Stringlocation="San Francisco";client.updateAccount(session,null,displayName,avatarUrl,null,location);
Client
1
2
3
4
5
6
7
8
9
10
vardisplay_name="My new name";varavatar_url="http://graph.facebook.com/avatar_url";varlocation="San Francisco";varupdate:NakamaAsyncResult=yield(client.update_account_async(session,null,display_name,avatar_url,null,location),"completed")ifupdate.is_exception():print("An error occurred: %s"%update)returnprint("Account updated")
Client
1
2
3
4
5
6
7
8
9
10
vardisplay_name="My new name";varavatar_url="http://graph.facebook.com/avatar_url";varlocation="San Francisco";varupdate:NakamaAsyncResult=awaitclient.update_account_async(session,null,display_name,avatar_url,null,location)ifupdate.is_exception():print("An error occurred: %s"%update)returnprint("Account updated")
Client
1
2
3
4
5
6
7
8
9
10
PUT /v2/account HTTP/1.1
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{"display_name": "My new name",
"avatar_url": "http://graph.facebook.com/avatar_url",
"location": "San Francisco"}
With server-side code it’s possible to update any user’s profile.
Server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
localnk=require("nakama")localuser_id="4ec4f126-3f9d-11e7-84ef-b7c182b36521"-- some user's id.localmetadata={}localusername="my-new-username"localdisplay_name="My new Name"localtimezone=nillocallocation="San Francisco"locallang_tag=nillocalavatar_url="http://graph.facebook.com/avatar_url"localstatus,err=pcall(nk.account_update_id,user_id,metadata,username,display_name,timezone,location,lang_tag,avatar_url)if(notstatus)thennk.logger_info(("Account update error: %q"):format(err))end
Server
1
2
3
4
5
6
7
8
9
10
11
12
13
userID:="4ec4f126-3f9d-11e7-84ef-b7c182b36521"// some user's id.
username:="my-new-username"// must be unique
metadata:=make(map[string]interface{})displayName:="My new name"timezone:=""location:="San Francisco"langTag:=""avatarUrl:="http://graph.facebook.com/avatar_url"iferr:=nk.AccountUpdateId(ctx,userID,username,metadata,displayName,timezone,location,langTag,avatarUrl);err!=nil{// Handle error.
logger.Error("Account update error: %s",err.Error())}