Collections Every app or game has data which is specific to the project.
This information must be stored for each user, as well as updated, retrieved, and displayed within various parts of a UI. For this purpose the server incorporates a storage engine with a design optimized for object ownership , access permissions , and batch operations.
Data is stored in collections with one or more objects which contain a unique key with JSON content. A collection is created without any configuration required. This creates a simple nested namespace which represents the location of a object.
This design gives great flexibility for developers to group sets of information which belong together within a game or app.
Collection
 +---------------------------------------------------------------------+
 |   Object                                                             | 
|   +----------+------------+-------------+-----+-------------------+  | 
|   |  ?UserId? |  Identifier |  Permissions |  ... |        Value       |   | 
|   +---------------------------------------------------------------+  | 
+---------------------------------------------------------------------+
 The Collection and Key are used to identify the object itself. The UserId is used to identify the owner of an object, and to check for the needed read/write permissions when calling those operations on a object from the client.
Writing custom SQL is discouraged in favor of using the built-in features of the Storage Engine. If custom SQL is needed for your use case, please 
contact Heroic Labs  before proceeding.
The creation of custom tables is strongly discouraged.
Write objects
#  A user can write one or more objects which will be stored in the database server. These objects will be written in a single transaction which guarantees the writes succeed together.
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 curl -X PUT "http://127.0.0.1:7350/v2/storage"  \
  'Authorization: Bearer <session token>'  \
 '{"objects":
     [
       {
         "collection": "saves",
         "key": "savegame",
         "value": "{\"progress\": \"50\"}"
       },
       {
         "collection": "stats",
         "key": "skill",
         "value": "{\"progress\": \"24\"}"
       }
     ]
   }' 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 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 ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 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 )); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 let  saveGame  =  "{  \" progress \" : 50 }" 
let  myStats  =  "{  \" skill \" : 24 }" 
 let  writeObjects  =  [ 
    WriteStorageObject ( collection :  "saves" ,  key :  "savegame" ,  value :  saveGame ), 
     WriteStorageObject ( collection :  "stats" ,  key :  "skills" ,  value :  myStats ) 
 ] 
 let  acks  =  try  await  client . writeStorageObjects ( session :  session ,  objects :  writeObjects ) 
debugPrint ( "Successfully stored objects:" ,  acks ) 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 const  saveGame  =  '{"progress": 50}' ; 
const  myStats  =  '{"skill": 24}' ; 
 var  writeObjects  =  [ 
  WriteStorageObject ( collection:  'saves' ,  key:  'savegame' ,  value:  saveGame ), 
   WriteStorageObject ( collection:  'stats' ,  key:  'skills' ,  value:  myStats ), 
 ]; 
 for  ( final  wo  in  writeObjects )  { 
  await  client . writeStorageObject ( 
     session:  session , 
     collection:  wo . collection , 
     key:  wo . key , 
     value:  wo . value , 
   ); 
 } 
print ( 'Successfully stored objects:  $ writeObjects ' ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 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 ); 
Client
1
 2
 3
 4
 5
 6
 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 ()); 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 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 ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 var  save_game  =  "{  \" progress \" : 50 }" 
var  my_stats  =  "{  \" skill \" : 24 }" 
var  can_read  =  1 
var  can_write  =  1 
var  version  =  "" 
 var  acks  :  NakamaAPI . ApiStorageObjectAcks  =  await  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 ) 
 ]) 
 if  acks . is_exception (): 
    print ( "An error occurred:  %s "  %  acks ) 
     return 
 
 print ( "Successfully stored objects:" ) 
 for  a  in  acks . acks : 
    print ( " %s "  %  a ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 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\"}" 
     } 
   ] 
 } 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 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 
Conditional writes
#  When objects are successfully stored a version is returned which can be used with further updates to perform concurrent modification checks with the next write. This is known as a conditional write.
A conditional write ensures a client can only update the object if they’ve seen the previous version of the object. The purpose is to prevent a change to the object if another client has changed the value between the first client’s read and its next write.
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 curl -X PUT "http://127.0.0.1:7350/v2/storage"  \
  'Authorization: Bearer <session token>'  \
 '{
     "objects": [
       {
         "collection": "saves",
         "key": "savegame",
         "value": "{\"progress\": \"50\"}",
         "version": "some-previous-version"
       }
     ]
   }' 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 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 ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 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 )); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 let  saveGame  =  "{  \" progress \" : 50 }" 
 let  acks  =  try  await  client . writeStorageObjects ( session :  session ,  WriteStorageObject ( 
    collection :  "saves" , 
     key :  "savegame" , 
     value :  saveGame , 
     version :  "<version>" 
 )) 
 debugPrint ( "Stored objects:" ,  objectIds ) 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 const  saveGame  =  '{"progress": 50}' ; 
 await  client . writeStorageObject ( 
    session:  session , 
     collection:  'saves' , 
     key:  'savegame' , 
     value:  saveGame , 
     version:  '<version>' , 
 ); 
 print ( 'Stored objects:' ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 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 ); 
Client
1
 2
 3
 4
 5
 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 ()); 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 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 ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 var  save_game  =  "{  \" progress \" : 50 }" 
var  can_read  =  1 
var  can_write  =  1 
var  version  =  "<version>" 
 var  acks  :  NakamaAPI . ApiStorageObjectAcks  =  await  client . write_storage_objects_async ( session ,  [ 
    NakamaWriteStorageObject . new ( "saves" ,  "savegame" ,  can_read ,  can_write ,  save_game ,  version ) 
 ]) 
 if  acks . is_exception (): 
    print ( "An error occurred:  %s "  %  acks ) 
     return 
 
 print ( "Successfully stored objects:" ) 
 for  a  in  acks . acks : 
    print ( " %s "  %  a ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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>" 
     } 
   ] 
 } 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 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 
We support another kind of conditional write which is used to write an object only if none already exists for that object’s collection and key.
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 curl -X PUT "http://127.0.0.1:7350/v2/storage"  \
  'Authorization: Bearer <session token>'  \
 '{
     "objects": [
       {
         "collection": "saves",
         "key": "savegame",
         "value": "{\"progress\": \"50\"}",
         "version": "*"
       }
     ]
   }' 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 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 ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 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 )); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 let  saveGame  =  "{  \" progress \" : 50 }" 
 let  acks  =  try  await  client . writeStorageObjects ( session :  session ,  WriteStorageObject ( 
    collection :  "saves" , 
     key :  "savegame" , 
     value :  saveGame , 
     version :  "*" 
 )) 
 debugPrint ( "Stored objects:" ,  objectIds ) 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 const  saveGame  =  '{"progress": 50}' ; 
 await  client . writeStorageObject ( 
    session:  session , 
     collection:  'saves' , 
     key:  'savegame' , 
     value:  saveGame , 
     version:  '*' , 
 ); 
 print ( 'Stored objects:' ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 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 ); 
Client
1
 2
 3
 4
 5
 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 ()); 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 // 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 ; 
      } 
 }); 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 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 ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 var  save_game  =  "{  \" progress \" : 50 }" 
var  can_read  =  1 
var  can_write  =  1 
var  version  =  "*"  # represents "no version". 
 var  acks  :  NakamaAPI . ApiStorageObjectAcks  =  await  client . write_storage_objects_async ( session ,  [ 
    NakamaWriteStorageObject . new ( "saves" ,  "savegame" ,  can_read ,  can_write ,  save_game ,  version ) 
 ]) 
 if  acks . is_exception (): 
    print ( "An error occurred:  %s "  %  acks ) 
     return 
 
 print ( "Successfully stored objects:" ) 
 for  a  in  acks . acks : 
    print ( " %s "  %  a ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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" : "*" 
     } 
   ] 
 } 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 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 
Read objects
#  Just like with writing objects  you can read one or more objects from the database server.
Each object has an owner and permissions. An object can only be read if the permissions allow it. An object which has no owner can be fetched with "null" and is useful for global objects which all users should be able to read.
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 curl -X POST "http://127.0.0.1:7350/v2/storage"  \
  'Authorization: Bearer <session token>'  \
 '{
     "object_ids": [
       {
         "collection": "saves",
         "key": "savegame",
         "user_id": "some-user-id"
       }
     ]
   }' 
Client
1
 2
 3
 4
 5
 6
 7
 8
 const  objects  =  await  client . readStorageObjects ( session ,  { 
  "object_ids" :  [{ 
     "collection" :  "saves" , 
     "key" :  "savegame" , 
     "user_id" :  session . user_id 
   }] 
 }); 
console . info ( "Read objects: %o" ,  objects ); 
Client
1
 2
 3
 4
 5
 6
 7
 8
 9
 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 )); 
Client
1
 2
 3
 4
 5
 6
 var  result  =  try  await  client . readStorageObjects ( session :  session ,  ids :  [ 
    StorageObjectId ( collection :  "saves" ,  key :  "savegame" ,  userId :  session . UserId ) 
   ] 
 ) 
 debugPrint ( "Read objects:" ,  result . objects ) 
Client
1
 2
 3
 4
 5
 6
 7
 8
 final  result  =  await  client . readStorageObject ( 
  session:  session , 
   collection:  'saves' , 
   key:  'savegame' , 
   userId:  session . userId , 
 ) 
 print ( 'Read objects:  ${ result } ' ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 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 ); 
Client
1
 2
 3
 4
 5
 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 ()); 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 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 ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 var  result  :  NakamaAPI . ApiStorageObjects  =  await  client . read_storage_objects_async ( session ,  [ 
    NakamaStorageObjectId . new ( "saves" ,  "savegame" ,  session . user_id ) 
 ]) 
 if  result . is_exception (): 
    print ( "An error occurred:  %s "  %  result ) 
     return 
 
 print ( "Read objects:" ) 
 for  o  in  result . objects : 
    print ( " %s "  %  o ) 
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 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" 
     } 
   ] 
 } 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 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 
List objects
#  You can list objects in a collection and page through results.
The objects returned can be filtered to those owned by a specific user, or "null" for all public records.
Client
1
 2
 curl -X GET "http://127.0.0.1:7350/v2/storage/saves?user_id=some-user-id&limit=10"  \
  'Authorization: Bearer <session token>' 
Client
1
 2
 3
 const  limit  =  100 ;  // default is 10.
 const  objects  =  await  client . listStorageObjects ( session ,  "saves" ,  session . user_id ,  limit ); 
console . info ( "List objects: %o" ,  objects ); 
Client
1
 2
 3
 const  int  limit  =  100 ;  // default is 10. 
var  result  =  await  client . ListUsersStorageObjectsAsync ( session ,  "saves" ,  session . UserId ,  limit ); 
Console . WriteLine ( "List objects: {0}" ,  result ); 
Client
1
 2
 3
 let  limit  =  100  // default is 10. 
var  result  =  try  await  client . listStorageObjects ( session :  session ,  collection :  "saves" ,  limit :  limit ) 
debugPrint ( "List objects:" ,  result . objects ) 
Client
1
 2
 3
 4
 5
 6
 7
 8
 const  limit  =  100 ;  // default is 10.
 final  result  =  await  client . listStorageObjects ( 
  session:  session , 
   collection:  'saves' , 
   userId:  session . userId , 
   limit:  limit , 
 ); 
print ( 'List objects:  ${ result . objects } ' ); 
Client
1
 2
 3
 4
 5
 6
 7
 8
 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 ); 
Client
1
 2
 StorageObjectList   objects   =   client . listUsersStorageObjects ( session ,   "saves" ,   session . getUserId ()). get (); 
 System . out . format ( "List objects %s" ,   objects ); 
 
Client
1
 2
 3
 4
 5
 6
 7
 8
 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 ) 
Client
1
 2
 3
 4
 5
 6
 7
 8
 var  limit  =  100  # default is 10. 
var  objects  :  NakamaAPI . ApiStorageObjectList  =  await  client . list_storage_objects_async ( session ,  "saves" ,  session . user_id ,  limit ) 
 if  objects . is_exception (): 
  print ( "An error occurred:  %s "  %  objects ) 
   return 
 
 print ( "List objects:  %s "  %  objects ) 
Client
1
 2
 3
 4
 5
 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>
 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 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 
Remove objects
#  A user can remove an object if it has the correct permissions and they own it.
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 curl -X PUT "http://127.0.0.1:7350/v2/storage/delete"  \
  'Authorization: Bearer <session token>'  \
 '{
     "object_ids": [
       {
         "collection": "saves",
         "key": "savegame"
       }
     ]
   }' 
Client
1
 2
 3
 4
 5
 6
 7
 await  client . deleteStorageObjects ( session ,  { 
  "object_ids" :  [{ 
     "collection" :  "saves" , 
     "key" :  "savegame" 
   }] 
 }); 
console . info ( "Deleted objects." ); 
Client
1
 2
 3
 4
 5
 6
 7
 8
 var  result  =  await  client . DeleteStorageObjectsAsync ( session ,  new  []  { 
  new  StorageObjectId  { 
     Collection  =  "saves" , 
     Key  =  "savegame" 
   } 
 }); 
 Console . WriteLine ( "Deleted objects." ); 
Client
1
 2
 3
 var  result  =  try  await  client . deleteStorageObjects ( session :  session ,  ids :  [ StorageObjectId ( collection :  "saves" ,  key :  "savegame" )]) 
 debugPrint ( "Deleted objects." ) 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 final  result  =  await  client . deleteStorageObjects ( 
  session:  session , 
   ids:  [ 
     StorageObjectId ( 
       collection:  'saves' , 
       key:  'savegame' , 
     ), 
   ], 
 ); 
 print ( 'Deleted objects.' ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 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 ); 
Client
1
 2
 3
 4
 StorageObjectId   objectId   =   new   StorageObjectId ( "saves" ); 
 objectId . setKey ( "savegame" ); 
 client . deleteStorageObjects ( session ,   objectId ). get (); 
 System . out . format ( "Deleted objects." ); 
 
Client
1
 2
 3
 4
 5
 6
 7
 8
 9
 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." ) 
Client
1
 2
 3
 4
 5
 6
 7
 8
 9
 var  del  :  NakamaAsyncResult  =  await  client . delete_storage_objects_async ( session ,  [ 
  NakamaStorageObjectId . new ( "saves" ,  "savegame" ) 
 ]) 
 if  del . is_exception (): 
  print ( "An error occurred:  %s "  %  del ) 
   return 
 
 print ( "Deleted objects." ) 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 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" 
     } 
   ] 
 } 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 local  objects_ids  =  { 
  { 
     collection  =  "saves" , 
     key  =  "savegame" 
   } 
 } 
 local  result  =  client.delete_storage_objects ( object_ids ) 
 if  result.error  then 
  print ( result.message ) 
   return 
 end 
You can also conditionally remove an object if the object version matches the version sent by the client.
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 curl -X PUT \
  \
 'Authorization: Bearer <session token>'  \
 '{
     "object_ids": [
       {
         "collection": "saves",
         "key": "savegame",
         "version": "<version>"
       }
     ]
   }' 
Client
1
 2
 3
 4
 5
 6
 7
 8
 await  client . deleteStorageObjects ( session ,  { 
  "object_ids" :  [{ 
     "collection" :  "saves" , 
     "key" :  "savegame" , 
     "version" :  "<version>" 
   }] 
 }); 
console . info ( "Deleted objects." ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 var  result  =  await  client . DeleteStorageObjectsAsync ( session ,  new  []  { 
  new  StorageObjectId  { 
     Collection  =  "saves" , 
     Key  =  "savegame" , 
     UserId  =  session . UserId , 
     Version  =  "<version>" 
   } 
 }); 
 Console . WriteLine ( "Deleted objects." ); 
Client
1
 2
 3
 4
 5
 var  result  =  try  await  client . deleteStorageObjects ( session :  session ,  ids :  [ 
    StorageObjectId ( collection :  "saves" ,  key :  "savegame" ,  userId :  session . userId ,  version  =  "<version>" ) 
 ]) 
 debugPrint ( "Deleted objects." ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 final  result  =  try  await  client . deleteStorageObjects ( 
  session:  session , 
   ids:  [ 
     StorageObjectId ( 
       collection:  'saves' , 
       key:  'savegame' , 
       userId:  session . userId , 
       version  =  '<version>' , 
     ), 
   ], 
 ); 
 print ( 'Deleted objects.' ); 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 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 ); 
Client
1
 2
 3
 4
 5
 StorageObjectId   objectId   =   new   StorageObjectId ( "saves" ); 
 objectId . setKey ( "savegame" ); 
 objectId . setVersion ( "<version>" ); 
 client . deleteStorageObjects ( session ,   objectId ). get (); 
 System . out . format ( "Deleted objects." ); 
 
Client
1
 2
 3
 4
 5
 6
 7
 8
 9
 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." ) 
Client
1
 2
 3
 4
 5
 6
 7
 8
 9
 var  del  =  await  client . delete_storage_objects_async ( session ,  [ 
  NakamaStorageObjectId . new ( "saves" ,  "savegame" ,  session . user_id ,  "<version>" ) 
 ]) 
 if  del . is_exception (): 
  print ( "An error occurred:  %s "  %  del ) 
   return 
 
 print ( "Deleted objects." ) 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 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>" 
     } 
   ] 
 } 
Client
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 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 
Related Pages