Skip to content

SPA + Backend Architecture

Use this architecture when you have a separate frontend team or an existing React/Vue/Angular application.

  • Separate frontend and backend codebases
  • Existing React/Vue/Angular application
  • Different deployment strategies for frontend/backend
  • Frontend team prefers their own tooling

Create the API:

Terminal window
npx create-velox-app my-api

Create your SPA separately (Vite, Create React App, Next.js, etc.):

Terminal window
npm create vite@latest my-frontend -- --template react-ts
api/src/procedures/products.ts
import { procedures, procedure } from '@veloxts/velox';
import { z } from '@veloxts/velox';
export const productProcedures = procedures('products', {
listProducts: procedure()
.output(z.array(ProductSchema))
.query(({ ctx }) => ctx.db.product.findMany()),
getProduct: procedure()
.input(z.object({ id: z.string() }))
.output(ProductSchema)
.query(({ input, ctx }) => ctx.db.product.findUniqueOrThrow({
where: { id: input.id },
})),
});

Install the VeloxTS client:

Terminal window
cd my-frontend
pnpm add @veloxts/client

Create a typed client:

frontend/src/api/client.ts
import { createClient } from '@veloxts/client';
import type { productProcedures } from '../../api/src/procedures/products';
type AppProcedures = {
products: typeof productProcedures;
};
export const api = createClient<AppProcedures>({
baseUrl: 'http://localhost:3030/api',
});

Use in components:

frontend/src/components/ProductList.tsx
import { useEffect, useState } from 'react';
import { api } from '../api/client';
export function ProductList() {
const [products, setProducts] = useState<Product[]>([]);
useEffect(() => {
api.products.listProducts().then(setProducts);
}, []);
return (
<ul>
{products.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
);
}

Enable CORS for your frontend origin:

api/src/index.ts
import { veloxApp } from '@veloxts/velox';
const app = veloxApp({
cors: {
origin: ['http://localhost:5173'], // Vite default
credentials: true,
},
});

For better data fetching, use the React hooks:

import { createVeloxHooks } from '@veloxts/client/react';
import { api } from './client';
export const { useQuery, useMutation } = createVeloxHooks({ client: api });
// In component
const { data: products, isLoading } = useQuery(
['products'],
() => api.products.listProducts()
);