Skip to main content

Introduction

The Strapi data provider enables you to build Refine applications with Strapi v4, a leading open-source headless CMS. It provides full support for CRUD operations, file uploads, and authentication.

Installation

Install the Strapi v4 data provider package:
npm install @refinedev/strapi-v4 axios

Setup

1

Create Strapi project

First, create a Strapi v4 project or use an existing one. Visit strapi.io/documentation for setup instructions.
2

Create Axios instance

Create an Axios instance and configure the Strapi auth helper:
import axios from "axios";
import { AuthHelper } from "@refinedev/strapi-v4";

const API_URL = "https://api.example.com";

const axiosInstance = axios.create();
const strapiAuthHelper = AuthHelper(API_URL);
3

Configure data provider

Configure the Refine app with the Strapi data provider:
import { Refine } from "@refinedev/core";
import { DataProvider } from "@refinedev/strapi-v4";
import axios from "axios";

const API_URL = "https://api.example.com";
const axiosInstance = axios.create();

const App = () => {
  return (
    <Refine
      dataProvider={DataProvider(API_URL, axiosInstance)}
    >
      {/* Your app content */}
    </Refine>
  );
};

Basic Usage

Get List

Fetch a list of records from a Strapi collection:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  pagination: {
    current: 1,
    pageSize: 10,
  },
  sorters: [
    {
      field: "createdAt",
      order: "desc",
    },
  ],
  filters: [
    {
      field: "status",
      operator: "eq",
      value: "published",
    },
  ],
});

Get One

Fetch a single record by ID:
import { useOne } from "@refinedev/core";

const { data } = useOne({
  resource: "posts",
  id: "1",
  meta: {
    populate: ["author", "categories"],
  },
});

Create

Create a new record:
import { useCreate } from "@refinedev/core";

const { mutate } = useCreate();

const handleSubmit = (values) => {
  mutate({
    resource: "posts",
    values: {
      title: values.title,
      content: values.content,
      status: "draft",
    },
  });
};

Update

Update an existing record:
import { useUpdate } from "@refinedev/core";

const { mutate } = useUpdate();

const handleUpdate = (id, values) => {
  mutate({
    resource: "posts",
    id,
    values: {
      title: values.title,
      content: values.content,
    },
  });
};

Delete

Delete a record:
import { useDelete } from "@refinedev/core";

const { mutate } = useDelete();

const handleDelete = (id) => {
  mutate({
    resource: "posts",
    id,
  });
};

Authentication

The Strapi data provider includes an auth helper for handling authentication:
import { Refine } from "@refinedev/core";
import { DataProvider, AuthHelper } from "@refinedev/strapi-v4";
import axios from "axios";

const API_URL = "https://api.example.com";
const axiosInstance = axios.create();
const strapiAuthHelper = AuthHelper(API_URL);

const authProvider = {
  login: async ({ email, password }) => {
    const { data, status } = await strapiAuthHelper.login(email, password);

    if (status === 200) {
      localStorage.setItem("token", data.jwt);

      // Set Authorization header
      axiosInstance.defaults.headers.common[
        "Authorization"
      ] = `Bearer ${data.jwt}`;

      return {
        success: true,
        redirectTo: "/",
      };
    }

    return {
      success: false,
      error: {
        message: "Login failed",
        name: "Invalid credentials",
      },
    };
  },
  logout: async () => {
    localStorage.removeItem("token");
    return {
      success: true,
      redirectTo: "/login",
    };
  },
  check: async () => {
    const token = localStorage.getItem("token");
    if (token) {
      axiosInstance.defaults.headers.common[
        "Authorization"
      ] = `Bearer ${token}`;
      return {
        authenticated: true,
      };
    }

    return {
      authenticated: false,
      redirectTo: "/login",
    };
  },
  getIdentity: async () => {
    const token = localStorage.getItem("token");
    if (!token) {
      return null;
    }

    const { data } = await strapiAuthHelper.me(token);
    return data;
  },
  onError: async (error) => {
    console.error(error);
    return { error };
  },
};

const App = () => {
  return (
    <Refine
      dataProvider={DataProvider(API_URL, axiosInstance)}
      authProvider={authProvider}
    >
      {/* Your app content */}
    </Refine>
  );
};

Populate Relations

Use the populate meta property to include related data:
import { useOne } from "@refinedev/core";

const { data } = useOne({
  resource: "posts",
  id: "1",
  meta: {
    populate: ["author", "categories", "cover"],
  },
});
For deep population:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  meta: {
    populate: {
      author: {
        populate: ["avatar"],
      },
      categories: "*",
      cover: "*",
    },
  },
});

File Upload

Handle file uploads with Strapi’s upload feature:
import { useCreate } from "@refinedev/core";
import { useState } from "react";

const CreatePost = () => {
  const { mutate } = useCreate();
  const [file, setFile] = useState<File>();

  const handleSubmit = async (values) => {
    // First, upload the file
    const formData = new FormData();
    if (file) {
      formData.append("files", file);
    }

    const uploadResponse = await axiosInstance.post(
      `${API_URL}/api/upload`,
      formData
    );

    const uploadedFile = uploadResponse.data[0];

    // Then create the post with the uploaded file
    mutate({
      resource: "posts",
      values: {
        ...values,
        cover: uploadedFile.id,
      },
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="file"
        onChange={(e) => setFile(e.target.files?.[0])}
      />
      {/* Other form fields */}
    </form>
  );
};

Advanced Filtering

Strapi supports complex filters:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  filters: [
    {
      field: "title",
      operator: "contains",
      value: "tutorial",
    },
    {
      field: "publishedAt",
      operator: "gte",
      value: "2024-01-01",
    },
    {
      field: "author.id",
      operator: "eq",
      value: "1",
    },
  ],
});

Locale Support

Work with internationalized content:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  meta: {
    locale: "fr",
  },
});

Publication State

Filter by publication state:
import { useList } from "@refinedev/core";

const { data } = useList({
  resource: "posts",
  meta: {
    publicationState: "preview", // 'live' or 'preview'
  },
});

Complete Example

import { Refine } from "@refinedev/core";
import { DataProvider, AuthHelper } from "@refinedev/strapi-v4";
import routerProvider from "@refinedev/react-router";
import { BrowserRouter } from "react-router";
import axios from "axios";

const API_URL = process.env.REACT_APP_API_URL!;

const axiosInstance = axios.create();
const strapiAuthHelper = AuthHelper(API_URL);

// Add request interceptor for auth
axiosInstance.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

const authProvider = {
  login: async ({ email, password }) => {
    const { data, status } = await strapiAuthHelper.login(email, password);

    if (status === 200) {
      localStorage.setItem("token", data.jwt);
      return { success: true, redirectTo: "/" };
    }

    return {
      success: false,
      error: { message: "Login failed", name: "Invalid credentials" },
    };
  },
  logout: async () => {
    localStorage.removeItem("token");
    return { success: true, redirectTo: "/login" };
  },
  check: async () => {
    const token = localStorage.getItem("token");
    if (token) {
      return { authenticated: true };
    }
    return { authenticated: false, redirectTo: "/login" };
  },
  getIdentity: async () => {
    const token = localStorage.getItem("token");
    if (!token) return null;

    const { data } = await strapiAuthHelper.me(token);
    return data;
  },
  onError: async (error) => {
    console.error(error);
    return { error };
  },
};

const App = () => {
  return (
    <BrowserRouter>
      <Refine
        dataProvider={DataProvider(API_URL, axiosInstance)}
        authProvider={authProvider}
        routerProvider={routerProvider}
        resources={[
          {
            name: "posts",
            list: "/posts",
            create: "/posts/create",
            edit: "/posts/edit/:id",
            show: "/posts/show/:id",
          },
        ]}
      >
        {/* Your routes and pages */}
      </Refine>
    </BrowserRouter>
  );
};

export default App;

Supported Operators

The Strapi data provider supports the following filter operators:
  • eq: Equals
  • ne: Not equals
  • lt: Less than
  • lte: Less than or equal
  • gt: Greater than
  • gte: Greater than or equal
  • in: In array
  • nin: Not in array
  • contains: Contains (case-sensitive)
  • containsi: Contains (case-insensitive)
  • null: Is null
  • nnull: Is not null

Next Steps

Data Providers Overview

Learn about other data providers

Strapi Documentation

Explore Strapi v4 features

Build docs developers (and LLMs) love