Calling API
Basic usage
The useCallProcedure hook returns a function that allows you to call backend procedures defined in your custom controller.
Example of a custom page in app/pages/products/index.jsx:
tsx
import { useCallProcedure, Page } from '@kottster/react';
export default () => {
// Get the callProcedure function
const callProcedure = useCallProcedure();
const handleClick = async () => {
// Call the backend procedure
const result = await callProcedure('getProducts');
console.log(result);
};
return (
<Page>
<button onClick={handleClick}>
Load Products
</button>
</Page>
);
};tsx
import { useCallProcedure, Page } from '@kottster/react';
import { type Procedures } from './api.server';
export default () => {
// Get the typed callProcedure function
const callProcedure = useCallProcedure<Procedures>();
const handleClick = async () => {
// Call the backend procedure
const result = await callProcedure('getProducts');
console.log(result);
};
return (
<Page>
<button onClick={handleClick}>
Load Products
</button>
</Page>
);
};Passing parameters
You can pass parameters to your backend procedures as the second argument:
tsx
import { useState } from 'react';
import { useCallProcedure, Page } from '@kottster/react';
export default () => {
const callProcedure = useCallProcedure();
const [product, setProduct] = useState(null);
const loadProduct = async (productId) => {
// Pass parameters in the second argument
const result = await callProcedure('getProduct', { id: productId });
setProduct(result);
};
return (
<Page>
<button onClick={() => loadProduct(1)}>
Load Product 1
</button>
{product && (
<div>
<h3>{product.name}</h3>
<p>Price: ${product.price}</p>
</div>
)}
</Page>
);
};tsx
import { useState } from 'react';
import { useCallProcedure, Page } from '@kottster/react';
import { type Procedures } from './api.server';
export default () => {
const callProcedure = useCallProcedure<Procedures>();
const [product, setProduct] = useState(null);
const loadProduct = async (productId) => {
// Pass parameters in the second argument. It is fully type-safe
const result = await callProcedure('getProduct', { id: productId });
setProduct(result);
};
return (
<Page>
<button onClick={() => loadProduct(1)}>
Load Product 1
</button>
{product && (
<div>
<h3>{product.name}</h3>
<p>Price: ${product.price}</p>
</div>
)}
</Page>
);
};Using TanStack React Query
TanStack React Query provides powerful data fetching capabilities like caching, background updates, and optimistic updates. You can easily integrate it with useCallProcedure:
tsx
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useCallProcedure, Page } from '@kottster/react';
import { type Procedures } from './api.server';
export default () => {
const callProcedure = useCallProcedure<Procedures>();
const queryClient = useQueryClient();
// Get products using React Query
const { data: products, isLoading } = useQuery({
queryKey: ['products'],
queryFn: () => callProcedure('getProducts'),
});
// Mutation for creating a new product
const createProductMutation = useMutation({
mutationFn: (newProduct: { name: string; price: number }) =>
callProcedure('createProduct', newProduct),
onSuccess: () => {
// Invalidate and refetch products after successful creation
queryClient.invalidateQueries({ queryKey: ['products'] });
},
});
// Mutation for deleting a product
const deleteProductMutation = useMutation({
mutationFn: (productId: number) =>
callProcedure('deleteProduct', { id: productId }),
onSuccess: () => {
// Invalidate and refetch products after successful deletion
queryClient.invalidateQueries({ queryKey: ['products'] });
},
});
const handleCreateProduct = () => {
createProductMutation.mutate({
name: 'New Product',
price: 99.99,
});
};
const handleDeleteProduct = (productId: number) => {
deleteProductMutation.mutate(productId);
};
if (isLoading) return <Page><div>Loading...</div></Page>;
return (
<Page title="Products">
<button
onClick={handleCreateProduct}
disabled={createProductMutation.isPending}
>
{createProductMutation.isPending ? 'Creating...' : 'Add Product'}
</button>
<ul>
{products?.map(product => (
<li key={product.id}>
{product.name} - ${product.price}
<button
onClick={() => handleDeleteProduct(product.id)}
disabled={deleteProductMutation.isPending}
>
Delete
</button>
</li>
))}
</ul>
</Page>
);
};