Synchronize the frontend API client (Orval) with the backend Swagger (NestJS). Use this skill when backend endpoints change or when implementing new API calls in the frontend.
Use this skill to keep the admin-web frontend in sync with the admin-api backend and use the generated TanStack Query hooks.
Follow these steps whenever the backend API (Controller, DTO, or Entity) changes.
In apps/admin-api/:
npm run setup:swagger
This updates src/metadata.ts based on your decorators and DTOs.
The frontend synchronization (Orval) needs the backend's JSON documentation.
Make sure admin-api is running on http://localhost:3000.
In apps/admin-web/:
npm run set-up
This will:
http://localhost:3000/docs-json.src/api/endpoints.ts (Hooks).src/api/model/*.ts (Types).src/api/zod.ts (Validation schemas).Orval generates TanStack Query (v5) hooks.
Use use[OperationName] hooks.
import { useGetTenants } from '@/api/endpoints';
function TenantList() {
const { data, isLoading, error } = useGetTenants();
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data?.data?.map(tenant => (
<li key={tenant.id}>{tenant.name}</li>
))}
</ul>
);
}
Use use[OperationName]Mutation hooks.
import { useCreateTenantMutation } from '@/api/endpoints';
import { toast } from 'sonner';
function CreateTenantForm() {
const mutation = useCreateTenantMutation({
mutation: {
onSuccess: () => {
// No need to call toast.success() here!
// It's handled globally in App.tsx via MutationCache.
},
}
});
const handleSubmit = (data) => {
mutation.mutate({ data });
};
return <button onClick={() => handleSubmit({ name: 'New Org' })} disabled={mutation.isPending}>Create</button>;
}
Use generated Zod schemas with useAppForm from @repo/ui.
import { createTenantSchema } from '@/api/zod';
import { useAppForm } from '@repo/ui/components/form/context';
import z from 'zod';
const form = useAppForm({
defaultValues: { name: '' },
validators: {
onSubmit: createTenantSchema,
},
onSubmit: ({ value }) => {
// Call mutation here
},
});
You can extend generated schemas for UI-specific logic (like adding a "confirm password" field).
import { SignInControllerResetPasswordBody } from '@/api/zod';
import { z } from 'zod';
const form = useAppForm({
defaultValues: {
password: '',
confirmPassword: '',
},
validators: {
onSubmit: SignInControllerResetPasswordBody.extend({
confirmPassword: z.string(),
}),
},
// ...
});
admin-api is actually running on port 3000.npm run setup:swagger in the backend before npm run set-up in the frontend.@ApiProperty, @Length, etc.) and ensure the Swagger plugin is correctly generating metadata.src/lib/axios-instance.ts in admin-web.toast.success or toast.error in components. The admin-web application handles this globally in App.tsx (using MutationCache) and error-handler.ts.