Giter VIP home page Giter VIP logo

ra-strapi-rest's Introduction

Simple REST Data Provider for React Admin - Strapi

React Admin data provider for Strapi.js.

Strapi V4 Update

RA-STRAPI-REST works with Strapi V4. 🚀

If you want to use the version compatible with Strapi V3 check this PR.

Also, it is converted to TypeScript.

Check out this demo for reference => Demo

  • Works with Single Types
  • Works with Collection types
  • Works with Components
  • Handles single and multiple Media files
  • Handles basic filtering
  • Handles sorting and pagination
  • Works with reference fields/inputs under community version
  • Tested with Sqlite and Postgres

Please submit a PR with an example if you find any bugs. Thanks!

Usage

Save the index.ts file as ra-strapi-rest.ts and import it in your react-admin project. No need to npm install another dependency :)

// App.tsx

import raStrapiRest from "./ra-strapi-rest";

If you prefer to add this to node modules, go ahead and run the following command

npm install ra-strapi-rest

or

yarn add ra-strapi-rest

Then import it in your App.tsx as usual

import raStrapiRest from "ra-strapi-rest";

Example

import { fetchUtils, Admin, Resource } from "react-admin";
import { ArticleList } from "./pages/articles/articleList";
import raStrapiRest from "./ra-strapi-rest";

const strapiApiUrl = "http://localhost:1337/api";

const httpClient = (url: string, options: any = {}) => {
  options.headers = options.headers || new Headers({ Accept: "application/json" });
  options.headers.set("Authorization", `Bearer ${import.meta.env.VITE_STRAPI_API_TOKEN}`);
  return fetchUtils.fetchJson(url, options);
};

export const dataProvider = raStrapiRest(strapiApiUrl, httpClient);

const App = () => (
  <Admin dataProvider={dataProvider}>
    <Resource name="articles" list={ArticleList} />
  </Admin>
);

export default App;

ArticleList Component:

import { List, Datagrid, TextField } from "react-admin";

export const ArticleList = () => (
  <List hasCreate hasEdit filters={articleFilters}>
    <Datagrid rowClick="show">
      <TextField source="title" />
      <TextField source="published_date" label="Publish date" />
    </Datagrid>
  </List>
);

Check out this demo for detailed reference => Demo

ra-strapi-rest's People

Contributors

daugsbi avatar ggarcia92 avatar jeremyrobert avatar nazirov91 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ra-strapi-rest's Issues

ReferenceField not displaying data in List view

Hello, I have a model with Accounts and Projects. The relation is one account can have many projects and one project is related to one account.

This is working fine in creation, updating and showing views but not in List view. The code is the same for Show and List:

          <ReferenceField source="account" reference="accounts" label="Account">
            <TextField source="name" />
          </ReferenceField>

Also, I have a filter by Account in the List view and it's properly working.

Any idea why the reference field is not showing data in the list view?

TypeError: _dataProvider_server_rest__WEBPACK_IMPORTED_MODULE_3__.strapiDataProvider.getOne is not a function

I am trying to perform a simple fetch with strapi rest. I am able to fetch the data with Resource from react-admin but when i want to fetch a single record with custom query or a getList in useEffect i am not able to do so

import simpleRestProvider from 'ra-strapi-rest';

const strapiDataProvider = simpleRestProvider(
  'http://localhost:1337'
);

 useEffect(() => {   
    strapiDataProvider
      .getList('customers')
      .then((data: any) => {
        console.log('data: ', data);
      })
      .catch((error: any) => {
        console.log('error: ', error);
      });
  }, []); 

I am getting following error

TypeError: dataProvider_server_rest__WEBPACK_IMPORTED_MODULE_3_.strapiDataProvider.getList is not a function

I have also tried to get a single record

 useEffect(() => {
    strapiDataProvider
      .getOne('customers', { id: 61 })
      .then((data:any) => {
        console.log('data: ', data);
      })
      .catch((error: any) => {
        console.log('error: ', error);
      });
  }, []);

I am getting same error for the above as well

TypeError: dataProvider_server_rest__WEBPACK_IMPORTED_MODULE_3_.strapiDataProvider.getOne is not a function

What am i doing wrong?

Error for creating ressource

Hello,

Im using your provider for strapi and when i want to create a ressource i got an error from strapi because "data" is missing from request
Capture d’écran 2023-05-19 à 10 14 02
Capture d’écran 2023-05-19 à 10 14 13

I updated your provider for making working it by updating this line

Capture d’écran 2023-05-19 à 10 15 37

By this one

Capture d’écran 2023-05-19 à 10 15 59

And all working well. But i really dont know if im doing good ...

Missing ctx.query parameter in <Model>.count

I noticed that using the code as it-is from the Example will throw an error in Strapi console

TypeError: Cannot read property 'source' of undefined

I fixed the error by sending the ctx.query as a parameter in the count method plus using the fully qualified class name as below:
ctx.set('Content-Range', await strapi.services.<Model>.count(ctx.query));

My Node/Strapi : Version: 3.0.0-alpha.25.2 (node v11.1.0)

Unable to show data in ArrayField

Hello, I'm using and ArrayInput like this:

     <ArrayInput source="webs">
        <SimpleFormIterator>
          <TextInput source="name" />
          <TextInput source="web" />
        </SimpleFormIterator>
      </ArrayInput>

In Strapi I have a JSON field to store this data. When I save in react admin the data in Strapi is being saved this way:

[
  {
    "name": "coolName",
    "web": "coolUrl"
  }
]

In react admin it recognizes there is a record or more than one but the information is not showing in any of the views.

Show view:

          <Datagrid>
            <TextField source="name" />
            <UrlField source="web" />
          </Datagrid>
        </ArrayField>

Any idea why the data is not displaying correctly?

Has and belongs to many relation

Hello!

I'm facing another problem. In this case with the Has and belongs to many relation from Strapi.

Example, I have an entity called users and an entity called projects. Users are assigned to multiple projects and a project has many users assigned.

This is supposed to be working in react admin with something like:

  • In create/edit:
<ReferenceArrayInput
          source="project"
          reference="projects"
          label="Projects"
        >
          <SelectArrayInput optionText="name" label="Choose project" />
        </ReferenceArrayInput>
  • In show view:
      <ReferenceArrayField source="project" reference="projects">
        <SingleFieldList>
          <ChipField source="name" />
        </SingleFieldList>
      </ReferenceArrayField>

I've tried on source adding .id, .ids, _ids but is not working. Any idea why? Thanks!

Handle Strapi Components Show And Update

When A schema from strapi includes a 'component' field; It will be replaced with IDs hence the 'show' and 'Edit' will fail to show proper data. (since components do not have 'crud's in strapi)

I had To Do Something Like This:

    const replaceRefObjectsWithIds = json => {
    	Object.keys(json).forEach(key => {
	    const fd = json[key]; // field data
	    const referenceKeys = [];
	    if (fd && (fd.id || fd._id) && !fd.mime) {
                json[key] = fd.id || fd._id;
	    } else if (Array.isArray(fd) && fd.length > 0 && !fd[0].mime) {
                fd.map(item => referenceKeys.push(item.id || item._id));
                if(key != "KEY_IN_DOCUMENT_REFERS_TO_AN_ARRAY_OF_COMPONENTS") { // HERE -----------
                    json[key] = referenceKeys;
                }
	    }
	});

To Avoid component values being replaced with IDs

ImageInput causes CastError: Cast to ObjectId failed for value "[]" at path "avatar"

This issue was already created, but I didn't get response so I created this new thread.

#24

Please see my response

@nazirov91 I am running into this problem as well, it goes away when I change this to

        if(existingFileIds.length === 1){
            data[fieldName] = existingFileIds[0]; 
        }else if (existingFileIds.length > 1) {
            data[fieldName] = [...existingFileIds];
        }

I am not sure that this will play nice in all scenarios tho, maybe you have more insight on this.

Single-Type Support

Single-Type

What is the Behaviour of single-type resource

  1. Is it Possible to extend the dataProvider for special cases?
    2.It is very curricial for Strapi user , it is used commonly for app settings
    del

Suggestion: Replace Content-Range

Hi @nazirov91
Thanks for creating this plugin and maintaining it.

This is a follow up question on issue #9.
Do you have an example with a middleware to implement the content-range behaviour for all routes?

Another option could be to request the total with the count route:
strapiAPI/MY-MODEL/count
An additional request could be worth it and it shouldn't be too complicated to implement 👍

What do you think?

Is pagination work ?

Hello :)

I try to use to very great module with react-admin and strapi. But i've question about Pagination, because my Content-Range is present in my header, but the pagination don't show the correct count and button next/prev.

I've try with static count (ex. 500), but same result.

Do you know if we need other setting ?

Thank you !

Relationship

I try using Relationship, but I don`t get category date

I have a api to get all categories

http://localhost:1337/categories

The dataProvider threw an error. It should return a rejected Promise instead.

Hi!
I am trying to load images following the instructions from readme.md
And when I pass field name to the data provider I am getting the following error: Unhandled Rejection (Error): The dataProvider threw an error. It should return a rejected Promise instead.
error

// <strapi_project>/api/dish/models/dish.settings.json
...
"image": {
"model": "file",
"via": "related",
"allowedTypes": [
"images"
],
"plugin": "upload",
"required": false,
"pluginOptions": {}
},
...
model_settings

in App.js:
const uploadFields = ["image"];
const dataProvider = simpleRestProvider(baseUrl, uploadFields);

app js

Add example for Relations to the Readme

I fail to understand if this Data Provider is supposed to work with Strapi Realtions and React-Admins ReferenceManyField or ReferenceArrayField - can you please add examples to the ReadMe for those use cases ?

For my specific issue:

I have 2 collections Users and Groups - with a one user to many groups relation field in strapi (reference field is called re_groups in users and re_users in groups)

if i use a 1:1 relation this works without an issue

<ReferenceField source="re_users.id" reference="users"> <TextField source="username" /> </ReferenceField>

but if i use a ReferenceManyField target seems to be ignored or i simply dont understand what the target is supposed to be:

according to https://marmelab.com/react-admin/Fields.html#reference-fields

accepts a reference attribute, which specifies the resource to fetch for the related record. It also accepts a source attribute which defines the field containing the value to look for in the target field of the referenced resource. By default, this is the id of the resource (post.id in the previous example).

<ReferenceManyField label="Comments by" reference="users" target="???"> <SingleFieldList> <ChipField source="username" /> </SingleFieldList> </ReferenceManyField>

same goes for the ReferenceArrayField - basic examples with the relationship field of strapi should get added.

thank you!

Fails with current version of strapi and react admin

I am not sure if this is going to work anymore cause i see the post and get request it is trying to call /count and gets 404. also it does not automatically get all the fields. So were do i find the updated one ?

Get Entire File Url

By Default, when strapi is returning the url to an image it only returns beginning with the "/uploads/[id]". Is there anyway to get the entire url to return so that we can use the ImageField Elements in ReactAdmin?

File Upload

Hi is is possible to handle file uploading to Strapi for some records in the future? I have tried using FormData and FileInput from react-admin but was not able to produce successful result. Thanks

ImageInput causes CastError: Cast to ObjectId failed for value "[]" at path "avatar"

I am working on a small example application to get familiar with Strapi and React-Admin and am attempting to upload an image using the ImageInput component. I feel like I've read your documentation at https://github.com/nazirov91/ra-strapi-rest#file-upload and React-Admins at ImageInput at https://marmelab.com/react-admin/Inputs.html.

Right now I only have a single field that allows file upload so my App.js has:

const uploadFields = ["avatar"];

And is being passed in verbatim as what you've written in the documentation:

const dataProvider = simpleRestProvider('http://localhost:1337', httpClient, uploadFields);

For my create screen I also have your code verbatim as:

<ImageInput source="avatar" label="Avatar" accept="image/*">
     <ImageField source="url" title="name" />
</ImageInput>

Inside of my person.settings.json the field is referenced as:

"avatar": {
  "model": "file",
  "via": "related",
  "allowedTypes": [
	"images"
  ],
  "plugin": "upload",
  "required": false
},

Whenever I hit save on the form I see the following error logged from the server:

error CastError: Cast to ObjectId failed for value "[]" at path "avatar"
at new CastError (server\node_modules\mongoose\lib\error\cast.js:39:11)
at ObjectId.cast (server\node_modules\mongoose\lib\schema\objectid.js:246:11)

When I inspect the post request I do see it sending an empty array as the field which makes me think that's likely where the error is coming from, the POST request is:

-----------------------------3577480103102910508985816256
Content-Disposition: form-data; name="data"

{"fullname":"Bob Smith","avatar":[]}
-----------------------------3577480103102910508985816256--

I think this is an issue with how the uploaded files are being mapped to the fields but first wanted to see if you saw anything I had missed. I also wanted to thank you for providing the ra-strapi-rest as it did save me a ton of time in getting an example application up and running with React-Admin

Internal Server Error

I have a find permission on my strapi model for public, I did go through all the steps for strapi, added the content-range and replaced the cors with provided code.
I also imported the strapi-provider file in App.js and passed the URL both localhost:1338 and http://localhost:1338. My endpoint is seller-subscriptions and I passed in the Resource but I got the "An internal server error occurred".
Thanks

N/A

UPDATE for the admin:

Please delete this issue, I found the fix, I forgot to add the 'http' client, and this had nothing to do with what I asked. Thanks.

updated/alt auth example

Thanks a lot for the work you put into this, it's making my life a lot easier for building a small internal app. When it came to following your auth provider, the examples were slightly off for the versions I was running. For example, the user's plugin directory structure has changed ever so slightly

I'm using:
Strapi - 3.0.0-beta.19.3
React-Admin - 3.3.0

I was able to get it working with

//  App.js
const httpClient = (url, options = {}) => {
  if (!options.headers) {
    options.headers = new Headers({ Accept: 'application/json' })
  }
  const token = Cookies.getCookie('token') // <-- Change localstorage to the cookie helper
  options.headers.set('Authorization', `Bearer ${token}`)
  return fetchUtils.fetchJson(url, options)
}
// authProvider.js
import Cookies from './helpers/Cookies'

export default {

    login: ({ username, password }) => {
        const identifier = username // <-- Changing username to match Strapi's identifier requirement
        const request = new Request('http://localhost:1337/auth/local', {
            method: 'POST',
            body: JSON.stringify({ identifier, password }),
            headers: new Headers({ 'Content-Type': 'application/json'})
        });
        return fetch(request)
            .then(response => {
                if (response.status < 200 || response.status >= 300) {
                    throw new Error(response.statusText);
                }
                return response.json();
            })
            .then(response => {
                Cookies.setCookie('token', response.jwt, 1);
                Cookies.setCookie('role', response.user.role.name, 1);
            });
    },

    logout: () => {
        Cookies.deleteCookie('token');
        Cookies.deleteCookie('role');
        return Promise.resolve();
    },

    checkAuth: () => {
        return Cookies.getCookie('token') ? Promise.resolve() : Promise.reject();
    },

    checkError: ({ status }) => {
        if (status === 401 || status === 403) {
            Cookies.deleteCookie('token');
            Cookies.deleteCookie('role');
            return Promise.reject();
        }
        return Promise.resolve();
    },

    getPermissions: () => {
        const role = Cookies.getCookie('role');
        return role ? Promise.resolve(role) : Promise.reject();
    },
}

This may just fit my use case, but just wanted to make sure to help others if they come across this issue. Let me know if you would like this issue as a pull request instead.

The Content-Range header is missing in the HTTP Response

after login using it

import React from 'react';
import authProvider from './authProvider';
//import dataProvider from './dataProvider';
import { fetchUtils, Admin, Resource ,ListGuesser} from 'react-admin';
import simpleRestProvider from './helpers/rest.strapi';
import { apiUrl  } from './config';

const httpClient = (url, options = {}) => {
  if (!options.headers) {
      options.headers = new Headers({ Accept: 'application/json' });
  }
  const token = localStorage.getItem('token');
  options.headers.set('Authorization', `Bearer ${token}`);
  return fetchUtils.fetchJson(url, options);
}

const dataProvider = simpleRestProvider(apiUrl, httpClient);

const App = () => (
  <Admin authProvider={authProvider} dataProvider={dataProvider}>
      <Resource name="products" list={ListGuesser} />
  </Admin>
);

export default App;

display it

The Content-Range header is missing in the HTTP Response. The simple REST data provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare Content-Range in the Access-Control-Expose-Headers header?

Handle Strapi validation errors

I'm using Strapi for the backend and ra-strapi-rest dataProvider in react-admin but I'm experiencing some issues.

For instance, Strapi sends a ValidationError this way:
{"statusCode":400,"error":"Bad Request","message":"ValidationError","data":{"errors":{"members":["members must be greater than or equal to 1"]}}}·

This results in a single error Notification on react-admin which only displays: "ValidationError" instead of a more descriptive error or errors.

Any ideas on how to fix this?

Example doesnt work with fetchAll, use find instead

In the readme example, using fetchAll doesnt work. Use find instead. e.g

find: async (ctx) => {
  ctx.set('Content-Range', await <Model_Name>.count());
  if (ctx.query._q) {
    return strapi.services.<Model_Name>.search(ctx.query);
  } else {
    return strapi.services.<Model_Name>.find(ctx.query);
  }
},

No edit / or show buttons?

What am i missing here? i've defined the "create", "edit" and "show" functions, yet no buttons appear in the list at all. if i manually enter the url's for these routes they work as expected, but nothing appears in the list view to access these? Using Strapi 3.6.8 on the latest version of React Admin.

User model CORS error

Hello,

In the README, you state that we must update the controller for each model we have, overriding the async find method in the controller file. However, Since the user model is automatically included with Strapi, there is no controller file for the user model. How do you suppose we get rid of the cors error associated when trying to access the user data.

What should the settings be in AuthProvider to connect to Strapi?

Thank you for a great effort. Just what I was looking for. Could you give a tip on what settings should be in the authProvider to connect to Strapi?
Tried the following but it doesn't seem to work:

if (type === AUTH_LOGIN) {
        const { username, password } = params;
        const request = new Request('http://localhost:1337/auth/local', {
            method: 'POST',
            body: JSON.stringify({ username, password }),
            headers: new Headers({ 'Content-Type': 'application/json' }),
        })
        return fetch(request)
            .then(response => {
                if (response.status < 200 || response.status >= 300) {
                    throw new Error(response.statusText);
                }
                return response.json();
            })
            .then(({ token }) => {
                localStorage.setItem('token', token);
            });
    }
    return Promise.resolve();

Data provider for strapi 3.0.0 beta version

How to use this data provider for strapi's beta version as using this data provider requires us to add a Content-Range header in the strapi's controller's plugin which is not provided in the beta version.
In beta version of strapi the controller's and service's are empty for any plugin we install externally.

Data UPDATE does not work correctly

Strapi refuses to update data due to the created_at and updated_at fields in the request. A possible solution would be to remove these fields from params.data. Or you can limit the updated data in the react-admin itself?

Problem code

case UPDATE:
                url = `${apiUrl}/${resource}/${params.id}`;
                options.method = 'PUT';
                delete params.data["created_at"];
                delete params.data["updated_at"];
                options.body = JSON.stringify(params.data);
                break;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.