Library of @fartlabs/jsonx components for
composing @fartlabs/rt routers in JSX.
Presented by @johncmanuel.
Generated API documentation is available at https://jsr.io/@fartlabs/rtx.
Let's learn how to get started with rtx by creating a simple router in Deno.
1. Install Deno.
2. Start a new Deno project.
deno init3. Add @fartlabs/rtx as a project dependency.
deno add jsr:@fartlabs/rtx4. Add the following values to your deno.json(c) file.
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@fartlabs/rtx"
}
}5. Add a file ending in .[j|t]sx to your project. For example, main.tsx.
import { Get, Router } from "@fartlabs/rtx";
const router = (
<Router default={() => new Response("Not found", { status: 404 })}>
<Get
pattern="/"
handler={() =>
new Response("Hello, World!")}
/>
</Router>
);
Deno.serve((request) => router.fetch(request));6. Spin up your HTTP server by running the .[j|t]sx file.
deno run --allow-net main.tsximport { Delete, Get, Post, Put, Router } from "@fartlabs/rtx";
const router = (
<Router default={() => new Response("Not found", { status: 404 })}>
<Get
pattern="/"
handler={() =>
new Response("Home")}
/>
<Post
pattern="/items"
handler={async (ctx) => {
const body = await ctx.request.json();
return Response.json({ created: body }, { status: 201 });
}}
/>
<Put
pattern="/items/:id"
handler={async (ctx) => {
const body = await ctx.request.json();
const id = ctx.params?.pathname.groups.id;
return Response.json({ updatedId: id, data: body });
}}
/>
<Delete
pattern="/items/:id"
handler={(ctx) => {
return new Response(null, { status: 204 });
}}
/>
</Router>
);import { Get, Router } from "@fartlabs/rtx";
const router = (
<Router default={() => new Response("Not found", { status: 404 })}>
<Get
pattern="/users/:id"
handler={(ctx) => {
const id = ctx.params?.pathname.groups.id;
const url = new URL(ctx.request.url);
const page = Number(url.searchParams.get("page") ?? 1);
return Response.json({ id, page });
}}
/>
</Router>
);You can wrap handlers to implement common behavior (auth, logging, headers).
import { Get, Router } from "@fartlabs/rtx";
import type { RequestHandler } from "@fartlabs/rt";
const withJson =
(handler: RequestHandler<unknown>): RequestHandler<unknown> =>
async (
ctx,
) => {
try {
const res = await handler(ctx);
const headers = new Headers(res.headers);
if (!headers.has("content-type")) {
headers.set("content-type", "application/json; charset=utf-8");
}
return new Response(res.body, { status: res.status, headers });
} catch (error) {
return Response.json({ error: String(error) }, { status: 500 });
}
};
const router = (
<Router default={() => new Response("Not found", { status: 404 })}>
<Get
pattern="/health"
handler={withJson(() =>
Response.json({ ok: true })
)}
/>
</Router>
);You can define multiple routes in the same Router component:
import { Get, Router } from "@fartlabs/rtx";
const router = (
<Router default={() => new Response("Not found", { status: 404 })}>
<Get
pattern="/users"
handler={() =>
Response.json([{ id: "1" }])}
/>
<Get
pattern="/users/:id"
handler={(ctx) => Response.json({ id: ctx.params?.pathname.groups.id })}
/>
</Router>
);import { Get, Router } from "@fartlabs/rtx";
const router = (
<Router default={() => new Response("Not found", { status: 404 })}>
<Get
pattern="/boom"
handler={() => {
throw new Error("Something went wrong");
}}
/>
</Router>
);
// Wrap fetch to centralize error responses
const safeFetch = async (request: Request) => {
try {
return await router.fetch(request);
} catch (error) {
return Response.json({ error: String(error) }, { status: 500 });
}
};
Deno.serve((request) => safeFetch(request));Run deno fmt to format the code.
Run deno lint to lint the code.
@fartlabs/rt: Minimal runtime router powering the JSX components here@fartlabs/jsonx: Minimal JSX runtime used byrtx
This repo uses Deno tests.
deno test -A- Replace imperative route registrations with JSX elements like
Get,Post, etc. - Create a top-level
Routerand pass adefaulthandler for unmatched routes. - Handlers receive a single context object with
request,params(URLPattern match results), and other properties. Route parameters are accessed viactx.params.pathname.groups.paramName. - Compose cross-cutting concerns by higher-order functions that wrap route
handler.
See LICENSE for details.
Developed with ❤️ @FartLabs
