Audit Log Provider
Refine allows you to track changes and who made them in your data by sending a new log event record whenever a new record is created, updated or deleted. Mutations made with data hooks are automatically sent to the auditLogProvider as an event. You can also manually send events to the auditLogProvider via hooks.
To use auditLogProvider, you just need to pass it to <Refine>:
import { Refine } from "@refinedev/core";
import auditLogProvider from "./auditLogProvider";
const App = () => (
<Refine
/* ... */
auditLogProvider={auditLogProvider}
/>
);
An auditLogProvider must have the following methods:
create: Logs an event to the audit log.get: Returns a list of events.update: Updates an event in the audit log.
Below are the corresponding interfaces for each of these methods:
const auditLogProvider = {
create: (params: {
resource: string;
action: string;
data?: any;
author?: {
name?: string;
[key: string]: any;
};
previousData?: any;
meta?: Record<string, any>;
}) => void;
get: (params: {
resource: string;
action?: string;
meta?: Record<string, any>;
author?: Record<string, any>;
}) => Promise<any>;
update: (params: {
id: BaseKey;
name: string;
}) => Promise<any>;
}
NOTE
Refine provides the useLog and useLogList hooks that can be used to access your auditLogProvider methods from anywhere in your application.
Creating an Audit Log Provider
Let's create an auditLogProvider to understand how it works better. Though we will be using dataProvider to handle events, you can do it however you want thanks to Refine providing an agnostic API.
get
This method is used to get a list of audit log events.
For example, using the useLogList hook to list all resource activities by a specific record id creates an event like this:
{
"resource": "posts",
"meta": {
"id": "1"
}
}
Now let's see how we can handle these events in our audit log provider.
export const auditLogProvider: AuditLogProvider = {
get: async (params) => {
const { resource, meta, action, author } = params;
const response = await fetch(
`https://example.com/api/audit-logs/${resource}/${meta.id}`,
{
method: "GET",
},
);
const data = await response.json();
return data;
},
};
create
We recommend you create audit logs on the API side for security concerns since the data can be changed on the client side.
This method is used to create an audit log event. It is triggered when a new successful mutation is made or when you use useLog's log method. The incoming parameters show the values of the new record to be created.
When the mutations is successful, the create method is called with the following parameters, depending on the mutation type:
Refine returns the
previousDatafrom the react-query cache if it can find it, returnsundefinedotherwise.In create mutations, if the request response has an
idfield, it will be added to themetaobject.If
getUserIdentityis defined in your auth provider, theauthorobject will be added to the event with the value returned bygetUserIdentity.
- Create
- Update
- Delete
- Create Many
- Update Many
- Delete Many
create
When a record is created, Refine automatically sends an event to create method like this:
{
"action": "create",
"resource": "posts",
"data": {
"title": "Hello World",
"content": "Hello World"
},
"meta": {
"dataProviderName": "simple-rest",
// If request response has a `id` field, it will be add in the `meta` field.
"id": 1
}
}
update
When a record is updated, Refine automatically sends an event to create method like this:
{
"action": "update",
"resource": "posts",
"data": {
"title": "New Hello World",
"content": "New Hello World"
},
"previousData": {
"title": "Hello World",
"content": "Hello World"
},
"meta": {
"dataProviderName": "simple-rest",
"id": 1
}
}
delete
When a record is deleted, Refine automatically sends an event to create method like this:
{
"action": "delete",
"resource": "posts",
"meta": {
"dataProviderName": "simple-rest",
"id": 1
}
}
createMany
When a record is created with the useCreateMany hook, Refine automatically sends an event to the create method like this:
{
"action": "createMany",
"resource": "posts",
"data": [
{
"title": "Hello World 1"
},
{
"title": "Hello World 2"
}
],
"meta": {
"dataProviderName": "simple-rest",
// If request response has a `id` field, it will be add in the `meta` field.
"ids": [1, 2]
}
}
updateMany
When a record is updated with the useUpdateMany hook, Refine automatically sends an event to the create method like this:
{
"action": "updateMany",
"resource": "posts",
"data": {
"status": "published"
},
"previousData": [
{
"status": "draft"
},
{
"status": "archived"
}
],
"meta": {
"dataProviderName": "simple-rest",
"ids": [1, 2]
}
}
deleteMany
When a record is deleted with the useDeleteMany hook, Refine automatically sends an event to the create method like this:
{
"action": "deleteMany",
"resource": "posts",
"meta": {
"dataProviderName": "simple-rest",
"id": [1, 2]
}
}
And here is how we can handle these events in our audit log provider:
export const auditLogProvider: AuditLogProvider = {
create: (params) => {
const { resource, meta, action, author, data, previousData } = params;
console.log(resource); // "produts", "posts", etc.
console.log(meta); // { id: "1" }, { id: "2" }, etc.
console.log(action); // "create", "update", "delete"
// author object is `useGetIdentity` hook's return value.
console.log(author); // { id: "1", name: "John Doe" }
console.log(data); // { name: "Product 1", price: 100 }
console.log(previousData); // { name: "Product 1", price: 50 }
await fetch("https://example.com/api/audit-logs", {
method: "POST",
body: JSON.stringify(params),
});
return { success: true };
},
};
For more information, refer to the useLog documentation→
update
This method is used to update an audit log event.
For example, using useLog's log method creates an event like below:
{
"id": "1",
"name": "event name"
}
export const auditLogProvider: AuditLogProvider = {
update: async (params) => {
const { id, name, ...rest } = params;
console.log(id); // "1"
console.log(name); // "Created Product 1"
console.log(rest); // { foo: "bar" }
await fetch(`https://example.com/api/audit-logs/${id}`, {
method: "PATCH",
body: JSON.stringify(params),
});
return { success: true };
},
};
For more information, refer to the useLog documentation→
Supported Hooks
The following hooks will call Audit Log Provider's create method when a mutation is successful.
| Package | Hooks |
|---|---|
| @refinedev/core | useForm |
| @refinedev/antd | useForm, useModalForm, useDrawerForm, useStepsForm |
| @refinedev/mantine | useForm, useModalForm, useDrawerForm, useStepsForm |
| @refinedev/react-hook-form | useForm, useModalForm, useStepsForm |
Here are the parameters each hook send to create:
useCreate
const { mutate } = useCreate();
mutate({
resource: "posts",
values: {
title: "New Post",
status: "published",
content: "New Post Content",
},
meta: {
foo: "bar",
},
});
// Calls Audit Log Provider's `create` method with the following parameters:
{
"action": "create",
"resource": "posts",
"data": {
"title": "Title",
"status": "published",
"content": "New Post Content"
},
"meta": {
"id": "1",
// Additional metadata is included in `meta`.
"foo": "bar"
}
}
useCreateMany
const { mutate } = useCreateMany();
mutate({
resource: "posts",
values: [
{
title: "Title1",
status: "published",
content: "New Post Content1",
},
{
title: "Title2",
status: "published",
content: "New Post Content2",
},
],
meta: {
foo: "bar",
},
});
// Calls Audit Log Provider's `create` method with the following parameters:
{
"action": "createMany",
"resource": "posts",
"data": [
{
"title": "Title1",
"status": "published",
"content": "New Post Content1"
},
{
"title": "Title2",
"status": "published",
"content": "New Post Content2"
}
],
"meta": {
"ids": [1, 2],
// Additional metadata is included in `meta`.
"foo": "bar"
}
}
useUpdate
const { mutate } = useUpdate();
mutate({
id: 1,
resource: "posts",
values: {
title: "Updated New Title",
},
});
// Calls Audit Log Provider's `create` method with the following parameters:
{
"action": "update",
"resource": "posts",
"data": {
"title": "Updated New Title",
"status": "published",
"content": "New Post Content"
},
"previousData": {
"title": "Title",
"status": "published",
"content": "New Post Content"
},
"meta": {
"id": 1
}
}
useUpdateMany
const { mutate } = useUpdateMany();
mutate({
ids: [1, 2],
resource: "posts",
values: {
title: "Updated New Title",
},
});
// Calls Audit Log Provider's `create` method with the following parameters:
{
"action": "updateMany",
"resource": "posts",
"data": {
"title": "Updated New Title"
},
"previousData": [
{
"title": "Title1"
},
{
"title": "Title2"
}
],
"meta": {
"ids": [1, 2]
}
}
useDelete
const { mutate } = useDelete();
mutate({
id: 1,
resource: "posts",
});
// Calls Audit Log Provider's `create` method with the following parameters:
{
"action": "delete",
"resource": "posts",
"meta": {
"id": 1
}
}
useDeleteMany
const { mutate } = useDeleteMany();
mutate({
ids: [1, 2],
resource: "posts",
});
// Calls Audit Log Provider's `create` method with the following parameters:
{
"action": "deleteMany",
"resource": "posts",
"meta": {
"ids": [1, 2]
}
}
Enable/Disable to Audit Log by Mutation Type for a Resource
With meta.audit, you can specify which mutations trigger audit logs; otherwise, all create, update, and delete actions will be logged by default.
For example, if you have the code below, only events will be created for the create mutation.
<Refine
dataProvider={dataProvider(API_URL)}
resources={[
{
name: "posts",
meta: {
audit: ["create"],
},
},
]}
/>
Example
npm create refine-app@latest -- --example audit-log-provider