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:
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 🚨️