API Integration
Maybern uses Orval to generate type-safe API clients from the backend OpenAPI specification.Generated Clients
API clients are generated insrc/gen/api/:
Report incorrect code
Copy
Ask AI
gen/api/
├── models/ # TypeScript types for all models
├── endpoints/ # API functions (queries, mutations)
└── schemas/ # Zod schemas for validation
Using Queries
Report incorrect code
Copy
Ask AI
import {
useFundFamilyList,
useFundFamilyRetrieve,
} from "@/gen/api";
// List all fund families
function FundFamilyList() {
const { data, isLoading, error } = useFundFamilyList();
if (isLoading) return <Spinner />;
if (error) return <ErrorAlert error={error} />;
return (
<List>
{data?.results.map((ff) => (
<ListItem key={ff.id}>{ff.name}</ListItem>
))}
</List>
);
}
// Get single fund family
function FundFamilyDetail({ id }: { id: string }) {
const { data: fundFamily } = useFundFamilyRetrieve(id);
return <h1>{fundFamily?.name}</h1>;
}
Using Mutations
Report incorrect code
Copy
Ask AI
import { useFundFamilyCreate, useFundFamilyUpdate } from "@/gen/api";
function CreateFundFamily() {
const createMutation = useFundFamilyCreate();
const handleCreate = async (formData: FundFamilyCreateData) => {
try {
const result = await createMutation.mutateAsync({
data: formData,
});
toast.success(`Created ${result.name}`);
navigate(routes.fundFamily.detail({ id: result.id }));
} catch (error) {
toast.error("Failed to create fund family");
}
};
return (
<FundFamilyForm
onSubmit={handleCreate}
isLoading={createMutation.isPending}
/>
);
}
Query Options
Report incorrect code
Copy
Ask AI
// With query options
const { data } = useFundFamilyList({
query: {
enabled: !!customerId,
refetchInterval: 30000,
staleTime: 5000,
},
});
// With parameters
const { data } = useTransactionList({
status: "completed",
page: 1,
pageSize: 25,
});
Error Handling
Report incorrect code
Copy
Ask AI
function DataComponent() {
const { data, error, isError } = useFundFamilyList();
if (isError) {
// Error is typed
if (error.response?.status === 404) {
return <NotFound />;
}
if (error.response?.status === 403) {
return <Forbidden />;
}
return <GenericError error={error} />;
}
return <Data data={data} />;
}
Cache Invalidation
Report incorrect code
Copy
Ask AI
import { useQueryClient } from "@tanstack/react-query";
function UpdateButton({ id }: { id: string }) {
const queryClient = useQueryClient();
const updateMutation = useFundFamilyUpdate();
const handleUpdate = async (data: UpdateData) => {
await updateMutation.mutateAsync({ id, data });
// Invalidate related queries
queryClient.invalidateQueries({ queryKey: ["fundFamily"] });
};
}
Regenerating Clients
After backend API changes:Report incorrect code
Copy
Ask AI
# Regenerate from OpenAPI spec
pnpm gen:api
# Or regenerate everything
pnpm gen
Never edit files in
src/gen/. They will be overwritten on regeneration.Custom Hooks
Wrap generated hooks for reusable patterns:Report incorrect code
Copy
Ask AI
// hooks/useFundFamilyWithDefaults.ts
export function useFundFamilyWithDefaults(id: string) {
const { data, ...rest } = useFundFamilyRetrieve(id);
return {
fundFamily: data,
displayName: data?.name ?? "Loading...",
isActive: data?.status === "active",
...rest,
};
}
Orval Configuration
Configuration inorval.config.ts:
Report incorrect code
Copy
Ask AI
export default {
maybern: {
input: "../backend/frontend-maybern.yml",
output: {
mode: "tags-split",
target: "./src/gen/api",
schemas: "./src/gen/api/models",
client: "react-query",
override: {
mutator: {
path: "./src/shared/api/axios.ts",
name: "customInstance",
},
},
},
},
};