diff --git a/content/en-us/cloud/open-cloud/usage-data-stores.md b/content/en-us/cloud/open-cloud/usage-data-stores.md
index ef9d64d76..7fd055fba 100644
--- a/content/en-us/cloud/open-cloud/usage-data-stores.md
+++ b/content/en-us/cloud/open-cloud/usage-data-stores.md
@@ -78,6 +78,9 @@ When [creating an API Key](./api-keys.md) for this example, make sure you perfor
#### Add scripts for the user inventory support portal
+
+ Click for Python Example
+
After creating the API key with permissions required for the example app, you need to add Python scripts to perform app functionalities. The `data_stores_methods.py` file shows how to define [`List Entries`](../../reference/cloud/datastores-api/v1.json#list-entries), [`Get Entry`](../../reference/cloud/datastores-api/v1.json#get-entry), and [`Increment Entry`](../../reference/cloud/datastores-api/v1.json#increment-entry) methods. The `update_inventory` file uses the defined methods to list a subset of user inventories, increase the virtual currency for each user, and update the data.
```python title='data_stores_methods.py'
@@ -186,6 +189,102 @@ To test, set the `API_KEY` environment variable and run `update_inventory` file:
export API_KEY=... \
python update_inventory
```
+
+
+
+
+ Click for JavaScript Example
+
+After creating the API key with the required permissions for the example app, you need to add JavaScript code to perform app functionalities. The `data_stores_methods.js` file shows how to define [`List Entries`](../../reference/cloud/datastores-api/v1.json#list-entries), [`Get Entry`](../../reference/cloud/datastores-api/v1.json#get-entry), and [`Increment Entry`](../../reference/cloud/datastores-api/v1.json#increment-entry) methods. The `update_inventory.js` file uses the defined methods to list a subset of user inventories, increase the virtual currency for each user, and update the data.
+
+```javascript title='dataStoresMethods.js'
+const fetch = require('node-fetch');
+
+class DataStores {
+ constructor() {
+ this._baseUrl = "https://apis.roblox.com/datastores/v1/universes/{universeId}";
+ this._apiKey = process.env.API_KEY;
+ this._universeId = "UNIVERSE_ID";
+ this.ATTR_HDR = 'Roblox-entry-Attributes';
+ this.USER_ID_HDR = 'Roblox-entry-UserIds';
+ this._objectsUrl = `${this._baseUrl}${this._universeId}/standard-datastores/datastore/entries/entry`;
+ this._incrementUrl = `${this._objectsUrl}/increment`;
+ this._listObjectsUrl = `${this._baseUrl}${this._universeId}/standard-datastores/datastore/entries`;
+ }
+
+ async _getHeaders() {
+ return { 'x-api-key': this._apiKey };
+ }
+
+ async getEntry(datastore, objectKey, scope = null) {
+ const url = `${this._objectsUrl}?datastoreName=${datastore}&entryKey=${objectKey}&scope=${scope || ''}`;
+ const response = await fetch(url, { headers: await this._getHeaders() });
+ const data = await response.json();
+ return data;
+ }
+
+ async listEntries(datastore, scope = null, prefix = "", limit = 100) {
+ const url = `${this._listObjectsUrl}?datastoreName=${datastore}&scope=${scope}&prefix=${prefix}&limit=${limit}`;
+ const response = await fetch(url, { headers: await this._getHeaders() });
+ const data = await response.json();
+ return data;
+ }
+
+ async incrementEntry(datastore, objectKey, incrementBy, scope = null) {
+ const url = `${this._incrementUrl}?datastoreName=${datastore}&entryKey=${objectKey}&incrementBy=${incrementBy}&scope=${scope || ''}`;
+ const response = await fetch(url, { method: 'POST', headers: await this._getHeaders() });
+ const data = await response.json();
+ return data;
+ }
+}
+
+module.exports = DataStores;
+```
+
+```javascript title='updateInventory.js'
+const DataStores = require('./dataStoresMethods');
+const dataStoresApi = new DataStores();
+
+// Set up
+const datastoreName = "Inventory";
+
+// List keys for a subset of users
+dataStoresApi.listEntries(datastoreName)
+ .then(keys => {
+ console.log(keys);
+ });
+
+// Read inventory for each user
+for (let x = 0; x < 5; x++) {
+ const updatedObjectKey = `User_${x + 1}`;
+ dataStoresApi.getEntry(datastoreName, updatedObjectKey)
+ .then(value => {
+ console.log(`${updatedObjectKey} has ${value.gems} gems in their inventory`);
+ });
+}
+
+// Update the currency of each user by 10
+for (let x = 0; x < 5; x++) {
+ const updatedObjectKey = `User_${x + 1}`;
+ dataStoresApi.incrementEntry(datastoreName, updatedObjectKey, 10)
+ .then(value => {
+ console.log(`${updatedObjectKey} now has ${value.robux} robux in their inventory`);
+ });
+}
+```
+
+To test, set the `API_KEY` environment variable and run the JavaScript file:
+
+```bash
+export API_KEY=... \
+node updateInventory.js
+```
+
+
+
+### External Persistent Leaderboard
+
+=======
### External persistent leaderboard
@@ -219,6 +318,9 @@ When [creating an API Key](./api-keys.md) for this example, make sure you perfor
#### Add scripts for the leaderboard
+
+Click for Python Example
+
After creating the API key with permissions required for the example app, you need to add Python scripts to perform app functionalities.
The `ordered_data_stores.py` file shows how to define [`List`](../../reference/cloud/datastores-api/ordered-v1.json#list), [`Create`](../../reference/cloud/datastores-api/ordered-v1.json#create), [`Update`](../../reference/cloud/datastores-api/ordered-v1.json#update) and [`Increment`](../../reference/cloud/datastores-api/ordered-v1.json#increment) methods. The `leaderboard` file uses the defined methods to create entries of users in ordered data stores, display scores, increment scores of winning users, and update the leaderboard. The `leaderboard` file also imports a `config` JSON file for configuring the Universe ID, API domain, and your API key.
@@ -308,3 +410,105 @@ python leaderboard
```
After completing testing, you can publish or embed the leaderboard to websites outside of Roblox for more reach.
+
+
+
+
+Click for JavaScript Example
+
+After creating the API key with the required permissions for the example app, you need to add JavaScript code to perform app functionalities.
+
+The `ordered_data_stores.js` file shows how to define [`List`](../../reference/cloud/datastores-api/ordered-v1.json#list), [`Create`](../../reference/cloud/datastores-api/ordered-v1.json#create), [`Update`](../../reference/cloud/datastores-api/ordered-v1.json#update), and [`Increment`](../../reference/cloud/datastores-api/ordered-v1.json#increment) methods. The `leaderboard.js` file uses the defined methods to create entries of users in ordered data stores, display scores, increment scores of winning users, and update the leaderboard. The `leaderboard.js` file also imports a `config.json` file for configuring the Universe ID, API domain, and your API key.
+
+```javascript title='ordered_data_stores.js'
+const axios = require('axios');
+const fs = require('fs');
+
+class DataStores {
+ constructor(configFile) {
+ this._config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
+ }
+
+ _H() {
+ return { 'x-api-key': this._config.api_key, 'Content-Type': 'application/json' };
+ }
+
+ async list(datastore, scope, pageSize = 10, orderBy = "", filter = "", exclusiveStartKey = "") {
+ const url = `${this._config.api_key_url}universes/${this._config.universe_id}/orderedDataStores/${datastore}/scopes/${scope}/entries`;
+ const response = await axios.get(url, {
+ headers: this._H(),
+ params: { "max_page_size": pageSize, "order_by": orderBy, "filter": filter, "page_token": exclusiveStartKey }
+ });
+ return response.data;
+ }
+
+ async create(datastore, scope, entry, data) {
+ const url = `${this._config.api_key_url}universes/${this._config.universe_id}/orderedDataStores/${datastore}/scopes/${scope}/entries`;
+ const payload = JSON.stringify({ "value": 11 });
+ const response = await axios.post(url, payload, {
+ headers: this._H(),
+ params: { "id": entry }
+ });
+ return response.data;
+ }
+
+ async increment(datastore, scope, entry, incrementBy) {
+ const url = `${this._config.api_key_url}universes/${this._config.universe_id}/orderedDataStores/${datastore}/scopes/${scope}/entries/${entry}:increment`;
+ const payload = JSON.stringify({ "amount": incrementBy });
+ const response = await axios.post(url, payload, { headers: this._H() });
+ return response.data;
+ }
+}
+```
+
+```javascript title='leaderboard.js'
+const leaderboardEndpoints = require('./ordered_data_stores');
+
+const datastores = new leaderboardEndpoints.DataStores('config.json');
+
+// Variables
+const orderedDataStore = 'PlayerScores';
+const scope = 'global';
+const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
+
+// Create an entry and give each new player 50 points for joining the game
+entryNames.forEach(async (name) => {
+ await datastores.create(orderedDataStore, scope, name, 50);
+});
+
+// Display the players' scores
+datastores.list(orderedDataStore, scope).then((playerScores) => {
+ console.log(playerScores);
+});
+
+// Increment the first player's score for winning the game
+datastores.increment(orderedDataStore, scope, entryNames[0], 100);
+
+// Increment all the players' scores for participating in the game
+entryNames.forEach(async (name) => {
+ await datastores.increment(orderedDataStore, scope, name, 10);
+});
+
+// Display the updated leaderboard
+datastores.list(orderedDataStore, scope).then((updatedPlayerScores) => {
+ console.log(updatedPlayerScores);
+});
+```
+
+```json title='config'
+{
+ "universe_id": "",
+ "api_key_url": "https://apis.roblox.com/datastores/ordered-v1/",
+ "api_key": ""
+}
+```
+
+To test, set the `API_KEY` environment variable and run the `leaderboard.js` file:
+
+```bash
+export API_KEY=... \
+node leaderboard.js
+```
+
+After completing testing, you can publish or embed the leaderboard to websites outside of Roblox for more reach.
+