TypeScript SDK
A typed client for the whole API, with no dependencies. Runs anywhere fetch does.
@opencloudport/sdk covers everything in the API, with types. It has no dependencies and runs in Node 18+, Bun, Deno, Cloudflare Workers and browsers.
Not on npm yet
The SDK isn't published yet. Until it is, everything it does is also a plain HTTP call in this API reference.
Set up
import { OpenCloudPort } from '@opencloudport/sdk'
const ocp = new OpenCloudPort({
apiKey: process.env.OCP_API_KEY!, // a workspace key (ocp_…) or an app key (ocps_…)
baseUrl: 'https://opencloudport.com',
})Every call throws an OcpError when it fails, with the API's status, code and message:
import { OcpError } from '@opencloudport/sdk'
try {
await ocp.apps.create({ name: 'Bakery site' })
} catch (err) {
if (err instanceof OcpError && err.code === 'insufficient_balance') {
// ask the person to add funds
}
}An app, start to finish
const { id } = await ocp.apps.create({ name: 'Bakery site', external_id: 'customer_42' })
const app = ocp.app(id)
await app.services.add({ type: 'compute' })
await app.services.add({ type: 'database' }) // env.DB
await app.services.add({ type: 'storage', name: 'IMAGES' }) // env.IMAGES
await app.services.add({ type: 'email' }) // env.EMAIL
await app.services.waitUntilReady()
await app.deploy({ main: 'index.js', files: { 'index.js': code } })
await app.env.set('STRIPE_KEY', 'sk_live_…')Reference
ocp
| Method | What it does |
|---|---|
apps.create({ name, external_id?, metadata? }) | Make an app (or get the one with that external_id). |
apps.list({ limit?, starting_after?, status?, external_id? }) | A page of apps. |
apps.get(id), apps.update(id, { name?, metadata? }), apps.delete(id) | One app. |
app(id) | Everything about one app, below. |
balance() | The workspace's balance. |
prices() | Today's price for every meter. |
app.services
| Method | What it does |
|---|---|
add({ type, name? }) | Plug in a service. |
list(), get(serviceId), remove(serviceId) | |
waitUntilReady({ timeoutMs?, intervalMs? }) | Wait until every service is set up. Throws service_failed or timeout. |
Code and variables
| Method | What it does |
|---|---|
app.deploy({ main, files, compatibilityDate?, compatibilityFlags? }) | Upload and deploy. files maps each path to a string, bytes or a Blob. Resolves once it's live. |
app.deployments.list(), .get(id), .redeploy(id) | redeploy is a rollback. A deployment's source says where it came from. |
app.usage({ start?, end?, group_by? }) | This app's usage and cost (ocp.usage(…) for the whole workspace). |
app.env.list(), .set(name, value, { type? }), .remove(name) | Variables. type is 'secret' (default) or 'plain'. |
Databases: app.database(nameOrId)
| Method | What it does |
|---|---|
query(sql, params?, { readOnly? }) | Run SQL. One result per statement. With readOnly, SQL that would change data is refused before it runs. |
rows(sql, params?) | The last statement's rows, as objects keyed by column. |
Files: app.storage(nameOrId)
| Method | What it does |
|---|---|
put(key, body, { contentType? }) | Upload, straight to the bucket. |
get(key) | A Response (read it with .text(), .arrayBuffer()…), or null if there's no such file. |
list({ prefix?, cursor?, limit? }) | A page of files. |
delete(key) | |
signedUrl(key, { method?, expiresIn? }) | A link to hand a browser. |
credentials() | Temporary S3 credentials for this bucket, or null where they can't be issued. |
Email: app.email(nameOrId)
send({ to, subject, text?, html?, from_name?, reply_to?, end_user? }). Inside an app's own code, use connectEmail(env.EMAIL).send(…) instead: it needs no API key.
Sandboxes
| Method | What it does |
|---|---|
app.sandboxes.create({ size?, idle_timeout_seconds?, internet?, env?, label?, end_user? }) | Start one. |
app.sandboxes.list(), .get(id) | |
app.sandbox(id).exec(command, { cwd?, env?, timeout_ms? }) | Run and wait. |
.writeFiles(files) | { path: text }, or a list with base64 content for binary files. |
.readFile(path) | Text, or null. |
.list(path?), .remove(path) | |
.startProcess(name, command, { cwd?, env? }), .process(name), .stopProcess(name) | Long-running servers. |
.previewUrl(port) | The preview link, or null until previews have a domain. |
.deploy({ folder?, codeFolder?, main?, assetsConfig? }) | Make what it built live: folder is static files (dist), codeFolder + main bundled code. Nothing is read back through you. |
.copyFromBucket(bucket, { prefix?, path? }), .saveToBucket(path, bucket, { prefix? }) | Move files between the sandbox and a bucket (by name or id), binary included. |
.start(), .stop(), .delete() | Start again after stopping (empty workspace), stop to stop paying, or delete. |
App keys: app.keys
list(), create({ name, service_id?, access?, spend_limit? }) (the secret key is returned this once), setSpendLimit(keyId, dollarsOrNull) and revoke(keyId). An app key can make keys for its sub-agents too, never broader than itself.
Using an app key
A key for one app works the same way, within its app. A key for just one service can't list the app's services, so name the service by id instead:
const db = ocp.app(appId).database('svc_…') // not 'DB'
opencloudport