Friends

Friends are a great way to build a social community. Users can add other users to their list of friends, see who is online or when they were last online, chat together in real-time, and interact together in gameplay or collaboration.

Each user builds up a list of friends by who they know already from their social networks, friend requests they send, requests they receive, and who the server recommends they should know. This information is stored in a social graph within the system as a powerful way to interact with other users. Much like how Twitter or Facebook work.

Any social community must be maintained carefully to prevent spam or abuse. To help with this problem it’s also possible for a user to block users they no longer want to communicate with and for the server to ban a user via server-side code to completely disable an account.

Nakama is a common Japanese word that directly translates to friend or comrade. Some believe the word means “people who are considered closer than family”, though that is not a part of the official definition. We feel it expresses the kind of social communities we want developers to build into their games and apps!

Add friends

A user can add one or more friends by that user’s ID or username. The user added will not be marked as a friend in the list until they’ve confirmed the friend request. The user who receives the request can confirm it by adding the user back.

When a friend request is sent or the user is added an in-app notification will be sent. See the in-app notification section for more info.

Client
1
2
3
var ids = ["user-id1", "user-id2"];
var usernames = ["username1"];
await client.addFriends(session, ids, usernames);
Client
1
2
3
var ids = new[] {"user-id1", "user-id2"};
var usernames = new[] {"username1"};
await client.AddFriendsAsync(session, ids, usernames);
Client
1
2
3
vector<string> ids = { "user-id1", "user-id2" };
vector<string> usernames = { "username1" };
client->addFriends(session, ids, usernames);
Client
1
2
3
List<String> ids = Arrays.asList("user-id1", "user-id2");
String[] usernames = new String[] {"username1"};
client.addFriends(session, ids, usernames).get();
Client
1
2
3
4
5
6
7
var ids = ["user-id1", "user-id2"]
var usernames = ["username1"]
var result : NakamaAsyncResult = yield(client.add_friends_async(session, ids, usernames), "completed")

if result.is_exception():
    print("An error occurred: %s" % result)
    return
Client
1
2
3
4
5
6
7
local ids = { "user-id1", "user-id2" }
local usernames = { "username1" }
local result = nakama.add_friends(client, ids, usernames)

if result.error then
  print(result.message)
end
Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/friend?ids=user-id1&ids=user-id2&usernames=username1" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
4
5
POST /v2/friend?ids=user-id1&ids=user-id2&usernames=username1
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>

When both users have added each other as friends, it’s easy to initiate real-time chat in a 1-on-1 channel. See the real-time chat section for more info.

Import friends

A user who registers or links their account with Facebook, Steam, or another social network can select to have friends from that network imported into their friend list.

Alternatively, you can enable users to perform this import at any time via Nakama’s support for on-demand importing of friends.

Facebook

Import a user’s Facebook friends into your app at any time, and optionally reset their current friends list when doing so.

Client
1
2
3
const token = "<facebookAuthToken>";
const reset = true;
await client.importFacebookFriends(token, reset);
Client
1
2
3
var token = "<facebookAuthToken>";
var reset = true;
await client.ImportFacebookFriendsAsync(token, reset);
Client
1
2
3
string token = "<facebookAuthToken>";
bool reset = true;
client->importFacebookFriends(token, reset);
Client
1
2
3
String token = "<facebookAuthToken>";
Bool reset = true;
client.importFacebookFriends(token, reset).get();
Client
1
2
3
4
5
6
7
var token = "<facebookAuthToken>"
var reset = true
var result : NakamaAsyncResult = yield(client.import_facebook_friends(token, reset), "completed")

if result.is_exception():
    print("An error occurred: %s" % result)
    return
Client
1
2
3
4
5
6
7
local token = "<facebookAuthToken>"
local reset = true
local result = nakama.import_facebook_friends(token, reset)

if result.error then
  print(result.message)
end
Client
1
2
3
curl -X POST "http://127.0.0.1:7350/v2/friend/facebook" \
  -H 'Authorization: Bearer <session token>'
  -d '{"token": <facebookAuthToken>, "reset": true}'
Client
1
2
3
4
5
6
7
8
9
POST /v2/friend/facebook
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "token": "<facebookAuthToken>",
  "reset": true
}

Steam

Import a user’s Facebook friends into your app at any time, and optionally reset their current friends list when doing so.

Client
1
2
3
const steamId = "<steamId>";
const reset = true;
await client.importSteamFriends(steamId, reset);
Client
1
2
3
var steamId = "<steamId>";
var reset = true;
await client.ImportSteamFriendsAsync(steamId, reset);
Client
1
2
3
string steamId = "<steamId>";
bool reset = true;
client->importSteamFriends(steamId, reset);
Client
1
2
3
String steamId = "<steamId>";
Bool reset = true;
client.importSteamFriends(steamId, reset).get();
Client
1
2
3
4
5
6
7
var steam_id = "<steam_id>"
var reset = true
var result : NakamaAsyncResult = yield(client.import_steam_friends(steam_id, reset), "completed")

if result.is_exception():
    print("An error occurred: %s" % result)
    return
Client
1
2
3
4
5
6
7
local steam_id = "<steam_id>";
local reset = true
local result = nakama.import_steam_friends(steam_id, reset)

if result.error then
  print(result.message)
end
Client
1
2
3
curl -X POST "http://127.0.0.1:7350/v2/friend/steam" \
  -H 'Authorization: Bearer <session token>'
  -d '{"steamId": <steamId>, "reset": true}'
Client
1
2
3
4
5
6
7
8
9
POST /v2/friend/steam
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "steamId": "<steamId>",
  "reset": true
}

List friends

You can list all of a user’s friends, blocked users, friend requests received (invited), and invites they’ve sent. These statuses are returned together as part of the friend list which makes it easy to display in a UI. A single friends listing will return a page of up to 1000 friends. Pass the returned cursor to subsequent list calls to retrieve more friend pages.

Client
1
2
const friends = await client.listFriends(session);
console.info("Successfully retrieved friend list:", friends);
Client
1
2
3
4
5
6
var result = await client.ListFriendsAsync(session);

foreach (var f in result.Friends)
{
    System.Console.WriteLine("Friend '{0}' state '{1}'", f.User.Username, f.State);
}
Client
1
2
3
4
5
6
auto successCallback = [](NFriendsPtr friends)
{
    std::cout << "Successfully retrieved friend list: " << friends->friends.size() << std::endl;
};

client->listFriends(session, {}, {}, {}, successCallback);
Client
1
2
3
4
5
Friends friends = client.listFriends(session).get();

for (Friend friend : friends.getFriendsList()) {
  System.out.format("Friend %s state %d", friend.getUser().getUsername(), friend.getState());
}
Client
1
2
3
4
5
6
7
8
9
var list : NakamaAPI.ApiFriendList = yield(client.list_friends_async(session), "completed")

if list.is_exception():
    print("An error occurred: %s" % list)
    return

for f in list.friends:
    var friend = f as NakamaAPI.ApiFriend
    print("User %s, status %s" % [friend.user.id, friend.state])
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
local result = nakama.list_friends(client)

if result.error then
  print(result.message)
  return
end

for _,friend in ipairs(result.friends) do
  pprint(friend)
end
Client
1
2
curl -X GET "http://127.0.0.1:7350/v2/friend" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
4
5
GET /v2/friend
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>

Remove friends

A user can remove a friend, reject a received invite, cancel a friend request sent, or unblock a user. If a user is unblocked they are removed from the friend list entirely. To re-add them each user must add the other again.

Similar to how Friend Add works we reuse Friend Remove to cancel or undo whatever friend state is current with another user.

Client
1
2
3
var ids = ["user-id1", "user-id2"];
var usernames = ["username1"];
await client.deleteFriends(session, ids, usernames);
Client
1
2
3
var ids = new[] {"user-id1", "user-id2"};
var usernames = new[] {"username1"};
await client.DeleteFriendsAsync(session, ids, usernames);
Client
1
2
3
vector<string> ids = { "user-id1", "user-id2" };
vector<string> usernames = { "username1" };
client->deleteFriends(session, ids, usernames);
Client
1
2
3
List<String> ids = Arrays.asList("user-id1", "user-id2");
String[] usernames = new String[] {"username1"};
client.deleteFriends(session, ids, usernames).get();
Client
1
2
3
4
5
6
7
8
9
var ids = ["user-id1", "user-id2"]
var usernames = ["username1"]
var remove : NakamaAsyncResult = yield(client.delete_friends_async(session, ids, usernames), "completed")

if remove.is_exception():
    print("An error occurred: %s" % remove)
    return

print("Remove friends: user ids %s, usernames %s" % [ids, usernames])
Client
1
2
3
4
5
6
7
local ids = { "user-id1", "user-id2" }
local usernames = { "username1" }
local result = nakama.delete_friends(client, ids, usernames)

if result.error then
  print(result.message)
end
Client
1
2
curl -X DELETE "http://127.0.0.1:7350/v2/friend?ids=user-id1&ids=user-id2&usernames=username1" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
4
5
DELETE /v2/friend?ids=user-id1&ids=user-id2&usernames=username1
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>

Block a friend

You can stop a user from using 1-on-1 chat or other social features with a user if you block them. The user who wants to block should send the message. They can be unblocked later with a Friend Remove message.

A user who has been blocked will not know which users have blocked them. That user can continue to add friends and interact with other users.

Client
1
2
3
var ids = ["user-id1", "user-id2"];
var usernames = ["username1"];
await client.blockFriends(session, ids, usernames);
Client
1
2
3
var ids = new[] {"user-id1", "user-id2"};
var usernames = new[] {"username1"};
await client.BlockFriendsAsync(session, ids, usernames);
Client
1
2
3
vector<string> ids = { "user-id1", "user-id2" };
vector<string> usernames = { "username1" };
client->blockFriends(session, ids, usernames);
Client
1
2
3
List<String> ids = Arrays.asList("user-id1", "user-id2");
String[] usernames = new String[] {"username1"};
client.blockFriends(session, ids, usernames).get();
Client
1
2
3
4
5
6
7
8
9
var ids = ["user-id1", "user-id2"]
var usernames = ["username1"]
var block : NakamaAsyncResult = yield(client.block_friends_async(session, ids, usernames), "completed")

if block.is_exception():
    print("An error occurred: %s" % block)
    return

print("Remove friends: user ids %s, usernames %s" % [ids, usernames])
Client
1
2
3
4
5
6
7
local ids = { "user-id1", "user-id2" }
local usernames = { "username1" }
local result = nakama.block_friends(client, ids, usernames)

if result.error then
  print(result.message)
end
Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/friend/block?ids=user-id1&ids=user-id2&usernames=username1" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
4
5
POST /v2/friend/block?ids=user-id1&ids=user-id2&usernames=username1
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>

Ban a user

A user can be banned with server-side code.

This is best used by a moderator system within your player community. You can assign particular users the ability to send an RPC to permanently ban a user.

Server
1
2
3
4
5
6
7
8
local nk = require("nakama")

local bad_users = {"someuserid", "anotheruserid"}
local success, err = pcall(nk.users_ban_id, bad_users)

if (not success) then
  nk.logger_error(("Ban failed: %q"):format(err))
end
Server
1
2
3
4
5
6
if err := nk.UsersBanId(ctx, []string{
    "someruserid",
    "anotheruserid",
}); err != nil {
    logger.Error("Ban failed: %s", err.Error())
}
Server
1
2
3
4
5
6
7
let badUsers = ['someuserid', 'anotheruserid'];

try {
  nk.usersBanId(badUsers);
} catch (error) {
  // Handle error
}

Banning a user will prevent them from being able to connect to the server and interact at all in the future, but it does not implicitly logout or disconnect their session.

To ensure a banned user cannot reconnect using a still valid auth token, and that any open socket connections are closed, you must ban the user and logout and disconnect their active session(s). See a complete example on banning users.

Friend state

CodePurpose
0Users are friends with each other.
1User A has sent an invitation and pending acceptance from user B.
2User A has received an invitation but has not accepted yet.
3User A has banned user B.