Supabase Edge Runtime: Self-hosted Deno Features
At present we’re open-sourcing Supabase Edge Runtime for self-hosting Deno Edge Features.
Edge Runtime is MIT licensed, written in Rust, and based mostly on the most recent Deno Runtime (1.32+). If you happen to’ve been utilizing the Supabase CLI to serve features you then’re already one in all our Beta testers (thanks!).
Host your Edge Features wherever
We launched Supabase Edge Features a bit greater than a 12 months in the past. We use Deno Deploy to host your edge features globally throughout 30+ information facilities, so your customers get tremendous quick responses. This setup works nice for us! We didn’t have a straightforward answer for self-hosting Edge Features. We’re releasing Edge Runtime to deal with this.
Certainly one of our core ideas is “Everything is portable”, that means it’s best to be capable to take any a part of the Supabase stack and host it your self.
Supabase Edge Runtime is an internet server written in Rust that makes use of a customized Deno runtime. It will possibly serve TypeScript, JavaScript, and WASM features. All of your current Edge Features run on Edge Runtime with out altering a single line of code.
Higher native growth expertise
Self-hosting isn’t the one advantage of Edge Runtime. It is going to enhance the native growth expertise of Edge Features.
Serve all features
Supabase CLI can now serve all native Edge Features by working supabase features serve
. Beforehand, you might solely serve a single Edge Operate at a time. This was not an amazing expertise for native growth. A few of you even devised clever hacks to get round this limitation.
While you run supabase features serve
, the CLI makes use of Edge Runtime to serve all features. It helps JWT verification, import maps, and passing customized setting variables. It hot-reloads native modifications, supplying you with a seamless growth expertise.
Dev/Prod parity
Edge Runtime improves Dev/Prod parity for Edge Features. You will have encountered points the place an Edge Operate works regionally however fails when deployed. The principle trigger for that is Deno Deploy Runtime is extra restrictive and only supports a subset of Deno APIs. Edge Runtime exposes the identical APIs out there within the Deno Deploy Runtime. This may assist you to spot points quicker whereas creating and keep away from surprises when deploying.
Implementing reminiscence/period limits
One other neat characteristic we constructed into Edge Runtime is the choice to implement limits on reminiscence and wall-clock durations. At present, we’re setting them to wise defaults (reminiscence set to 150 MB and execution period set to 60s). This may help you simulate your features’ useful resource utilization and deal with the conduct in the event that they run into the bounds. Quickly we are going to enable configuring these limits through CLI config to be able to match them with the actual limits of the deployment platform.
The right way to self-host Edge Features
We now have put collectively a demo on self-host edge features on Fly.io (it’s also possible to use different suppliers like Digital Ocean or AWS).
To attempt it your self:
-
Clone the demo repository to your machine
-
Copy your Edge Operate into the
./features
listing within the demo repo. -
Replace the Dockerfile to tug the most recent edge-runtime picture (test releases)
-
Optionally edit
./features/predominant/index.ts
, including another request preprocessing logic (for instance, you may allow JWT validation, deal with CORS requests) -
Run
fly launch
to create a brand new app to serve your Edge Features -
Entry your Edge Operate by visiting:
https://{your-app-name}.fly.dev/{your-function-name}
View the logs for the Edge Runtime by visiting Fly.io’s Dashboard > Your App > Metrics. You’ll be able to serve Edge Runtime from a number of areas by working fly areas add [REGION]
.
Standing on the shoulders of Deno
You might marvel why we can’t use Deno Runtime to self-host features. Isn’t it open-source and out there as a Docker container?
Deno Runtime, by default, consists of a big selection of built-in APIs, making it straightforward to make use of for a number of use circumstances out of the field. Nevertheless, this makes it tough to make use of for serving internet requests. You want the runtime embedded inside an internet server that may boot quick and, for safety, has a extra restricted API.
Nevertheless, Deno’s structure makes it straightforward to increase its core capabilities and create a personalized runtime to match our wants. Deno supplies a Rust crate known as deno_core
, which abstracts the interactions with V8 JavaScript engine. Utilizing deno_core
we are able to create a JS context (often called a V8 Isolate). A V8 isolate has minimal overhead as well up and a single course of can host a number of V8 isolates. While you load an internet web page that accommodates scripts from a number of domains in a browser, every of them runs in a separate v8 isolate.
Edge Runtime implements an HTTP server (utilizing hyper) that listens to incoming requests. When Edge Runtime is booted, it spins up a JS context (V8 isolate), which we name the Important Employee
. Important Employee runs in a separate thread, executing the supplied predominant module. When a brand new HTTP request is obtained, the Rust runtime will ahead it to the Important Employee.
You’ll be able to write a predominant module to deal with all incoming requests. This may appear like a typical Deno Edge Operate. The principle distinction is that it has entry to a worldwide object known as “EdgeRuntime
”.
EdgeRuntime
international supplies strategies to create and entry UserWorkers
. Important Employee
can optionally delegate a request to a UserWorker to deal with and reply.
Person Staff are separate JS contexts (V8 isolates) that may run a given Edge Operate. They’ve a restricted API (for instance, they don’t get entry to the host machine’s setting variables). You may also management the reminiscence and period a Person Employee can run.
Right here’s a easy implementation of a Important Employee that receives a request, then creates a Person Employee and passes the dealing with of request to the employee.
serve(async (req: Request) => {
const memoryLimitMb = 150
const workerTimeoutMs = 1 * 60 * 1000
const noModuleCache = false
const importMapPath = null
const envVars = [
['USER', 'foo'],
['PASSWORD', 'BAR'],
]
attempt {
const employee = await EdgeRuntime.userWorkers.create({
servicePath,
memoryLimitMb,
workerTimeoutMs,
noModuleCache,
importMapPath,
envVars,
})
return await employee.fetch(req)
} catch (e) {
const error = { msg: e.toString() }
return new Response(JSON.stringify(error), {
standing: 500,
headers: { 'Content material-Sort': 'utility/json' },
})
}
})
What’s Subsequent?
Open-sourcing Edge Runtime is step one of an thrilling roadmap we’ve deliberate for Edge Features. Within the coming months, you will note tighter integrations with the remainder of the Supabase ecosystem. Listed below are some sneak peeks at what’s to return subsequent.
API Gateway to different Supabase companies
We plan to make use of Edge Runtime as a alternative for Kong, appearing as an API gateway to different Supabase companies. This is not going to solely simplify the self-hosting setup but in addition provide the choice to do Request pre/post-processing utilizing JavaScript.
Right here’s a easy instance of re-routing a request to a special endpoint utilizing Edge Runtime.
serve(async (req) => {
attempt {
if (req.url.endsWith('/relaxation/v1/old_table')) {
return await fetch('http://relaxation:3000/relaxation/v1/new_table', {
headers: req.headers,
methodology: req.methodology,
physique: req.physique,
})
}
} catch (e) {
const error = { msg: e.toString() }
return new Response(JSON.stringify(error), {
standing: 500,
headers: { 'Content material-Sort': 'utility/json' },
})
}
})
Scheduled Features
Since Edge Runtime’s Important Employee runs within the background so long as the server is working, we are able to put it to use to run periodic duties.
For instance, right here’s a naive implementation of how it may be used to set off a operate each 2 minutes. In manufacturing, it’s essential account for server restarts and timer resetting.
const interval = 2 * 60 * 1000 // 2 minutes
attempt {
const employee = await EdgeRuntime.userWorkers.create({
servicePath,
memoryLimitMb,
workerTimeoutMs,
noModuleCache,
importMapPath,
envVars,
})
const req = new Request('http://localhost/scheduled-job')
setInterval(() => employee.fetch(req), interval)
} catch (e) {
console.error(e)
}
Customized World Objects
One other thrilling factor about delivery a customized JavaScript runtime is that we are able to management the out there international objects within the runtime. In earlier examples, chances are you’ll seen we used EdgeRuntime
with out importing a particular module to our operate, this was doable as a result of we uncovered it as a worldwide object within the runtime.
We will introduce a Supabase
international object that may present platform particular options. For instance, just like Deno.writeTextFile
, we are able to expose a Supabase.writeTextFile
which may instantly write a file to Supabase Storage.
We ???? Contributions
We’re excited to construct Edge Runtime in public and contain the Supabase group within the course of. As an preliminary beta launch, there are nonetheless bugs and efficiency quirks to be ironed out. Don’t draw back from making an attempt it although.
You’ll be able to report any points you encounter in repo’s GitHub issues. In case you have concepts on make edge-runtime higher, attain out through Twitter or Discord.