Enterprise admin app2025Full-stack engineer5 months

Ops Console

A multi-tenant operations console with typed API clients generated from OpenAPI and permission-aware UI, replacing three internal tools.

  • React
  • TanStack Query
  • Spring Security
  • OpenAPI
3 to 1
internal tools consolidated
0
permission incidents after launch
60fps
virtualised tables at 100k rows

The problem

Support, finance and warehouse teams each had their own tool with its own login and inconsistent data. Permission bugs leaked actions to the wrong roles.

Constraints

Architecture

Key decisions

Decision 01
OpenAPI as the contract

The Spring controllers publish an OpenAPI spec in CI; the React SDK is generated from it, so a breaking API change fails the frontend build.

Trade-off: Generated code needs a thin hand-written layer for ergonomics.

Decision 02
Permissions as data, enforced twice

The server is the authority and Postgres row-level security backs it up. The UI reads the same policy to hide actions instead of disabling them.

Trade-off: Policy changes must be deployed to both layers together.

In the code

web/src/hooks/useCan.ts
1export function useCan(action: Action, resource: Resource) {2  const { data: policy } = useQuery({3    queryKey: ["policy"],4    queryFn: api.auth.getPolicy,5    staleTime: 5 * 60_000,6  });78  return useMemo(9    () => !!policy && evaluate(policy, action, resource),10    [policy, action, resource],11  );12}

What I learned