Developer Workflow
This guide covers day-to-day development tasks for the Workloom API and web app.
Running the Full Stack
# Install dependencies
pnpm install
# Start the API server (port 4050)
pnpm api:start
# Start the web dev server (port 3050, proxies to API)
pnpm web:start
# Run all tests
pnpm test
# Type-check the entire monorepo
pnpm typecheck
Adding a New API Endpoint
Step 1: Define the Route
Create or update a route file in packages/registry-server/src/routes/:
import { createRoute, z } from '@hono/zod-openapi'
const myRoute = createRoute({
method: 'get',
path: '/v1/my-resource',
request: {
query: z.object({ limit: z.number().optional() }),
},
responses: {
200: {
content: { 'application/json': { schema: MyResponseSchema } },
description: 'Success',
},
},
})
Step 2: Implement the Handler
Register the route on the Hono app in packages/registry-server/src/index.ts:
app.openapi(myRoute, async (c) => {
const { limit } = c.req.valid('query')
const result = await service.getMyResource(limit)
return c.json(result, 200)
})
Step 3: Regenerate the OpenAPI Spec
# Start the API server and extract the spec
pnpm api:start &
curl http://localhost:4050/v1/openapi.json > apps/api/openapi.json
kill %1
Step 4: Regenerate the Client
pnpm --filter @workloom/workloom-api generate
This produces new typed client functions, React Query hooks, TypeScript types,
and Zod schemas in packages/workloom-api/src/generated/.
Step 5: Verify
# Type-check generated output
pnpm --filter @workloom/workloom-api generate:check
# Run full type-check
pnpm typecheck
# Run tests
pnpm test
Generation Pipeline Commands
| Command | Purpose |
|---|---|
pnpm --filter @workloom/workloom-api generate | Run kubb to regenerate client from spec |
pnpm --filter @workloom/workloom-api generate:check | Generate + tsc --noEmit validation |
pnpm typecheck | Type-check entire monorepo |
Kubb Configuration
The generation pipeline is configured in packages/workloom-api/kubb.config.ts:
| Plugin | Output | Purpose |
|---|---|---|
pluginTs | ./src/generated/types/ | TypeScript interfaces |
pluginClient | ./src/generated/client/ | Typed fetch functions |
pluginReactQuery | ./src/generated/hooks/ | React Query hooks |
pluginZod | ./src/generated/zod/ | Zod validation schemas |
Import paths:
- Client functions use
@workloom/fetch-clientfor transport. - Hooks import from
@workloom/react-query.
Using Generated Hooks in the Web App
After regenerating, new hooks are immediately available:
import { useMyResource } from '@workloom/workloom-api'
function MyPage() {
const { accessToken, headers } = useAccessToken()
const { data } = useMyResource({
client: { headers },
query: { enabled: !!accessToken },
})
// ...
}
CI Validation
CI enforces two synchronization checks:
- Spec freshness: If registry-server routes change but
openapi.jsonis not regenerated, CI fails. - Client freshness: If
openapi.jsonchanges but kubb is not rerun, CI fails (detected by diffing generated output against committed code).
Package Dependencies
@workloom/registry-server (Hono + zod-openapi)
produces -> apps/api/openapi.json
apps/api/openapi.json
consumed by -> @workloom/workloom-api (kubb)
@workloom/workloom-api (generated hooks/client)
depends on -> @workloom/fetch-client (transport)
depends on -> @workloom/react-query (query layer)
apps/web (React SPA)
depends on -> @workloom/workloom-api (generated hooks)
depends on -> @supabase/supabase-js (Supabase login for API-mediated OIDC)
Common Tasks
Update an existing endpoint's response shape
- Modify the Zod schema in
packages/registry-server/src/routes/. - Regenerate spec and client (Steps 3-5 above).
- Fix any type errors in
apps/webfrom the new response shape.
Add a new composition hook
- Create
apps/web/src/hooks/useMyModel.ts. - Import generated hooks from
@workloom/workloom-api. - Accept
{ headers, enabled }options. - Pass options to each generated hook call.
- Use
getCombinedStatusto derive combined loading state.
Debug an API request
- Check
GET /v1/openapi.jsonfor the expected request/response schema. - Verify the Bearer token is being sent (Network tab → Authorization header).
- Check
useAccessToken()returns a non-null token. - Verify
enabled: !!accessTokenis preventing premature queries.