Version: 4.xx.xx
data-provider-interface
import {
  DataProvider,
  HttpError,
  Pagination,
  CrudSorting,
  CrudFilters,
  CrudOperators,
} from "@refinedev/core";
import { stringify } from "query-string";
import axios, { AxiosInstance } from "axios";
type MethodTypes = "get" | "delete" | "head" | "options";
type MethodTypesWithBody = "post" | "put" | "patch";
const axiosInstance = axios.create();
export const dataProvider = (
  apiUrl: string,
  // get axios instance from user or use default one.
  httpClient: AxiosInstance = axiosInstance,
): DataProvider => ({
  getOne: async ({ resource, id, meta }) => {
    const url = `${apiUrl}/${resource}/${id}`;
    const { headers, method } = meta ?? {};
    const requestMethod = (method as MethodTypes) ?? "get";
    const { data } = await httpClient[requestMethod](url, { headers });
    return {
      data,
    };
  },
  update: async ({ resource, id, variables, meta }) => {
    const url = `${apiUrl}/${resource}/${id}`;
    const { headers, method } = meta ?? {};
    const requestMethod = (method as MethodTypesWithBody) ?? "patch";
    const { data } = await httpClient[requestMethod](url, variables, {
      headers,
    });
    return {
      data,
    };
  },
  create: async ({ resource, variables, meta }) => {
    const url = `${apiUrl}/${resource}`;
    const { headers, method } = meta ?? {};
    const requestMethod = (method as MethodTypesWithBody) ?? "post";
    const { data } = await httpClient[requestMethod](url, variables, {
      headers,
    });
    return {
      data,
    };
  },
  deleteOne: async ({ resource, id, variables, meta }) => {
    const url = `${apiUrl}/${resource}/${id}`;
    const { headers, method } = meta ?? {};
    const requestMethod = (method as MethodTypesWithBody) ?? "delete";
    const { data } = await httpClient[requestMethod](url, {
      data: variables,
      headers,
    });
    return {
      data,
    };
  },
  getList: async ({ resource, pagination, sorters, filters, meta }) => {
    const url = `${apiUrl}/${resource}`;
    const { headers: headersFromMeta, method } = meta ?? {};
    const requestMethod = (method as MethodTypes) ?? "get";
    // init query object for pagination and sorting
    const query: {
      _start?: number;
      _end?: number;
      _sort?: string;
      _order?: string;
    } = {};
    const generatedPagination = generatePagination(pagination);
    if (generatedPagination) {
      const { _start, _end } = generatedPagination;
      query._start = _start;
      query._end = _end;
    }
    const generatedSort = generateSort(sorters);
    if (generatedSort) {
      const { _sort, _order } = generatedSort;
      query._sort = _sort.join(",");
      query._order = _order.join(",");
    }
    const queryFilters = generateFilter(filters);
    const { data, headers } = await httpClient[requestMethod](
      `${url}?${stringify(query)}&${stringify(queryFilters)}`,
      {
        headers: headersFromMeta,
      },
    );
    const total = +headers["x-total-count"];
    return {
      data,
      total: total || data.length,
    };
  },
  getApiUrl: () => apiUrl,
});
// Convert axios errors to HttpError on every response.
axiosInstance.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    const customError: HttpError = {
      ...error,
      message: error.response?.data?.message,
      statusCode: error.response?.status,
    };
    return Promise.reject(customError);
  },
);
// convert Refine CrudOperators to the format that API accepts.
const mapOperator = (operator: CrudOperators): string => {
  switch (operator) {
    case "ne":
    case "gte":
    case "lte":
      return `_${operator}`;
    case "contains":
      return "_like";
    case "eq":
    default:
      return "";
  }
};
// generate query string from Refine CrudFilters to the format that API accepts.
const generateFilter = (filters?: CrudFilters) => {
  const queryFilters: { [key: string]: string } = {};
  if (filters) {
    filters.map((filter) => {
      if (filter.operator === "or" || filter.operator === "and") {
        throw new Error(
          `[@refinedev/simple-rest]: /docs/data/data-provider#creating-a-data-provider`,
        );
      }
      if ("field" in filter) {
        const { field, operator, value } = filter;
        if (field === "q") {
          queryFilters[field] = value;
          return;
        }
        const mappedOperator = mapOperator(operator);
        queryFilters[`${field}${mappedOperator}`] = value;
      }
    });
  }
  return queryFilters;
};
// generate query string from Refine CrudSorting to the format that API accepts.
const generateSort = (sorters?: CrudSorting) => {
  if (sorters && sorters.length > 0) {
    const _sort: string[] = [];
    const _order: string[] = [];
    sorters.map((item) => {
      _sort.push(item.field);
      _order.push(item.order);
    });
    return {
      _sort,
      _order,
    };
  }
  return;
};
// generate query string from Refine Pagination to the format that API accepts.
const generatePagination = (pagination?: Pagination) => {
  // pagination is optional on data hooks, so we need to set default values.
  const { current = 1, pageSize = 10, mode = "server" } = pagination ?? {};
  const query: {
    _start?: number;
    _end?: number;
  } = {};
  if (mode === "server") {
    query._start = (current - 1) * pageSize;
    query._end = current * pageSize;
  }
  return query;
};