Chuyển tới nội dung chính

Viết RTK Query Service

Cấu trúc

API services nằm trong src/services/*.service.ts, dùng baseApi.injectEndpoints.

Ví dụ đầy đủ

// src/services/product.service.ts
import { baseApi } from "@/config/baseApi";
import type { CommonResponse, PaginationResponse } from "@vppos/core/types";
import type { Product, CreateProductPayload } from "@/types/product.type";

export const productApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
// GET — Query
getProducts: builder.query<
PaginationResponse<Product>,
{ page: number; limit?: number; search?: string }
>({
query: ({ page, limit = 10, search }) => ({
url: "/products",
params: { page, limit, search },
}),
}),

// GET by ID — Query
getProductById: builder.query<CommonResponse<Product>, string>({
query: (id) => `/products/${id}`,
}),

// POST — Mutation
createProduct: builder.mutation<CommonResponse<Product>, CreateProductPayload>({
query: (body) => ({
url: "/products",
method: "POST",
body,
}),
}),

// PUT — Mutation
updateProduct: builder.mutation<
CommonResponse<Product>,
{ id: string; body: Partial<CreateProductPayload> }
>({
query: ({ id, body }) => ({
url: `/products/${id}`,
method: "PUT",
body,
}),
}),

// DELETE — Mutation
deleteProduct: builder.mutation<CommonResponse<null>, string>({
query: (id) => ({
url: `/products/${id}`,
method: "DELETE",
}),
}),
}),
});

// Export auto-generated hooks
export const {
useGetProductsQuery,
useLazyGetProductsQuery,
useGetProductByIdQuery,
useCreateProductMutation,
useUpdateProductMutation,
useDeleteProductMutation,
} = productApi;

Sử dụng trong component

Query (GET)

function ProductList() {
const { storeId } = useStoreSelection();
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, 300);

const { data, isLoading, isFetching } = useGetProductsQuery({
page: 1,
search: debouncedSearch,
});

if (isLoading) return <TableSkeleton />;
// render data.data...
}

Lazy Query (trigger thủ công)

const [trigger, { data, isFetching }] = useLazyGetProductsQuery();

const handleSearch = () => {
trigger({ page: 1, search: "keyword" });
};

Mutation (POST/PUT/DELETE)

const [createProduct, { isLoading }] = useCreateProductMutation();

const handleSubmit = async (data: CreateProductPayload) => {
try {
await createProduct(data).unwrap();
toastUtils.success("Tạo sản phẩm thành công");
} catch (err) {
toastUtils.error(getApiErrorMessage(err));
}
};

Quy tắc

mẹo
  • Luôn dùng CommonResponse<T> hoặc PaginationResponse<T> cho response type
  • Export tất cả hooks cần dùng
  • Không tạo useState loading riêng khi đã có RTK Query
  • Không gọi fetch/axios trực tiếp trong component