domco

Examples

Here are a few basic examples of common integrations and workflows. Check out the domco-examples repository for the complete suite of examples including SSR framework integrations.

Multi-Page Application

Given the following directory structure, you can create a multi-page application.

src/
├── client/
│	├── bar
│   │   └── +page.html
│   └── foo
│       └── +page.html
└── server/
	└── +func.ts

Import the pages and send them in a Response based on the pathname.

// src/server/+func.ts
// transformed src/client/bar/+page.html
import { html as bar } from "client:page/bar";
// transformed src/client/foo/+page.html
import { html as foo } from "client:page/foo";
import type { Handler, Prerender } from "domco";

export const handler: Handler = (req) => {
	const { pathname } = new URL(req.url); // get the pathname from the request

	// serve the html based on the pathname
	if (pathname === "/bar") {
		return new Response(bar, { headers: { "Content-Type": "text/html" } });
	} else if (pathname === "/foo") {
		return new Response(foo, { headers: { "Content-Type": "text/html" } });
	}

	return new Response("Not found", { status: 404 });
};

// if you want to prerender the routes to static pages during build
export const prerender: Prerender = ["/bar", "/foo"];

Server Frameworks

Hono

Hono is a fast, lightweight server framework built on Web Standards with support for any JavaScript runtime.

// src/server/+func.ts
import { html } from "client:page";
import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => c.html(html));

export const handler = app.fetch;

h3

h3 is a server framework built for high performance and portability running in any JavaScript runtime.

// src/server/+func.ts
import { html } from "client:page";
import { createApp, eventHandler, toWebHandler } from "h3";

const app = createApp();

app.use(eventHandler(() => html));

export const handler = toWebHandler(app);

Elysia

Elysia is an ergonomic server framework for humans. It has end-to-end type safety, type integrity, and exceptional developer experience. Supercharged by Bun.

// src/server/+func.ts
import { html } from "client:page";
import { Elysia } from "elysia";

const app = new Elysia().get("/", () => {
	return new Response(html, {
		headers: { "Content-Type": "text/html" },
	});
});

export const handler = app.handle;
// package.json
{
	"scripts": {
		"dev": "bunx --bun vite"
	}
}

Routers

If you just want to add a router, and create your own context for each route, here’s an example.

Trouter

Trouter is a fast, small-but-mighty, familiar fish router.

// src/server/+func.ts
import { html } from "client:page";
import type { Handler } from "domco";
import { Trouter, type Methods } from "trouter";

// custom context variable
type Context = {
	req: Request;
	params: Record<string, string>;
};

// custom handler/middleware
type RouteHandler = (context: Context) => Promise<Response | void>;

const router = new Trouter<RouteHandler>();

router
	.get("/", async (_c) => {
		return new Response(html, {
			headers: {
				"Content-Type": "text/html",
			},
		});
	})
	.get("/api/:id", async ({ params }) => {
		return new Response(JSON.stringify({ id: params.id }), {
			headers: {
				"Content-Type": "application/json",
			},
		});
	});

export const handler: Handler = async (req) => {
	const { pathname } = new URL(req.url);

	const { handlers, params } = router.find(req.method as Methods, pathname);

	for (const h of handlers) {
		// create context
		const context: Context = { req, params };

		// pass into handler
		const res = await h(context);

		if (res instanceof Response) {
			return res;
		}
	}

	return new Response("Not found", { status: 404 });
};

Deployment

Node server

Here’s an example of how to serve your func using the result of your build using node:http, sirv, and domco/listener.

// server.js
// import the `handler` from the build output
import { handler } from "./dist/server/func.js";
// converts web handler to a Node compatible request listener
import { nodeListener } from "domco/listener";
import { createServer } from "node:http";
// `sirv` serves static assets
import sirv from "sirv";

const assets = sirv("dist/client", {
	etag: true,
	setHeaders: (res, pathname) => {
		// serve `dist/client/_immutable/*` with immutable headers
		if (pathname.startsWith("/_immutable/")) {
			res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
		}
	},
});

const server = createServer((req, res) =>
	// first, look for a static asset
	assets(req, res, () =>
		// fallthrough to the handler if static asset is not found
		nodeListener(handler)(req, res),
	),
);

server.listen(3000);

Run this module to start your server and navigate to http://localhost:3000/ to view your application.

node server.js
Edit this page