Skip to main content
Deno 2 is finally here 🎉️
Learn more

Active Development

This module is currently in the initial development phase. Breaking changes may be introduced without warning before a stable 1.0.0 release. Check release notes of every release before updating.

dynablox opencloud


docs | deno.land/x | npm | release notes

An API wrapper for the OpenCloud Roblox API built using the Deno runtime, with support for NodeJS via transpilation.

  • Compatible: Built in TypeScript using Deno. Supported in NodeJS as a ES and CommonJS module via transpilation.
  • API Error handling: No matter the endpoint, errors are formatted into the same object format.
  • Permission checked (optional): Optional behavior to check if the current client has permissions for a resource before performing a request.

This is a stripped-down version of the original library, for Open Cloud.



Installation

Node Installation

Steps to install Node can be found here.

Run

npm install @dynabloxjs/opencloud

or

yarn add @dynabloxjs/opencloud

to install. (yarn add requires yarn)

Import it with CommonJS:

const dynablox = require("@dynabloxjs/opencloud");

or ESModule:

import * as dynablox from "@dynabloxjs/opencloud";

For pre-release builds:

Run

npm install dynabloxjs/dynablox_opencloud#node

or

yarn add dynabloxjs/dynablox_opencloud#node

to install the current pre-release build. (yarn add requires yarn)

Deno Installation

Steps to install Deno can be found here.

Unlike Node or Python, Deno modules only need to be imported via a path (and then cached).

Once you have done the necessary steps, import it:

import * as dynablox from "https://deno.land/x/dynablox_opencloud/mod.ts";

NOTE: You can also specify a version by appending @{version} to the module name.

… and it’s done! Only the --allow-net flag is needed for Dynablox Open Cloud, see more information about flags in the Deno manual.

For pre-release builds:

Replace https://deno.land/x/dynablox_opencloud/mod.ts with https://raw.githubusercontent.com/dynabloxjs/dynablox_opencloud/main/mod.ts, or clone onto your machine and import mod.ts from the downloaded folder in your code.

Starting Guide

NOTE: If you are not using tooling, you are silly.

All Uses (Functional Programming)

In BaseClient, there is almost no usage of OOP. All APIs on Open Cloud endpoints can be accessed via <BaseClient>.services, though with little documentation.

Import BaseClient from the module entrypoint, either the module name (NodeJS) or mod.ts (Deno) Construct a new BaseClient with either API key or other credentials.

const client = new BaseClient({
    credentials: {
        type: "APIKey",
        value: "APIKEYHERE"
    }
})

Open Cloud (API Key, OOP)

In OpenCloudClient, it is mostly object-orientated, with all methods available on <OpenCloudClient> with clear documentation, though APIs on Open Cloud endpoints are still accessible via <BaseClient>.services.

Import OpenCloudClient from the module entrypoint, either the module name (NodeJS) or mod.ts (Deno) Construct a new OpenCloudClient with your APIKey.

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
})

Permission Scopes

It is also possible to define scopes to tell the client what and what it can not access. This is similar to the actual Scopes system Roblox uses for OpenCloud and oAuth.

By default, all scopes are allowed.

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    scopes: [{
        // The scope type, see TSDoc for more options.
        scopeType: "universe-datastores.objects",
        // Allows for universe ID 13058 and datastoreName.
        // a Target part can also be "*" to allow all. Some target parts may be optional.
        targetParts: ["13058", "datastoreName"],
        // Allows for reading entry values in the DataStore.
        operations: ["read"],
        // NOTE: this is optional. If set to `true`, `operations` is ignored.
        allowAllOperations: false,
    }]
})

Ratelimiting

The OpenCloudClient also has a built-in ratelimit helper. If a ratelimit is reached, it will throw an OpenCloudClientError.

You can opt in for yielding behavior by setting ratelimiterShouldYield to true:

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    ratelimiterShouldYield: true,
})

It also exposes the Ratelimiter helper publicly, so you can have mutiple OpenCloudClients using the same ratelimit helper instance:

const client1 = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
});

const client2 = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    ratelimiter: client1.ratelimiter,
});

Retries

By default, if a response is 500, 502, or 504 it will retry 5 times, each retry having a 250ms timeout. It is possible to change it:

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    // Retry 5 more times on failed requests, if retrying is not wanted, set it to `0`.
    requestRetryCount: 5,
    // Each retry will take 250ms
    requestRetryTimeout: 250
});

Examples

Publishing a Place
Deno
import { OpenCloudClient } from "https://deno.land/x/dynablox_opencloud/mod.ts";

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    scopes: [{
        // Tell the client we have access to updating place data in the universe 13058, and no other universe.
        scopeType: "universe-places",
        targetParts: ["13058"],
        operations: ["write"],
    }],
});

// The methods have "base" because it doesn't actually make any HTTP requests.
const place = client.getBaseUniverse(13058).getBasePlace(1818);

const fileData = await Deno.readFile("./place.rbxl");

// Updates the content of the place for the Saved version type.
const placeVersion = await place.updateContents(fileData, "Saved");

console.log(`Updated place to version ${placeVersion}`);
NodeJS
const { OpenCloudClient } = require("@dynabloxjs/opencloud");
const fs = require("fs/promises");

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    scopes: [{
        // Tell the client we have access to updating place data in the universe 13058, and no other universe.
        scopeType: "universe-places",
        targetParts: ["13058"],
        operations: ["write"],
    }],
});

// The methods have "base" because it doesn't actually make any HTTP requests.
const place = client.getBaseUniverse(13058).getBasePlace(1818);

(async () => {
    const fileData = await fs.readFile("./place.rbxl");
    
    // Updates the content of the place for the Saved version type.
    const placeVersion = await place.updateContents(fileData, "Saved");
    
    console.log(`Updated place to version ${placeVersion}`);
})();
Accessing DataStores
Deno
import { OpenCloudClient } from "https://deno.land/x/dynablox_opencloud/mod.ts";

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    scopes: [{
        // Tell the client we have access to reading and listing DataStore objects on universe 13058 and not any other universe.
        scopeType: "universe-datastores.objects",
        targetParts: ["13058"],
        operations: ["read", "list"],
    }],
});

// The method has "base" because it doesn't actually make any HTTP requests.
const datastore = client.getBaseUniverse(13058).getStandardDataStore("TestStore");

// `ServicePage` has an async iterator implementation, let's use it.
for await (const keys of datastore.listEntries()) {
    keys.forEach(({key}) => {
        console.log(key);
        if (key.startsWith("Player")) {
            const data = await datastore.getEntry(key);

            console.log(`${key} data length: ${JSON.stringify(data).length}`);
        }
    });
}

// Or:
// const keys = await datastore.listEntries().getCurrentPage();
// keys.data.forEach(({key}) => ...);
// Get more data:
// const moreKeys = await keys.getNextPage();
// moreKeys.data.forEach(({key}) => ...);
NodeJS
const { OpenCloudClient } = require("@dynabloxjs/opencloud");

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    scopes: [{
        // Tell the client we have access to reading and listing DataStore objects on universe 13058 and not any other universe.
        scopeType: "universe-datastores.objects",
        targetParts: ["13058"],
        operations: ["read", "list"],
    }],
});

// The method has "base" because it doesn't actually make any HTTP requests.
const datastore = client.getBaseUniverse(13058).getStandardDataStore("TestStore");

(async () => {
    // `ServicePage` has an async iterator implementation, let's use it.
    for await (const keys of datastore.listEntries()) {
        keys.forEach(({key}) => {
            console.log(key);
            if (key.startsWith("Player")) {
                const data = await datastore.getEntry(key);
                
                console.log(`${key} data length: ${JSON.stringify(data).length}`);
            }
        });
    }

    // Or:
    // const keys = await datastore.listEntries().getCurrentPage();
    // keys.data.forEach(({key}) => ...);
    // Get more data:
    // const moreKeys = await keys.getNextPage();
    // moreKeys.data.forEach(({key}) => ...);
})();
Posting a message to be read by MessagingService
Deno
import { OpenCloudClient } from "https://deno.land/x/dynablox_opencloud/mod.ts";

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    scopes: [{
        // Tell the client we have access to publishing messages on universe 13058 and not any other universe.
        scopeType: "universe-messaging-service",
        targetParts: ["13058"],
        operations: ["publish"],
    }],
});

// The method has "base" because it doesn't actually make any HTTP requests.
const universe = client.getBaseUniverse(13058);

// Post the message to the universe.
await universe.postMessage("CoolTopic", { coolData: true });
NodeJS
const { OpenCloudClient } = require("@dynabloxjs/opencloud");

const client = new OpenCloudClient({
    credentialsValue: "APIKEYHERE",
    scopes: [{
        // Tell the client we have access to publishing messages on universe 13058 and not any other universe.
        scopeType: "universe-messaging-service",
        targetParts: ["13058"],
        operations: ["publish"],
    }],
});

// The method has "base" because it doesn't actually make any HTTP requests.
const universe = client.getBaseUniverse(13058);

(async() => {
    // Post the message to the universe.
    await universe.postMessage("CoolTopic", { coolData: true });
})();

License

MIT License