Introducing More Flexible Domain Association for Deno Subhosting
Subhosting makes it easy to securely run untrusted JavaScript from multiple customers in a secure, hosted sandbox. There are many use cases for Subhosting, such as offering their users edge functions, hosting ecommerce storefronts close to users, and more - all without worrying about security and maintaining production infrastructure.
When hosting your users’ code, you sometimes want their deployments to be
publicly accessible via a neat custom domain like user1.yourcompany.com
.
Now, managing custom domains across your users’ deployments is simpler with
our new, more flexible domain associations. This allows you to
programmatically manage domain mappings and attach custom domains to deployments
via the Subhosting API.
In this post, we’ll cover:
- Organization-wide wildcard subdomains
- Variables for simpler domain management
- How to attach and detach custom domains
- What’s next
Before we dive into how to use the API, let’s review what’s now supported with this feature.
Organization-wide wildcard subdomains
You can now assign different subdomains under the same wildcard domain to
different deployments. For example, given the *.example.com
wildcard domain,
you can now assign foo.example.com
to one deployment and bar.example.com
to
another. This flexibility allows for more sophisticated deployment strategies
and easier resource management.
Variables for simpler domain management
To make programmatically managing and referencing your users’ deployments simpler, two variables are now exposed when you specify a domain name to associate with a deployment:
{deployment.id}
: The unique identifier of the deployment.{project.name}
: The name of the project to which the deployment belongs.
These can be combined with arbitrary strings as long as they are valid domains and covered by the wildcard domain you registered after substitution. For example, you can specify a domain name like:
{deployment.id}.example.com
{project.name}.example.com
{project.name}-{deployment.id}.example.com
foo-{deployment.id}.example.com
foo-{deployment.id}-{project.name}.example.com
These variables can also be used in combination with the deno.dev
domain, but
in this case only the following two formats are allowed:
{project.name}-{deployment.id}.deno.dev
{project.name}.deno.dev
These enhancements provide better customization and automation capabilities, making it a lot easier to manage and reference deployment programmatically.
Let’s look at how these features are put to practice with an example.
How to attach and detach custom domains
Before you can attach custom domains to deployments, your custom domain first
needs to be registered using the
POST /organizations/{organizationId}/domains
endpoint:
import { assert } from "jsr:@std/assert/assert";
const orgId = "your-organization-id";
const orgToken = "your-organization-token";
const res = await fetch(
`https://api.deno.com/v1/organizations/${orgId}/domains`,
{
method: "POST",
body: JSON.stringify({
domain: "*.example.com",
}),
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${orgToken}`,
},
},
);
assert(res.ok);
The response body of this call contains dnsRecords
field, which is a list of
DNS records that you can use to setup a name server.
The next step is to issue TLS certificates for the domain. While you can do so
by
uploading certificates,
you can also provision TLS certificates via
POST /domains/{domainId}/certificates/provision
:
import { assert } from "jsr:@std/assert/assert";
const orgToken = "your-organization-token";
// Domain ID you got from the previous step
const domainId = "your-domain-id";
const res = await fetch(
`https://api.deno.com/v1/domains/${domainId}/certificates/provision`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${orgToken}`,
},
},
);
assert(res.ok);
Now you’re all set — let’s create a new deployment with
POST /projects/{projectId}/deployments
:
import { assert } from "jsr:@std/assert/assert";
const projectId = "your-project-id";
const orgToken = "your-organization-token";
const res = await fetch(
`https://api.deno.com/v1/projects/${projectId}/deployments`,
{
method: "POST",
body: JSON.stringify({
entryPointUrl: "main.ts",
assets: {
"main.ts": {
kind: "file",
content: 'Deno.serve(() => new Response("hello"));',
},
},
envVars: {},
domains: [
"foo.example.com",
"{deployment.id}.example.com",
"{project.name}-{deployment.id}.deno.dev",
],
}),
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${orgToken}`,
},
},
);
assert(res.ok);
Note the new domains
field in the JSON
payload, which is an array of the
custom domains that will be attached to the deployment once it’s successfully
created. Also, here is where you can use
named variables in the domain, which
will become actual values.
Let’s say that the newly created deployment ID is chonky-dog-57
under the
project my-project
. In our example, the following domains will all route to
this newly created deployment:
https://foo.example.com
https://chonky-dog-57.example.com
https://my-project-chonky-dog-57.deno.dev
What if we want to attach a custom domain to an existing deployment? We can use
PUT /deployments/{deploymentId}/domains/{domain}
:
import { assert } from "jsr:@std/assert/assert";
const deploymentId = "chonky-dog-57";
const orgToken = "your-organization-token";
const extraDomain = "prefix-{project.name}.example.com";
const res = await fetch(`/deployments/${deploymentId}/domains/${extraDomain}`, {
method: "PUT",
headers: {
"Authorization": `Bearer ${orgToken}`,
},
});
assert(res.ok);
Now, anyone visiting https://prefix-my-project.example.com
will be directed to
the existing chonky-dog-57
deployment.
Note that anytime a domain is attached to a deployment, it’s automatically detached from any previous deployment.
We can also manually detach a domain from a deployment with
DELETE /deployments/{deploymentId}/domains/{domain}
:
import { assert } from "jsr:@std/assert/assert";
const deploymentId = "chonky-dog-57";
const orgToken = "your-organization-token";
const res = await fetch(
`/deployments/${deploymentId}/domains/foo.example.com`,
{
method: "DELETE",
headers: {
"Authorization": `Bearer ${orgToken}`,
},
},
);
assert(res.ok);
Now, the domain https://foo.example.com
will no longer direct to the
chonky-dog-57
deployment.
What’s next
Subhosting continues to be a great choice for organizations interested in running their users’ code without worrying about security or maintaining production infrastructure.
We’ll continue to invest resources in making Subhosting the easiest way to run third party untrusted code securely, so you can focus on delivering value to your end users.
🚨️ Want to run users’ untrusted code securely without building it from scratch?
Check out Deno Subhosting, where you can run JavaScript from multiple users in a secure, hosted sandbox in minutes.