Introducing Edge Functions on the Nulvless Network

What edge functions are, how they run your TypeScript in the region closest to each user, and when to use them instead of single-region serverless.

Gleisson MatosFounder & CEO5 min read
On this page

An edge function is a small piece of server-side code that runs in many regions at once, as close as possible to the person calling it. You deploy it once, and every request is answered by the nearest copy instead of travelling to a single server on the other side of the world.

This post explains what edge functions are, how they run on the Nulvless Network, how they compare with traditional serverless functions, and what they are good for.

Edge functions, in one paragraph

A traditional serverless function lives in one region. If your users are in São Paulo, Lisbon and Tokyo and the function runs in Virginia, two of those three users pay for an ocean crossing on every call. An edge function is deployed to every region of a global network and runs where the request arrives. It is still serverless: no machines to manage, no capacity to plan, billing by invocation. What changes is where the code runs.

Single-region server

The request crosses to one region, and the session check may cross again

UserRegionAuth

Edge function

The request stops at the closest edge, where the session is checked too

UserEdgeAuth
The same request, served from one region and from the edge.

The difference compounds when the function needs to know who is calling. In a single-region setup, checking a session often means one more trip, to wherever the identity service lives. On the Nulvless Network the identity service runs on the same edge as your function, so that check stays local too.

The Nulvless Network

The Nulvless Network is the global edge layer under every Nulvless project. Each request lands on the region closest to the user, the session can be verified right there, and your edge functions run in the same place. It is the same network that serves sign-in, sessions and tokens, which is why an authenticated request does not have to cross an ocean to find out who sent it.

Every function gets a stable URL built from your project:

https://<project>.nulvless.co/functions/<slug>

The URL is the same everywhere. The network decides which region answers it.

What happens on a request

Your userSão Paulo

Nulvless Network, nearest edge

  1. 1Route

    A call to /functions/report lands on the closest region

  2. 2Run

    An isolate starts your function in milliseconds, with its secrets

  3. 3Verify

    auth.verify checks the session without leaving the network

4The response leaves from the same edge it arrived at

One request to an edge function on the Nulvless Network.
  1. Route. The request for /functions/<slug> arrives at the closest edge of the Nulvless Network.
  2. Run. Your function starts in a V8 isolate, not a container. Isolates start in milliseconds, so cold starts stay short, and each one is sandboxed from every other function.
  3. Verify. If the function needs to know who is calling, it asks with auth.verify, and the answer comes from the same network.
  4. Respond. The response goes back to the user from the edge it arrived at.

Writing an edge function

Nulvless Edge Functions run TypeScript on a Deno-compatible runtime with npm support. The smallest useful function is a default export with a fetch handler that takes a standard Request and returns a standard Response:

index.ts
export default {
  async fetch(req: Request) {
    const url = new URL(req.url);
    const name = url.searchParams.get('name') ?? 'world';

    return Response.json({ message: `Hello, ${name}` });
  },
};

There is nothing Nulvless-specific in that code. It uses the Web APIs you already know: Request, Response, URL and fetch. Deno.serve works too, and so does a framework like Express or Fastify if you prefer one.

npm packages

Import packages from npm with the npm: specifier. There is no package.json and no build step to configure:

import { z } from 'npm:[email protected]';

const Body = z.object({ email: z.string().email() });

Pin a version, as above, so a new release of a dependency never changes your function without a deploy.

Secrets and environment variables

API keys and other configuration never go in the source. Add them in the Secrets tab of the function in the console, and read them at runtime:

const stripeKey = Deno.env.get('STRIPE_SECRET_KEY');

Secrets are injected when the function runs and versioned per key, so rotating one does not require touching the code.

Every function also receives a few values without you adding them: NULVLESS_URL, the URL of your project, plus NULVLESS_FUNCTION_SLUG and NULVLESS_FUNCTION_VERSION. Names that start with NULVLESS_ or NVL_ are reserved for these, so pick another prefix for your own secrets.

From code to every edge

  1. 1Write

    TypeScript in the console editor or your own repository

  2. 2Build

    npm imports resolved and bundled, no build step to configure

  3. 3Deploy

    The new version goes out to every edge

  4. 4Observe

    Logs, errors and latency per version, with one-click rollback

From code to every edge.

You can write a function in the editor inside the console for a quick change, or keep it in your own repository. Each deploy builds the function, resolves its npm imports and ships the new version to every edge. Every deploy is kept as a version, so a bad release is one click from being rolled back, and the function's logs, errors and latency are recorded per version.

When to use an edge function

Edge functions fit work that is short, triggered by a request, and close to your users or to their session:

  • Authenticated API endpoints that check who is calling and what they may do, then answer. We cover this one step by step in Authenticate users in edge functions.
  • Webhooks from Stripe, GitHub or any provider that needs a fast 200.
  • AI features that call OpenAI or another model on behalf of a signed-in user, without exposing your API key to the browser.
  • Transactional messages, like sending a welcome email through Nulvless Communications after a sign-up.
  • Glue between services, like syncing a new customer to your CRM.

They are a poor fit for long-running jobs, heavy batch processing or anything that needs to hold a connection to a single database far from your users for a long time. Keep those in a backend or a queue, and let an edge function hand work to them.

Frequently asked questions

What is the difference between edge functions and serverless functions?

Both are serverless: you deploy code, not servers. A typical serverless function runs in one region you choose. An edge function runs in every region of a global network and is executed by the one closest to the request.

Do edge functions have cold starts?

Yes, but they are short. Nulvless Edge Functions run in V8 isolates rather than containers, so a new instance starts in milliseconds instead of seconds.

What language are Nulvless Edge Functions written in?

TypeScript or JavaScript, on a Deno-compatible runtime with Web APIs and npm packages through the npm: specifier.

How do I keep API keys out of my code?

Store them as secrets on the function in the console and read them with Deno.env.get. They are injected at runtime and never stored in the source.

Can an edge function know which user is calling?

Yes. Send the user's access token in the Authorization header and check it with auth.verify from @nulvless/nulvless-js. The next post shows the whole flow, including roles, scopes and sudo mode.

Keep reading