Runable v1.0.0-alpha.20
Why Runable? Docs Modules Work with AI Blog About Changelog
  • 01Why Runable?
  • 02Docs
  • 03Modules
  • 04Work with AI
  • 05Blog
  • 06About
  • 07Changelog
Vue without a fixed server runtime.
  1. Blog
  2. One Vue Application, Many Server Runtimes

One Vue Application, Many Server Runtimes

Understand what Runable adapters do and why the backend framework stays a dependency of the consuming project.

Rendering Vue is only one part of integrating a frontend into an existing server. Each backend framework has its own request objects, response lifecycle, middleware order, and error conventions.

Runable adapters translate those differences into one rendering pipeline.

The adapter owns the integration boundary

Without an adapter, a Node server must create the Runable application and forward every frontend request manually:

import { createServer } from "node:http";
import { createRunableApp, requestNode } from "runable";

const runableApp = createRunableApp();

createServer(async (request, response) => {
  await requestNode({
    runableApp: await runableApp,
    req: request,
    res: response,
  });
}).listen(3000);

That code is small, but a production integration also needs to account for development assets, errors, and responses that another middleware has already completed.

An official adapter packages those decisions behind the API expected by the backend.

For Express, the result is a request handler:

import Express from "express";
import { express } from "runable/adapters/express";

const app = Express();

app.get("/api/health", (_request, response) => {
  response.json({ status: "ok" });
});

app.use(express());

For Hono, the result is Hono middleware:

import { Hono } from "hono";
import { hono } from "runable/adapters/hono";

const app = new Hono();

app.get("/api/health", (context) => {
  return context.json({ status: "ok" });
});

app.use("*", hono());

The public shape follows the host framework instead of forcing every project through a Runable-specific server abstraction.

Initialize once, render many times

An adapter creates the Runable application once and reuses the resulting promise across requests. That avoids rebuilding the Vite server or production renderer for each visit.

During development, Node adapters first let Vite process the request. If Vite serves a module, stylesheet, or another development asset, the response is already complete and rendering stops. Other requests continue to the Vue renderer.

This response check matters. Writing headers after another middleware has sent the response causes errors such as ERR_HTTP_HEADERS_SENT. The adapter protects the boundary by checking whether the response has ended before rendering.

Let backend routes run first

Runable should not intercept application APIs. Register the adapter after the routes that belong to the backend.

app.use(authentication());
app.use("/api", apiRouter);
app.use(express());

This order makes ownership clear:

  1. shared backend middleware runs;
  2. API and server routes get the first chance to respond;
  3. Runable renders the remaining frontend request.

Hono uses its own middleware flow, so its adapter waits for downstream handlers and renders only when the result is still a 404.

Install only the framework you use

runable exposes integrations for several runtimes, but it should not install all of those frameworks in every consuming application.

If the project uses Express, install Express. If it uses Fastify, install Fastify. Framework packages required to type and develop adapters belong in Runable's development dependencies, while the consuming project owns its chosen runtime.

That keeps the production dependency graph aligned with the actual server.

Drop down to the primitives when needed

Dedicated adapters cover common frameworks. A custom server can use one of two lower-level functions:

  • requestNode() writes to Node's ServerResponse;
  • requestWeb() accepts a Fetch API Request and returns a Response.

Use those primitives when a runtime has no adapter or when an internal server needs a specialized lifecycle. Otherwise, prefer the official adapter: it centralizes integration details that are easy to miss and difficult to diagnose later.

Browse the available servers in Integrations.

On this page

  • 1The adapter owns the integration boundary
  • 2Initialize once, render many times
  • 3Let backend routes run first
  • 4Install only the framework you use
  • 5Drop down to the primitives when needed
Runable

The Vue framework that brings productive conventions to any backend.

Product

  • Why Runable
  • Documentation
  • Installation
  • Integrations

Project

  • About
  • Blog
  • Changelog
  • Sponsor

Community

  • GitHub
  • Issues
  • Discussions
  • Bluesky

© 2026 Runable. Released under the MIT License.

Open source Built with Vue and Runable