SPA + Backend Architecture
The SPA + Backend architecture separates your Velox TS API from your frontend entirely. Choose this when you have separate teams, an existing frontend, or different deployment strategies for each.
When to Choose SPA + Backend
Section titled “When to Choose SPA + Backend”- Separate frontend and backend codebases
- Existing React/Vue/Angular application
- Different deployment strategies for frontend/backend
- Frontend team prefers their own tooling
Project Setup
Section titled “Project Setup”Create the API:
npx create-velox-app my-apiCreate your SPA separately (Vite, Create React App, Next.js, etc.):
npm create vite@latest my-frontend -- --template react-tsBackend: Velox TS API
Section titled “Backend: Velox TS API”import { procedures, procedure, resourceSchema, resource, resourceCollection } from '@veloxts/velox';import { z } from '@veloxts/velox';
// Define resource schema with field visibilityconst ProductSchema = resourceSchema() .public('id', z.string()) .public('name', z.string()) .public('price', z.number()) .authenticated('description', z.string()) .admin('costPrice', z.number()) .build();
export const productProcedures = procedures('products', { listProducts: procedure() .query(async ({ ctx }) => { const products = await ctx.db.product.findMany(); return resourceCollection(products, ProductSchema.public); }),
getProduct: procedure() .input(z.object({ id: z.string() })) .query(async ({ input, ctx }) => { const product = await ctx.db.product.findUniqueOrThrow({ where: { id: input.id }, }); return resource(product, ProductSchema.public); }),});Frontend: Type-Safe Client
Section titled “Frontend: Type-Safe Client”Install the Velox TS client:
cd my-frontendpnpm add @veloxts/clientCreate a typed client:
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:
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> );}CORS Configuration
Section titled “CORS Configuration”Enable CORS for your frontend origin:
import { veloxApp } from '@veloxts/velox';
const app = veloxApp({ cors: { origin: ['http://localhost:5173'], // Vite default credentials: true, },});React Query Integration
Section titled “React Query Integration”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 componentconst { data: products, isLoading } = useQuery( ['products'], () => api.products.listProducts());Related Content
Section titled “Related Content”- Client Package - Full client reference
- Authentication - Add auth
- REST Conventions - API patterns