palinxdocs

Talk to the server

API endpoints live next to your pages, in src/api. A page fetches them like any other URL.

Add an endpoint

Create src/api/greeting.ts:

src/api/greeting.ts
import { Api, api, get } from "@palinx/core";

@api("/greeting")
export default class Greeting extends Api {
  @get("/")
  show() {
    return Response.json({ message: "Hello from the server" });
  }
}

@api("/greeting") mounts the class at /api/greeting; palinx adds the /api prefix. @get("/") makes show() answer GET requests there. A handler returns a standard Response.

With bun run dev running, the terminal counts the endpoint:

  Routes: 2 pages, 1 API

and http://localhost:2500/api/greeting returns:

{"message":"Hello from the server"}

Fetch it from the page

src/routes/index.route.ts (changes since step 5)
 // There is no index.ts any more. `px dev` starts the server, and it finds
 // this page because the file lives in src/routes/.
 import { Route, html, signal } from "@palinx/core";
 
 export default class Home extends Route {
   private count = signal(0);
+  private message = signal("Loading...");
 
+  // Runs in the browser once the page is on screen.
+  async onMount() {
+    const res = await fetch("/api/greeting");
+    const body = await res.json();
+    this.message.set(body.message);
+  }
+
   render() {
     return html`
       <h1>hello</h1>
+      <p>The server says: ${this.message()}</p>
       <p>Clicked ${this.count()} times</p>
       <button @click=${() => this.count.set(this.count() + 1)}>Click me</button>
     `;
   }
 }

onMount() runs in the browser once the page is on screen, so it's the place to fetch your own API. It can be async. The result goes into a signal, and the page updates when it arrives.

Help your editor with the decorators

@api and @get are legacy (experimental) TypeScript decorators. Palinx compiles them itself, so the app runs without any config, but your editor will flag them until you add a tsconfig.json:

tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "skipLibCheck": true,
    "experimentalDecorators": true
  },
  "include": ["src"]
}

experimentalDecorators is the setting that matters; the rest matches what the minimal template ships.

Run it

http://localhost:2500 shows The server says: Loading... briefly, then:

The server says: Hello from the server

The counter still works.

How it works

The page is first rendered on the server with the signal's starting value. In the browser, onMount() requests /api/greeting, and the endpoint answers with JSON. An endpoint is just a class whose decorated methods answer requests; API endpoints covers other methods, parameters, request bodies and errors.

Next: Share logic with a service.