Providers are the backbone of Refine’s architecture. They are adapter interfaces that allow Refine to work with any backend service, authentication system, or third-party integration without being tied to a specific implementation.
What is a Provider?
A provider is an object that implements a specific interface. Refine calls methods on these providers to perform operations like fetching data, authenticating users, or checking permissions.
import { DataProvider, AuthProvider } from "@refinedev/core";
// Example provider structure
const dataProvider: DataProvider = {
getList: async (params) => { /* ... */ },
getOne: async (params) => { /* ... */ },
create: async (params) => { /* ... */ },
update: async (params) => { /* ... */ },
deleteOne: async (params) => { /* ... */ },
getApiUrl: () => "https://api.example.com",
};
const authProvider: AuthProvider = {
login: async (params) => { /* ... */ },
logout: async (params) => { /* ... */ },
check: async (params) => { /* ... */ },
onError: async (error) => { /* ... */ },
getIdentity: async () => { /* ... */ },
};
Data Provider
The Data Provider is the only required provider and handles all CRUD operations.
Interface Definition
From packages/core/src/contexts/data/types.ts:441-487:
export type DataProvider = {
// Required methods
getList: <TData extends BaseRecord = BaseRecord>(
params: GetListParams,
) => Promise<GetListResponse<TData>>;
getOne: <TData extends BaseRecord = BaseRecord>(
params: GetOneParams,
) => Promise<GetOneResponse<TData>>;
create: <TData extends BaseRecord = BaseRecord, TVariables = {}>(
params: CreateParams<TVariables>,
) => Promise<CreateResponse<TData>>;
update: <TData extends BaseRecord = BaseRecord, TVariables = {}>(
params: UpdateParams<TVariables>,
) => Promise<UpdateResponse<TData>>;
deleteOne: <TData extends BaseRecord = BaseRecord, TVariables = {}>(
params: DeleteOneParams<TVariables>,
) => Promise<DeleteOneResponse<TData>>;
getApiUrl: () => string;
// Optional methods
getMany?: <TData extends BaseRecord = BaseRecord>(
params: GetManyParams,
) => Promise<GetManyResponse<TData>>;
createMany?: <TData extends BaseRecord = BaseRecord, TVariables = {}>(
params: CreateManyParams<TVariables>,
) => Promise<CreateManyResponse<TData>>;
updateMany?: <TData extends BaseRecord = BaseRecord, TVariables = {}>(
params: UpdateManyParams<TVariables>,
) => Promise<UpdateManyResponse<TData>>;
deleteMany?: <TData extends BaseRecord = BaseRecord, TVariables = {}>(
params: DeleteManyParams<TVariables>,
) => Promise<DeleteManyResponse<TData>>;
custom?: <TData extends BaseRecord = BaseRecord>(
params: CustomParams,
) => Promise<CustomResponse<TData>>;
};
Implementation Example
import { DataProvider } from "@refinedev/core";
import axios from "axios";
const API_URL = "https://api.example.com";
export const dataProvider: DataProvider = {
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const { current = 1, pageSize = 10 } = pagination ?? {};
const { data } = await axios.get(`${API_URL}/${resource}`, {
params: {
_start: (current - 1) * pageSize,
_limit: pageSize,
// Apply filters and sorters
},
});
return {
data: data.items,
total: data.total,
};
},
getOne: async ({ resource, id, meta }) => {
const { data } = await axios.get(`${API_URL}/${resource}/${id}`);
return { data };
},
create: async ({ resource, variables, meta }) => {
const { data } = await axios.post(`${API_URL}/${resource}`, variables);
return { data };
},
update: async ({ resource, id, variables, meta }) => {
const { data } = await axios.patch(
`${API_URL}/${resource}/${id}`,
variables,
);
return { data };
},
deleteOne: async ({ resource, id, meta }) => {
const { data } = await axios.delete(`${API_URL}/${resource}/${id}`);
return { data };
},
getApiUrl: () => API_URL,
};
Refine provides pre-built data providers for popular backends like REST, GraphQL, Supabase, and more. Check the Data Providers section for ready-to-use implementations.
Multiple Data Providers
You can use multiple data providers for different resources:
import { Refine } from "@refinedev/core";
import restDataProvider from "@refinedev/simple-rest";
import graphqlDataProvider from "./graphql-provider";
<Refine
dataProvider={{
default: restDataProvider("https://api.example.com"),
graphql: graphqlDataProvider("https://graphql.example.com"),
}}
resources={[
{
name: "posts",
// Uses default provider
},
{
name: "users",
meta: {
dataProviderName: "graphql", // Uses graphql provider
},
},
]}
/>
Auth Provider
Handles authentication and authorization logic.
Interface Definition
From packages/core/src/contexts/auth/types.ts:66-78:
export type AuthProvider = {
login: (params: any) => Promise<AuthActionResponse>;
logout: (params: any) => Promise<AuthActionResponse>;
check: (params?: any) => Promise<CheckResponse>;
onError: (error: any) => Promise<OnErrorResponse>;
register?: (params: any) => Promise<AuthActionResponse>;
forgotPassword?: (params: any) => Promise<AuthActionResponse>;
updatePassword?: (params: any) => Promise<AuthActionResponse>;
getPermissions?: (params?: any) => Promise<PermissionResponse>;
getIdentity?: (params?: any) => Promise<IdentityResponse>;
};
Response Types
// Login, logout, register, forgotPassword, updatePassword
export type AuthActionResponse = {
success: boolean;
redirectTo?: string;
error?: Error;
successNotification?: {
message: string;
description?: string;
};
};
// check method
export type CheckResponse = {
authenticated: boolean;
redirectTo?: string;
logout?: boolean;
error?: Error;
};
// onError method
export type OnErrorResponse = {
redirectTo?: string;
logout?: boolean;
error?: Error;
};
Implementation Example
From examples/app-crm-minimal/src/providers/auth.ts:
import type { AuthProvider } from "@refinedev/core";
export const authProvider: AuthProvider = {
login: async ({ email, password }) => {
try {
const response = await fetch("https://api.example.com/auth/login", {
method: "POST",
body: JSON.stringify({ email, password }),
headers: { "Content-Type": "application/json" },
});
const { token } = await response.json();
localStorage.setItem("access_token", token);
return {
success: true,
redirectTo: "/",
};
} catch (error) {
return {
success: false,
error: {
name: "LoginError",
message: "Invalid email or password",
},
};
}
},
logout: async () => {
localStorage.removeItem("access_token");
return {
success: true,
redirectTo: "/login",
};
},
check: async () => {
const token = localStorage.getItem("access_token");
if (token) {
return {
authenticated: true,
};
}
return {
authenticated: false,
redirectTo: "/login",
logout: true,
};
},
onError: async (error) => {
if (error.statusCode === 401 || error.statusCode === 403) {
return {
logout: true,
redirectTo: "/login",
};
}
return { error };
},
getIdentity: async () => {
const token = localStorage.getItem("access_token");
if (!token) return null;
const response = await fetch("https://api.example.com/auth/me", {
headers: { Authorization: `Bearer ${token}` },
});
const user = await response.json();
return user;
},
getPermissions: async () => {
const token = localStorage.getItem("access_token");
if (!token) return null;
const response = await fetch("https://api.example.com/auth/permissions", {
headers: { Authorization: `Bearer ${token}` },
});
const permissions = await response.json();
return permissions;
},
};
All methods return promises. Refine handles redirects, notifications, and error states based on the returned values.
Access Control Provider
Enables role-based access control (RBAC) and permission management.
Interface Definition
From packages/core/src/contexts/accessControl/types.ts:82-85:
export type AccessControlProvider = {
can: (params: CanParams) => Promise<CanReturnType>;
options?: AccessControlOptions;
};
export type CanParams = {
resource?: string;
action: string;
params?: {
resource?: IResourceItem;
id?: BaseKey;
[key: string]: any;
};
};
export type CanReturnType = {
can: boolean;
reason?: string;
};
Implementation Example
From examples/access-control-casbin/src/App.tsx:
import { AccessControlProvider } from "@refinedev/core";
import { newEnforcer } from "casbin";
const role = "editor";
const accessControlProvider: AccessControlProvider = {
can: async ({ action, params, resource }) => {
const enforcer = await newEnforcer(model, adapter);
// Check permissions based on resource and action
if (action === "delete" || action === "edit" || action === "show") {
const can = await enforcer.enforce(
role,
`${resource}/${params?.id}`,
action,
);
return {
can,
reason: can ? undefined : "Unauthorized",
};
}
return {
can: await enforcer.enforce(role, resource, action),
};
},
};
Use the <CanAccess> component or useCan hook to conditionally render UI based on permissions.
Router Provider
Integrates Refine with your routing library.
Interface Definition
From packages/core/src/contexts/router/types.ts:72-79:
export type RouterProvider = {
go?: () => GoFunction;
back?: () => BackFunction;
parse?: () => ParseFunction;
Link?: React.ComponentType<
React.PropsWithChildren<{ to: string; [prop: string]: any }>
>;
};
export type GoFunction = (config: GoConfig) => void | string;
export type BackFunction = () => void;
export type ParseFunction = () => ParseResponse;
Implementation Example
import { RouterProvider } from "@refinedev/core";
import { useNavigate, useLocation, useParams, Link } from "react-router";
const routerProvider: RouterProvider = {
go: () => {
const navigate = useNavigate();
return ({ to, query, hash, options, type }) => {
// Implementation
};
},
back: () => {
const navigate = useNavigate();
return () => navigate(-1);
},
parse: () => {
const location = useLocation();
const params = useParams();
return () => {
// Parse and return current route info
};
},
Link,
};
Refine provides router providers for React Router, Next.js, Remix, and other popular routing solutions.
Notification Provider
Handles user notifications (success, error, info messages).
import { NotificationProvider } from "@refinedev/core";
import { notification } from "antd";
export const notificationProvider: NotificationProvider = {
open: ({ message, description, type, key }) => {
notification[type]({
message,
description,
key,
});
},
close: (key) => {
notification.close(key);
},
};
i18n Provider
Enables internationalization support.
import { I18nProvider } from "@refinedev/core";
import i18n from "i18next";
export const i18nProvider: I18nProvider = {
translate: (key, options) => i18n.t(key, options),
changeLocale: (lang) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
};
Live Provider
Enables real-time updates via WebSockets, SSE, or other protocols.
import { LiveProvider } from "@refinedev/core";
import { createClient } from "graphql-ws";
const wsClient = createClient({
url: "wss://api.example.com/graphql",
});
export const liveProvider: LiveProvider = {
subscribe: ({ channel, types, params, callback }) => {
// Subscribe to real-time events
},
unsubscribe: (subscription) => {
// Unsubscribe from events
},
publish: ({ channel, type, payload, date }) => {
// Publish events (optional)
},
};
Audit Log Provider
Tracks changes and user actions.
import { AuditLogProvider } from "@refinedev/core";
export const auditLogProvider: AuditLogProvider = {
create: (params) => {
// Log create action
},
update: (params) => {
// Log update action
},
get: (params) => {
// Retrieve audit logs
},
};
Best Practices
1. Error Handling
Always handle errors gracefully in providers:
getOne: async ({ resource, id }) => {
try {
const { data } = await api.get(`/${resource}/${id}`);
return { data };
} catch (error) {
throw {
message: error.message,
statusCode: error.response?.status || 500,
};
}
}
2. Type Safety
Use TypeScript for type-safe providers:
import { DataProvider, BaseRecord } from "@refinedev/core";
interface IPost extends BaseRecord {
title: string;
content: string;
}
const dataProvider: DataProvider = {
getOne: async <TData extends BaseRecord = BaseRecord>(
params: GetOneParams,
): Promise<GetOneResponse<TData>> => {
// Implementation
},
};
The meta parameter is available in all provider methods for custom data:
const { data } = useOne({
resource: "posts",
id: 1,
meta: {
headers: { "X-Custom-Header": "value" },
fields: ["id", "title", "author.name"],
},
});
Next Steps
Data Fetching
Learn how to fetch and manage data
Authentication
Implement authentication flows
Authorization
Set up access control
Data Provider Tutorial
Build a custom data provider