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

Introducing KV Backup for Deno Subhosting

Subhosting allows you to programmatically run untrusted JavaScript from multiple users in a secure sandbox, without the hassle of managing complex infrastructure. Since its self-service launch, many customers use Subhosting to host e-commerce storefronts close to users, provide code-level customization escape hatches for low-code workflow builders, and even simply reselling serverless edge functions.

Today, we are excited to announce that KV backup is now available for Subhosting. This feature gives you and your users improved data durability, as you can now constantly back up your KV databases to your own S3-compatible object storage, use point-in-time recovery, and more.

Let’s dive into how you can use this feature.

Simple, persistent data storage for your users

Your users may want to add data persistence to their code. While they can import any number of npm packages to connect to data storage, the simplest approach is using the built-in KV API:

const kv = await Deno.openKv();
Your users can access a globally replicated ACID database in a single line of code without any configuration.

This lets your users skip provisioning a new database instance and juggling API keys and dive right into writing code. Note, however, to enable KV for your users, you’ll need to programmatically create a KV database then attach it to a new deployment.

Learn more about building with Deno KV.

Set up a KV database

Before we show you how to perform backup operations to your KV database, let’s first create one in your organization using POST /organizations/{organizationId}/databases:

import { assert } from "jsr:@std/assert/assert";

const orgId = "your-organization-id";
const orgToken = "your-organization-token";

// Create a new database in your organization
const res = await fetch(
  `https://api.deno.com/v1/organizations/${orgId}/databases`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${orgToken}`,
    },
    body: JSON.stringify({
      description: "my database",
    }),
  },
);

assert(res.ok);

// You can get database ID from response body
const { id: databaseId } = await res.json();
console.log(databaseId);

Enable a KV backup

Once you have a KV database, you can enable a backup using POST /databases/{databaseId}/database_backups. The following example shows how to enable a backup to an S3-compatible object storage, in this case, Google Cloud Storage:

import { assert } from "jsr:@std/assert/assert";

const ACCESS_KEY_ID = Deno.env.get("ACCESS_KEY_ID")!;
const SECRET_ACCESS_KEY = Deno.env.get("SECRET_ACCESS_KEY")!;

const orgToken = "your-organization-token";
// Database ID you got from the previous step
const databaseId = "database-id";

const res = await fetch(
  `https://api.deno.com/v1/databases/${databaseId}/database_backups`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${orgToken}`,
    },
    body: JSON.stringify({
      kind: "s3",
      endpoint: "https://storage.googleapis.com",
      bucketName: "test-kv-backup",
      bucketRegion: "us-central1",
      accessKeyId: ACCESS_KEY_ID,
      secretAccessKey: SECRET_ACCESS_KEY,
      prefix: "backup/",
    }),
  },
);

assert(res.ok);

// You can get database backup ID from response body
const { id: databaseBackupId } = await res.json();
console.log(databaseBackupId);

You can then get the status of the backup along with other information using GET /database_backups/{databaseBackupId}:

import { assert } from "jsr:@std/assert/assert";

const orgToken = "your-organization-token";
// Database backup ID you got from the previous step
const databaseBackupId = "database-backup-id";

const res = await fetch(
  `https://api.deno.com/v1/database_backups/${databaseBackupId}`,
  {
    headers: {
      authorization: `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

console.log(await res.json());
// You would see something like:
// {
//   id: "database-id",
//   kind: "s3",
//   endpoint: "https://storage.googleapis.com",
//   bucketName: "test-kv-backup",
//   bucketRegion: "us-central1",
//   accessKeyId: "YOUR_ACCESS_KEY_ID",
//   prefix: "backup/",
//   status: {
//     code: "active"
//   }
// }

What if you forget the database backup ID? Don’t worry, you can call the GET /databases/{databaseId}/database_backups endpoint to list all backups for a database:

import { assert } from "jsr:@std/assert/assert";

const orgToken = "your-organization-token";
const databaseId = "database-id";

const res = await fetch(
  `https://api.deno.com/v1/databases/${databaseId}/database_backups`,
  {
    headers: {
      authorization: `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

console.log(await res.json());
// You would see something like:
// [
//   {
//     id: "database-id",
//     kind: "s3",
//     endpoint: "https://storage.googleapis.com",
//     bucketName: "test-kv-backup",
//     bucketRegion: "us-central1",
//     accessKeyId: "YOUR_ACCESS_KEY_ID",
//     prefix: "backup/",
//     status: {
//       code: "active"
//     }
//   }
// ]

Note that currently only one backup can be enabled for a single database. That means if you want to update your backup with different configuration settings, back up to a different destination, or to fix misconfigured credentials, you will first need to disable the existing backup. You can do that with DELETE /database_backups/{databaseBackupId}:

import { assert } from "jsr:@std/assert/assert";

const orgToken = "your-organization-token";
const databaseBackupId = "database-backup-id";

const res = await fetch(
  `https://api.deno.com/v1/database_backups/${databaseBackupId}`,
  {
    method: "DELETE",
    headers: {
      authorization: `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

Afterwards, you can enable a new backup with the new settings.

Advanced usage with backup data

You can manage your backup data with the denokv tool. For example, you can sync the data to a local SQLite file and view or checkout a recoverable point. For more details, please refer to the denokv documentation.

What’s next

Building a platform to deploy and run untrusted code securely is made simple using Deno Subhosting. You can launch your integrations platform, low-code solution, app marketplace, and more in weeks and not months, and with only a fraction of the cost.

We plan to continue to invest in ensuring Deno Subhosting is the easiest way to run third party untrusted code securely, so you can continue to focus on building value for your users.

🚨️ Read more about Deno Subhosting 🚨️