Skip to main content

Data fetching in CARE

Introduction

CARE uses TanStack Query for data fetching and state management.

API Route Definitions

Routes are defined with their path, method, and types:

const routes = {
users: {
current: {
path: "/api/v1/users/getcurrentuser/",
method: "GET",
TRes: Type<UserModel>(), // Response type
}
},
}

Basic Usage with useQuery

→ TanStack Docs: useQuery Overview

import { useQuery } from "@tanstack/react-query";
import query from "@/Utils/request/query";

export default function UserProfile() {
const { data, isLoading } = useQuery({
queryKey: [routes.users.current.path],
queryFn: query(routes.users.current)
});

if (isLoading) return <Loading />;

return (
<div>
<h1>{data?.name}</h1>
<p>{data?.email}</p>
</div>
);
}

Passing Query Parameters

→ TanStack Docs: Query Keys

For URLs like /api/v1/medicine/search/?search=Paracetamol:

function SearchMedicines() {
const { data } = useQuery({
queryKey: [routes.medicine.search.path, "Paracetamol"],
queryFn: query(routes.medicine.search, {
queryParams: { search: "Paracetamol" }
}),
enabled: true,
});

return <MedicinesList medicines={data?.results} />;
}

Using Path Parameters

→ TanStack Docs: Dynamic Query Keys

For URLs like /api/v1/consultation/123/prescriptions/:

function PrescriptionsList({ consultationId }: { consultationId: string }) {
const { data } = useQuery({
queryKey: [routes.prescriptions.list.path, consultationId],
queryFn: query(routes.prescriptions.list, {
pathParams: { consultation_id: consultationId }
})
});

return <List items={data?.results} />;
}

Using Request Body

While useQuery is typically used for GET requests, it can also handle POST requests that are semantically queries (like search operations):

function SearchPatients() {
const { data } = useQuery({
queryKey: ['patients', 'search', searchTerm],
queryFn: query(routes.patients.search, {
body: {
search_text: searchTerm,
filters: {
district: selectedDistrict,
status: "Active"
}
}
}),
enabled: Boolean(searchTerm)
});

return <PatientsList patients={data?.results} />;
}

Note: For mutations (creating, updating, or deleting data), use useMutation instead.

Debounced Queries

For scenarios requiring debounced API calls (like search inputs), use query.debounced. This helps reduce unnecessary API calls during rapid user input:

function SearchComponent() {
const [search, setSearch] = useState("");

const { data } = useQuery({
queryKey: ['search', search],
queryFn: query.debounced(routes.search, {
queryParams: { q: search },
debounceInterval: 500 // Optional: defaults to 500ms
}),
enabled: search.length > 0
});

return (
<Input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
);
}

The debounced query will wait for the specified interval after the last call before executing the request. This is particularly useful for:

  • Search inputs
  • Auto-complete fields
  • Any UI element where the user might trigger rapid successive updates

How Debounced Queries Work

→ TanStack Docs: Query Cancellation

The implementation leverages TanStack Query's built-in cancellation through AbortSignal:

  1. When a new query is triggered, TanStack Query automatically creates an AbortSignal
  2. If a new query starts before the debounce delay finishes:
    • The previous signal is aborted automatically by TanStack Query
    • The previous sleep promise is cancelled
    • A new debounce timer starts

No explicit cleanup is needed because:

  • The AbortSignal is passed through to the underlying fetch call
  • When aborted, both the sleep promise and the fetch request are cancelled automatically
  • TanStack Query handles the abortion and cleanup of previous in-flight requests

Mutations

→ TanStack Docs: Mutations

CARE provides a mutate utility function that works seamlessly with TanStack Query's useMutation hook for creating, updating, or deleting data:

import { useMutation } from "@tanstack/react-query";
import mutate from "@/Utils/request/mutate";

function CreatePrescription({ consultationId }: { consultationId: string }) {
const { mutate: createPrescription, isPending } = useMutation({
mutationFn: mutate(routes.prescriptions.create, {
pathParams: { consultationId },
}),
onSuccess: () => {
toast.success("Prescription created successfully");
},
});

return (
<Button
onClick={() => createPrescription({
medicineId: "123",
dosage: "1x daily"
})}
disabled={isPending}
>
Create Prescription
</Button>
);
}

Using Path Parameters with Mutations

For URLs that require path parameters, like /api/v1/patients/123/update/:

function UpdatePatient({ patientId }: { patientId: string }) {
const { mutate: updatePatient } = useMutation({
mutationFn: mutate(routes.patients.update, {
pathParams: { id: patientId },
silent: true // Optional: suppress error notifications
})
});

const handleSubmit = (data: PatientData) => {
updatePatient(data);
};

return <PatientForm onSubmit={handleSubmit} />;
}

The mutate utility accepts configuration options similar to the query utility:

  • pathParams: For URL parameters
  • queryParams: For query string parameters
  • silent: Optional boolean to suppress error notifications
  • Additional request options as needed

Further Reading

For advanced features like:

See the TanStack Query docs for complete documentation.

Legacy Hooks (Deprecated)

Note: The following hooks are deprecated:

  • Use useQuery instead of useTanStackQueryInstead
  • Use useMutation instead of useDeprecatedMutation

These exist only for backward compatibility and will be removed in future versions.