# 모음

**URL:** https://heroiclabs.com/docs/kr/nakama/concepts/storage/collections/
**Summary:** Nakama에는 사용자 계정과 같은 프로젝트별 데이터용 저장소 엔진이 통합되어 있습니다. 저장소 엔진 설계는 객체 소유권, 액세스 권한 및 일괄 작업에 최적화되어 있습니다. 데이터는 JSON 컨텐츠 및 고유 키가 포함된 하나 이상의 객체가 있는 모음에 저장됩니다.

---


# 모음

모든 앱이나 게임에는 프로젝트에 특정한 데이터가 있습니다.

이 정보는 UI의 다양한 부분 내에서 사용자를 위해 저장, 업데이트, 검색 및 표시되어야 합니다. 이를 위해 서버에는 [ 객체 소유권](../permissions/#object-ownership), [액세스 권한](../permissions/#object-permissions) 및 일괄 작업에 최적화된 설계의 저장소 엔진이 통합됩니다.

데이터는 JSON 컨텐츠 및 고유 키가 포함된 하나 이상의 객체가 있는 모음에 저장됩니다. 별도의 구성이 필요하지 않은 모음이 생성됩니다. 이렇게 하면 객체의 위치를 나타내는 간단한 중첩 네임스페이스가 생성됩니다.

```bash {linenos=false}
Collection
+---------------------------------------------------------------------+
|  Object                                                             |
|  +----------+------------+-------------+-----+-------------------+  |
|  | ?UserId? | Identifier | Permissions | ... |       Value       |  |
|  +---------------------------------------------------------------+  |
+---------------------------------------------------------------------+
```

이러한 설계에 따른 뛰어난 유연성으로 개발자는 게임 또는 앱 내에서 함께 속하는 정보 집합을 그룹화할 수 있습니다.

{{< note "important" >}}
저장소 엔진에 내장된 기능을 사용할 경우, 사용자 지정 SQL을 작성하지 않는 것이 좋습니다. 사용자 지정 SQL이 필요한 경우, 진행하기 전에 [Heroic Labs](mailto:support@heroiclabs.com)에 문의하시기 바랍니다.
{{< / note >}}

{{< note "error" >}}
사용자 지정 테이블을 생성하지 않는 것이 좋습니다.
{{< / note >}}

## 객체 쓰기

사용자는 데이터베이스 서버에 저장될 하나 이상의 객체를 작성할 수 있습니다. 이러한 객체는 모든 쓰기를 함께 성공시키는 단일 트랜잭션으로 작성됩니다.

{{< code type="client" >}}
```bash
curl -X PUT "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{"objects":
    [
      {
        "collection": "saves",
        "key": "savegame",
        "value": "{"progress": "50"}"
      },
      {
        "collection": "stats",
        "key": "skill",
        "value": "{"progress": "24"}"
      }
    ]
  }'
```
{{< / code >}}270

{{< code type="client" >}}
```javascript
var save_game = { "progress": 50 };
var my_stats = { "skill": 24 };

const object_ids = await client.writeStorageObjects(session, [
  {
    "collection": "saves",
    "key": "savegame",
    "value": save_game
  }, {
    "collection": "stats",
    "key": "skills",
    "value": my_stats
  }
]);

console.info("Successfully stored objects: ", object_ids);
```
{{< / code >}}270

{{< code type="client" >}}
```csharp
var saveGame = "{ "progress": 50 }";
var myStats = "{ "skill": 24 }";

var writeObjects = new [] {
    new WriteStorageObject
    {
        Collection = "saves",
        Key = "savegame",
        Value = saveGame
    },
    new WriteStorageObject
    {
        Collection = "stats",
        Key = "skills",
        Value = myStats
    }
};

await client.WriteStorageObjectsAsync(session, writeObjects);
Console.WriteLine("Successfully stored objects: [{0}]", string.Join(",\n  ", objectIds));
```
{{< / code >}}270

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjectAcks& acks)
{
    std::cout << "Successfully stored objects " << acks.size() << std::endl;
};

std::vector<NStorageObjectWrite> objects;
NStorageObjectWrite savesObject, statsObject;
savesObject.collection = "saves";
savesObject.key = "savegame";
savesObject.value = "{ "progress": 50 }";
objects.push_back(savesObject);

statsObject.collection = "stats";
statsObject.key = "skills";
statsObject.value = "{ "skill": 24 }";
objects.push_back(statsObject);
client->writeStorageObjects(session, objects, successCallback);
```
{{< / code >}}270

{{< code type="client" >}}
```java
String saveGame = "{ "progress": 50 }";
String myStats = "{ "skill": 24 }";
StorageObjectWrite saveGameObject = new StorageObjectWrite("saves", "savegame", saveGame, PermissionRead.OWNER_READ, PermissionWrite.OWNER_WRITE);
StorageObjectWrite statsObject = new StorageObjectWrite("stats", "skills", myStats, PermissionRead.OWNER_READ, PermissionWrite.OWNER_WRITE);
StorageObjectAcks acks = client.writeStorageObjects(session, saveGameObject, statsObject).get();
System.out.format("Stored objects %s", acks.getAcksList());
```
{{< / code >}}270

{{< code type="client" framework="godot3" >}}
```gdscript
var save_game = "{ "progress": 50 }"
var my_stats = "{ "skill": 24 }"
var can_read = 1
var can_write = 1
var version = ""

var acks : NakamaAPI.ApiStorageObjectAcks = yield(client.write_storage_objects_async(session, [
    NakamaWriteStorageObject.new("saves", "savegame", can_read, can_write, save_game, version),
    NakamaWriteStorageObject.new("stats", "skills", can_read, can_write, my_stats, version)
]), "completed")

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

print("Successfully stored objects:")

for a in acks.acks:
    print("%s" % a)
```
{{< / code >}}270

{{< code type="client" >}}
```shell
PUT /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "objects": [
    {
      "collection": "saves",
      "key": "key",
      "value": "{"hello": "world"}"
    },
    {
      "collection": "stats",
      "key": "skill",
      "value": "{"progress": "24"}"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local save_game = json.encode({ progress = 50 })
local my_stats = json.encode({ skill = 24 })
local can_read = 1
local can_write = 1
local version = ""

local objects = {
  {
				collection = "saves",
				key = "savegame",
				permissionRead = can_read,
				permissionWrite = can_write,
				value = save_game,
				version = version,
  },
  {
				collection = "stats",
				key = "skills",
				permissionRead = can_read,
				permissionWrite = can_write,
				value = my_stats,
				version = version,
  }
}

local result = client.write_storage_objects(objects)

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

for _,ack in ipairs(result.acks) do
  pprint(ack)
end
```
{{< / code >}}270

### 조건부 작성

객체가 성공적으로 저장되면 다음 쓰기와 동시 수정 검사를 수행하기 위해 추가 업데이트와 함께 사용할 수 있는 버전이 반환됩니다. 이것을 조건부 작성이라고 합니다.

조건부 작성의 경우 클라이언트는 이전 버전의 객체를 본 경우에만 객체를 업데이트할 수 있습니다. 이렇게 하는 이유는 다른 클라이언트가 첫 번째 클라이언트의 읽기와 다음 쓰기 사이에 값을 변경한 경우 객체가 변경되는 것을 방지하기 위해서입니다.

{{< code type="client" >}}
```bash
curl -X PUT "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "objects": [
      {
        "collection": "saves",
        "key": "savegame",
        "value": "{"progress": "50"}",
        "version": "some-previous-version"
      }
    ]
  }'
```
{{< / code >}}270

{{< code type="client" >}}
```javascript
var save_game = { "progress": 50 };

const object_ids = await client.writeStorageObjects(session, [
  {
    "collection": "saves",
    "key": "savegame",
    "value": save_game,
    "version": "<version>"
  }
]);

console.info("Stored objects: %o", object_ids);
```
{{< / code >}}270

{{< code type="client" >}}
```csharp
var saveGame = "{ "progress": 50 }";

var objectIds = await client.WriteStorageObjectsAsync(session, new [] { 
  new WriteStorageObject
  {
      Collection = "saves",
      Key = "savegame",
      Value = saveGame,
      Version = "<version>"
  }
});

Console.WriteLine("Stored objects: [{0}]", string.Join(",\n  ", objectIds));
```
{{< / code >}}270

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjectAcks& acks)
{
    std::cout << "Successfully stored objects " << acks.size() << std::endl;
};

std::vector<NStorageObjectWrite> objects;
NStorageObjectWrite savesObject;
savesObject.collection = "saves";
savesObject.key = "savegame";
savesObject.value = "{ "progress": 50 }";
savesObject.version = "<version>";
objects.push_back(savesObject);
client->writeStorageObjects(session, objects, successCallback);
```
{{< / code >}}270

{{< code type="client" >}}
```java
String saveGame = "{ "progress": 50 }";
StorageObjectWrite object = new StorageObjectWrite("saves", "savegame", saveGame, PermissionRead.OWNER_READ, PermissionWrite.OWNER_WRITE);
object.setVersion("<version>");
StorageObjectAcks acks = client.writeStorageObjects(session, object).get();
System.out.format("Stored objects %s", acks.getAcksList());
```
{{< / code >}}270

{{< code type="client" framework="godot3" >}}
```gdscript
var save_game = "{ "progress": 50 }"
var can_read = 1
var can_write = 1
var version = "<version>"

var acks : NakamaAPI.ApiStorageObjectAcks = yield(client.write_storage_objects_async(session, [
    NakamaWriteStorageObject.new("saves", "savegame", can_read, can_write, save_game, version)
]), "completed")

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

print("Successfully stored objects:")

for a in acks.acks:
    print("%s" % a)
```
{{< / code >}}270

{{< code type="client" >}}
```shell
PUT /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "objects": [
    {
      "collection": "saves",
      "key": "key",
      "value": "{"hello": "world"}",
      "version": "<version>"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local save_game = json.encode({ progress = 50 })
local can_read = 1
local can_write = 1
local version = "some previous version"

local objects = {
  {
				collection = "saves",
				key = "savegame",
				permissionRead = can_read,
				permissionWrite = can_write,
				value = save_game,
				version = version,
  }
}

local result = client.write_storage_objects(objects)

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

for _,ack in ipairs(result.acks) do
  pprint(ack)
end
```
{{< / code >}}270

객체의 모음과 키에 대해 이미 존재하지 않는 경우에만 객체를 작성하는 데 사용되는 또 다른 종류의 조건부 작성이 지원됩니다.

{{< code type="client" >}}
```bash
curl -X PUT "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "objects": [
      {
        "collection": "saves",
        "key": "savegame",
        "value": "{"progress": "50"}",
        "version": "*"
      }
    ]
  }'
```
{{< / code >}}270

{{< code type="client" >}}
```javascript
var save_game = { "progress": 50 };
const object_ids = await client.writeStorageObjects(session, [
  {
    "collection": "saves",
    "key": "savegame",
    "value": save_game,
    "version": "*"
  }
]);
console.info("Stored objects: %o", object_ids);
```
{{< / code >}}270

{{< code type="client" >}}
```csharp
var saveGame = "{ "progress": 50 }";

var objectIds = await client.WriteStorageObjectsAsync(session, new [] {
  new WriteStorageObject
  {
      Collection = "saves",
      Key = "savegame",
      Value = saveGame,
      Version = "*"
  }
});

Console.WriteLine("Stored objects: [{0}]", string.Join(",\n  ", objectIds));
```
{{< / code >}}270

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjectAcks& acks)
{
    std::cout << "Successfully stored objects " << acks.size() << std::endl;
};

std::vector<NStorageObjectWrite> objects;
NStorageObjectWrite savesObject;
savesObject.collection = "saves";
savesObject.key = "savegame";
savesObject.value = "{ "progress": 50 }";
savesObject.version = "*";
objects.push_back(savesObject);
client->writeStorageObjects(session, objects, successCallback);
```
{{< / code >}}270

{{< code type="client" >}}
```java
String saveGame = "{ "progress": 50 }";
StorageObjectWrite object = new StorageObjectWrite("saves", "savegame", saveGame, PermissionRead.OWNER_READ, PermissionWrite.OWNER_WRITE);
object.setVersion("*");
StorageObjectAcks acks = client.writeStorageObjects(session, object).get();
System.out.format("Stored objects %s", acks.getAcksList());
```
{{< / code >}}270

{{< code type="client" >}}
```java
// Requires Nakama 1.x

String saveGame = "{"progress": 1}";
String version = "*"; // represents "no version".

CollatedMessage<ResultSet<RecordId>> message = StorageWriteMessage.Builder.newBuilder()
    .record("myapp", "saves", "savegame", saveGame, version)
    .build();

Deferred<ResultSet<RecordId>> deferred = client.send(message);

deferred.addCallback(new Callback<ResultSet<RecordId>, ResultSet<RecordId>>() {
    @Override
    public ResultSet<RecordId> call(ResultSet<RecordId> list) throws Exception {
        // Cache updated version for next write.
        version = list.getResults().get(0).getVersion();
        return list;
    }
}).addErrback(new Callback<Error, Error>() {
    @Override
    public Error call(Error err) throws Exception {
        System.err.format("Error('%s', '%s')", err.getCode(), err.getMessage());
        return err;
    }
});
```
{{< / code >}}270

{{< code type="client" framework="godot3" >}}
```gdscript
var save_game = "{ "progress": 50 }"
var can_read = 1
var can_write = 1
var version = "*" # represents "no version".

var acks : NakamaAPI.ApiStorageObjectAcks = yield(client.write_storage_objects_async(session, [
    NakamaWriteStorageObject.new("saves", "savegame", can_read, can_write, save_game, version)
]), "completed")

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

print("Successfully stored objects:")

for a in acks.acks:
    print("%s" % a)
```
{{< / code >}}270

{{< code type="client" >}}
```shell
PUT /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "objects": [
    {
      "collection": "saves",
      "key": "key",
      "value": "{"hello": "world"}",
      "version": "*"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local save_game = json.encode({ progress = 50 })
local can_read = 1
local can_write = 1
local version = "*"   -- no version


local objects = {
  {
				collection = "saves",
				key = "savegame",
				permissionRead = can_read,
				permissionWrite = can_write,
				value = save_game,
				version = version,
  }
}

local result = client.write_storage_objects(objects)

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

for _,ack in ipairs(result.acks) do
  pprint(ack)
end
```
{{< / code >}}270

## 객체 읽기

[객체 쓰기](#write-objects)와 마찬가지로 데이터베이스 서버에서 하나 이상의 객체를 읽을 수 있습니다.

각 객체에는 소유자와 권한이 있습니다. 권한이 허용하는 경우에만 객체를 읽을 수 있습니다. 소유자가 없는 객체는 `"null"`(으)로 가져올 수 있으며 모든 사용자가 읽어야 하는 글로벌 객체에 유용합니다.

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "object_ids": [
      {
        "collection": "saves",
        "key": "savegame",
        "user_id": "some-user-id"
      }
    ]
  }'
```
{{< / code >}}270

{{< code type="client" >}}
```javascript
const objects = await client.readStorageObjects(session, {
  "object_ids": [{
    "collection": "saves",
    "key": "savegame",
    "user_id": session.user_id
  }]
});
console.info("Read objects: %o", objects);
```
{{< / code >}}270

{{< code type="client" >}}
```csharp
var result = await client.ReadStorageObjectsAsync(session, new [] {
  new StorageObjectId {
    Collection = "saves",
    Key = "savegame",
    UserId = session.UserId
  }
});

Console.WriteLine("Read objects: [{0}]", string.Join(",\n  ", result.Objects));
```
{{< / code >}}270

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjects& objects)
{
  for (auto& object : objects)
    {
        std::cout << "Object key: " << object.key << ", value: " << object.value << std::endl;
    }
};

std::vector<NReadStorageObjectId> objectIds;
NReadStorageObjectId objectId;
objectId.collection = "saves";
objectId.key = "savegame";
objectId.userId = session->getUserId();
objectIds.push_back(objectId);
client->readStorageObjects(session, objectIds, successCallback);
```
{{< / code >}}270

{{< code type="client" >}}
```java
StorageObjectId objectId = new StorageObjectId("saves");
objectId.setKey("savegame");
objectId.setUserId(session.getUserId());
StorageObjects objects = client.readStorageObjects(session, objectId).get();
System.out.format("Read objects %s", objects.getObjectsList().toString());
```
{{< / code >}}270

{{< code type="client" framework="godot3" >}}
```gdscript
var result : NakamaAPI.ApiStorageObjects = yield(client.read_storage_objects_async(session, [
    NakamaStorageObjectId.new("saves", "savegame", session.user_id)
]), "completed")

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

print("Read objects:")

for o in result.objects:
    print("%s" % o)
```
{{< / code >}}270

{{< code type="client" >}}
```shell
POST /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "object_ids": [
    {
      "collection": "saves",
      "key": "savegame",
      "user_id": "some-user-id"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local user_id = "some user id"

local objects_ids = {
  {
				collection = "saves",
				key = "savegame",
        userId = user_id
  }
}

local result = client.read_storage_objects(objects_ids)

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

for _,object in ipairs(result.objects) do
  pprint(object)
end
```
{{< / code >}}270

## 객체 나열

결과를 통해 모음 및 페이지에 객체를 나열할 수 있습니다. 반환된 객체는 사용자 또는 `"null"`이(가) 소유한 객체 또는 사용자가 소유하지 않은 공개 레코드에 대해 필터링될 수 있습니다.

{{< code type="client" >}}
```bash
curl -X GET "http://127.0.0.1:7350/v2/storage/saves?user_id=some-user-id&limit=10" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}270

{{< code type="client" >}}
```javascript
const limit = 100; // default is 10.
const objects = await client.listStorageObjects(session, "saves", session.user_id, limit);
console.info("List objects: %o", objects);
```
{{< / code >}}270

{{< code type="client" >}}
```csharp
const int limit = 100; // default is 10.
var result = await client.ListUsersStorageObjectsAsync(session, "saves", session.UserId, limit);
Console.WriteLine("List objects: {0}", result);
```
{{< / code >}}270

{{< code type="client" >}}
```cpp
auto successCallback = [](NStorageObjectListPtr list)
{
    for (auto& object : list->objects)
    {
        std::cout << "Object key: " << object.key << ", value: " << object.value << std::endl;
    }
};
client->listUsersStorageObjects(session, "saves", session->getUserId(), opt::nullopt, opt::nullopt, successCallback);
```
{{< / code >}}270

{{< code type="client" >}}
```java
StorageObjectList objects = client.listUsersStorageObjects(session, "saves", session.getUserId()).get();
System.out.format("List objects %s", objects);
```
{{< / code >}}270

{{< code type="client" framework="godot3" >}}
```gdscript
var limit = 100 # default is 10.
var objects : NakamaAPI.ApiStorageObjectList = yield(client.list_storage_objects_async(session, "saves", session.user_id, limit), "completed")

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

print("List objects: %s" % objects)
```
{{< / code >}}270

{{< code type="client" >}}
```shell
GET /v2/storage/<collection>?user_id=<user_id>&limit=<limit>&cursor=<cursor>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local user_id = "some user id"
local limit = 10
local result = client.list_storage_objects("saves", user_id, limit)

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

for _,object in ipairs(result.objects) do
  pprint(object)
end
```
{{< / code >}}270

## 객체 제거

사용자는 객체에 올바른 권한이 있고 소유하고 있는 경우 해당 개체를 제거할 수 있습니다.

{{< code type="client" >}}
```bash
curl -X PUT "http://127.0.0.1:7350/v2/storage/delete" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "object_ids": [
      {
        "collection": "saves",
        "key": "savegame"
      }
    ]
  }'
```
{{< / code >}}270

{{< code type="client" >}}
```javascript
await client.deleteStorageObjects(session, {
  "object_ids": [{
    "collection": "saves",
    "key": "savegame"
  }]
});
console.info("Deleted objects.");
```
{{< / code >}}270

{{< code type="client" >}}
```csharp
var result = await client.DeleteStorageObjectsAsync(session, new [] {
  new StorageObjectId {
    Collection = "saves",
    Key = "savegame"
  }
});

Console.WriteLine("Deleted objects.");
```
{{< / code >}}270

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "Deleted objects." << std::endl;
};

std::vector<NDeleteStorageObjectId> objectIds;
NDeleteStorageObjectId objectId;
objectId.collection = "saves";
objectId.key = "savegame";
objectIds.push_back(objectId);
client->deleteStorageObjects(session, objectIds, successCallback);
```
{{< / code >}}270

{{< code type="client" >}}
```java
StorageObjectId objectId = new StorageObjectId("saves");
objectId.setKey("savegame");
client.deleteStorageObjects(session, objectId).get();
System.out.format("Deleted objects.");
```
{{< / code >}}270

{{< code type="client" framework="godot3" >}}
```gdscript
var del : NakamaAsyncResult = yield(client.delete_storage_objects_async(session, [
  NakamaStorageObjectId.new("saves", "savegame")
]), "completed")

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

print("Deleted objects.")
```
{{< / code >}}270

{{< code type="client" >}}
```shell
PUT /v2/storage/delete
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "object_ids": [
    {
      "collection": "saves",
      "key": "savegame"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local objects_ids = {
  {
				collection = "saves",
				key = "savegame"
  }
}

local result = client.delete_storage_objects(object_ids)

if result.error then
  print(result.message)
  return
end
```
{{< / code >}}270

객체 버전이 클라이언트에서 보낸 버전과 일치하는 경우 조건부로 객체를 제거할 수도 있습니다.

{{< code type="client" >}}
```bash
curl -X PUT \
  http://127.0.0.1:7350/v2/storage/delete \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "object_ids": [
      {
        "collection": "saves",
        "key": "savegame",
        "version": "<version>"
      }
    ]
  }'
```
{{< / code >}}270

{{< code type="client" >}}
```javascript
await client.deleteStorageObjects(session, {
  "object_ids": [{
    "collection": "saves",
    "key": "savegame",
    "version": "<version>"
  }]
});
console.info("Deleted objects.");
```
{{< / code >}}270

{{< code type="client" >}}
```csharp
var result = await client.DeleteStorageObjectsAsync(session, new [] {
  new StorageObjectId {
    Collection = "saves",
    Key = "savegame",
    UserId = session.UserId,
    Version = "<version>"
  }
});

Console.WriteLine("Deleted objects.");
```
{{< / code >}}270

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "Deleted objects." << std::endl;
};

std::vector<NDeleteStorageObjectId> objectIds;
NDeleteStorageObjectId objectId;
objectId.collection = "saves";
objectId.key = "savegame";
objectId.version = "<version>";
objectIds.push_back(objectId);
client->deleteStorageObjects(session, objectIds, successCallback);
```
{{< / code >}}270

{{< code type="client" >}}
```java
StorageObjectId objectId = new StorageObjectId("saves");
objectId.setKey("savegame");
objectId.setVersion("<version>");
client.deleteStorageObjects(session, objectId).get();
System.out.format("Deleted objects.");
```
{{< / code >}}270

{{< code type="client" framework="godot3" >}}
```gdscript
var del = yield(client.delete_storage_objects_async(session, [
  NakamaStorageObjectId.new("saves", "savegame", session.user_id, "<version>")
]), "completed")

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

print("Deleted objects.")
```
{{< / code >}}270

{{< code type="client" >}}
```shell
PUT /v2/storage/delete
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "object_ids": [
    {
      "collection": "saves",
      "key": "savegame",
      "version": "<version>"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local objects_ids = {
  {
				collection = "saves",
				key = "savegame",
        version = "some version"
  }
}

local result = nakama.delete_storage_objects(object_ids)

if result.error then
  print(result.message)
  return
end
```
{{< / code >}}
