halide
v0.0.12
Published
A declarative BFF (Backend for Frontend) framework that standardizes SPA communication with auth, proxy, CSP, rate limiting, OpenAPI support, and static file hosting
Maintainers
Readme
What is Halide?
Halide is a purpose-built, declarative BFF runtime that standardizes how your SPA communicates with backend services. It gives these concerns a shared, predictable structure, configured once rather than assembled from scratch every time.
Browser (SPA)
↓
Halide (auth, routing, composition)
↓
Private backend servicesHalide is not an API gateway, a service mesh, or a full backend framework. It is specifically designed around SPA application boundaries.
Get started
See the demo app for a complete example.
Run Halide in an empty project (npm init). The server runs as a standalone process alongside your SPA build tooling.
npx halide initThis automatically:
- Creates
package.jsonandnodemon.jsonif missing - Installs
halide - Scaffolds a project structure with
server.tsentry point and health route - Creates
tsconfig.jsonfor the server build
Manual Installation
npm install halide// routes.ts
import { defineHalide } from 'halide';
const { apiRoute, proxyRoute } = defineHalide();
export const healthRoute = apiRoute({
access: 'public',
method: 'get',
path: '/api/health',
handler: async (_ctx, _app) => ({ status: 'ok' }),
});
export const userProxyRoute = proxyRoute({
access: 'private',
methods: ['get', 'post'],
path: '/api/users',
target: 'http://user-svc:3000',
});// server.ts
import { healthRoute, userProxyRoute } from './routes';
import { defineHalide } from 'halide';
const { createServer } = defineHalide();
const server = createServer({
app: {
root: './browser',
},
security: {
auth: {
strategy: 'jwks',
jwksUri: 'https://my-tenant.us.auth0.com/.well-known/jwks.json',
audience: 'https://api.example.com',
},
},
apiRoutes: [healthRoute],
proxyRoutes: [userProxyRoute],
});
server.start((port) => console.log(`Serving on ${port}`));npx tsx server.tsThe server starts on port 3553. Override with
app.portor thePORTenvironment variable.
Why Halide?
In most SPA setups, each application carries its own ad-hoc BFF implementation. Across multiple apps this becomes a mess:
- Every SPA implements auth slightly differently, and debugging token issues across them is painful
- Proxy logic is copy-pasted between projects, drifting over time
- Frontend code ends up coupled to internal service topology (URLs, ports, paths)
- CORS configuration is repeated across services, often inconsistently
- No clear boundary between "frontend backend" and "actual backend" responsibilities
Halide provides a shared structure for all of these concerns, configured once and consistent across every SPA:
- Static SPA hosting with fallback routing
- Typed API routes with validation
- Secure proxying to backend services
- Built-in auth (JWT / JWKS)
- CORS, CSP, and rate limiting
- Optional OpenAPI documentation
The result is a predictable backend layer across all your SPAs, without duplication or drift.
When not to use Halide
Halide is opinionated but extensible. API route handlers are arbitrary async functions, and proxy routes support per-route authorize, transform, and identity hooks. Consider alternatives if:
- You need direct control over the HTTP layer. Halide abstracts Hono behind a typed config. If you need to set custom response headers/status codes, stream responses, handle file uploads, or run arbitrary Hono middleware, a full backend framework gives you that access.
- You're building a multi-service backend, not just a BFF layer. Halide sits between a frontend and its backends. If you need inter-service communication, routing, or discovery, an API gateway or service mesh is designed for that.
- You need infrastructure-level proxy control. Halide provides per-route request transformation, identity header injection, and path rewriting, but does not expose response transformation, circuit breakers, TLS termination, load balancing, or retry policies. An API gateway or service mesh is a better fit for those.
