Skip to main content

Serving a Frontend with the API

Temba is a single Node process that can serve both your REST API and a frontend app's static files. This means you can build your frontend, point Temba at the output folder, and deploy the whole thing as one service — no separate static hosting needed.

How it works

Configure staticFolder with the path to your frontend's build output:

import { create } from 'temba'

const server = await create({
staticFolder: 'build',
})

server.start()

Temba will:

  • Serve your frontend from / (e.g. build/index.html for GET /)
  • Automatically set apiPrefix to 'api', so all API routes live under /api

The resulting URL structure:

RequestResult
GET /Serves build/index.html
GET /about.htmlServes build/about.html
GET /api/moviesAPI — returns all movies
POST /api/moviesAPI — creates a movie

Your frontend calls the API at /api/<resource>, and Temba handles both from the same port.

Building the frontend

This recipe is framework-agnostic. Any tool that produces a static build folder works — React, Vue, Svelte, plain HTML, etc. For example, with Vite the default output folder is dist:

const server = await create({
staticFolder: 'dist',
})

Make sure to build the frontend before starting Temba:

npm run build   # builds frontend into dist/ or build/
node server.js # starts Temba

Deploying to a cloud provider

Because Temba serves everything from a single Node process on a single port, deploying is straightforward — just deploy the Node app and the built frontend together.

What to include in your deployment:

  • Your server.js (or equivalent entry point)
  • The frontend build folder (build/ or dist/)
  • package.json and node_modules (or let the platform install dependencies)

Example: deploying to Railway, Render, or Fly.io

These platforms all work the same way:

  1. Push your project to a Git repository
  2. Connect the repository to the platform
  3. Set the start command to node server.js (or your entry point)
  4. Deploy — the platform installs dependencies, and your app is live

Make sure your frontend build step runs before the deploy, or configure a build command on the platform (e.g. npm run build && node server.js).

Port binding:

Cloud platforms typically assign a port via the PORT environment variable. Pass it through to Temba:

const server = await create({
staticFolder: 'build',
port: process.env.PORT || 8362,
})

server.start()

That's all it takes — one repo, one deployment, one running process serving both your frontend and your API.

Taking it further with WebSockets

Once your frontend and API are running together, you can enable WebSockets to push live data updates to your app:

const server = await create({
staticFolder: 'build',
port: process.env.PORT || 8362,
webSocket: true,
})

Your frontend connects to ws://localhost:8362/ws (or your deployed URL) and receives a message whenever a resource is created, updated, or deleted — no polling needed. This is an easy way to make your app feel noticeably more dynamic, especially for collaborative or real-time use cases.