Search code examples
reactjswebsocketfastapireact-admin

Connect FastAPI and react-admin via websocket


I'm kinda new with react and react-admin. The task is pretty simple, it is to connect FastAPI server with react-admin app via websocket. I think I have to create websocket dataProvider somehow.

for react-admin App.tsx


export const App = () => (

    <Admin
        dataProvider={dataProvider}
    >
        <Resource name="" list={} show={} />
    </Admin>
);

dataProvider.tsx

import jsonServerProvider from 'ra-data-json-server';

const URL = 'http://127.0.0.1:8000/'


export const dataProvider = jsonServerProvider(
    URL
);

When I simple change http to ws it gives an error url scheme 'ws' is not supported because it use fetch(), and fetch() work with http\s only.

I think there is no need of my FastAPI code but whatever:

@router.websocket('/ws/send-data/')
async def ws_send_users(websocket: WebSocket):
    print('2 in ws_send_users')
    await websocket.accept()
    while True:
        await websocket.send_text('text1')
        await asyncio.sleep(0.25)


@router.websocket('/ws/send-data/{user_inner}')
async def ws_send_user(websocket: WebSocket,
                       user_inner: str = None):
    print('1 in ws_send_user')

    await websocket.accept()
    while True:
        if not user_inner:
            return await ws_send_users(websocket)
        await websocket.send_text('text2')
        await asyncio.sleep(0.25)

With default http it works just perfect: ws_send_user is called when we want to show user in react-admin, and ws_send_users is called when we want to list users

this code creates websocket connection and gets events just fine but I think there is something wrong with it. Anyway IDK how to connect it with react-admin interface (list and show options).

export const App = () => (

    <Admin
        dataProvider={useEffect(() => {
            const WS = new WebSocket(`ws://127.0.0.1:8000/ws/send-data/`);
            WS.onmessage = e => {
                console.log('el', e.data)
                e.data && !(USERS.includes(e.data)) ? USERS.push(e.data) : null;
            }

        })}
    >
        <Resource name="" list={} show={} />
    </Admin>
);

the purpose is to get list of users to list and each separate user to show by some react-admin magic, so please if you're experienced with it help me.


Solution

  • Ok, I think I've connected it. Since I'm newbie with JS and React, I would like to impore the quality of the code

    class WebSocketDataProvider {
        constructor() {
            this.apiUrl = `ws://yourhost:yourport/ws/send-data/`;
            this.socket = new WebSocket(this.apiUrl);
            this.socket.addEventListener('open', this.handleSocketOpen);
            this.socket.addEventListener('message', this.handleSocketMessage);
            this.socket.addEventListener('error', this.handleSocketError);
            this.socket.addEventListener('close', this.handleSocketClose);
        }
    
        handleSocketOpen = () => {
            console.log('WebSocket connection is open');
        };
    
        handleSocketMessage = (event) => {
            this.getList(event);
        };
    
        handleSocketError = (event) => {
            console.log('HandleSocketError', event)
        };
    
        handleSocketClose = (event) => {
            console.log('HandleSocketClose', event)
        };
    
        getList = (event) => {
    
            return new Promise((resolve, reject) => {
                const handleResponse = (event) => {
                    const parsedResponse = JSON.parse(event.data);
                    let response = {
                        data: [],
                        total: Object.keys(parsedResponse).length,
                    }
                    Object.entries(parsedResponse).forEach((key, value) => {
                        let obj = {
                            id: key[0],
                            urls: key[1],
                        }
                        response.data.push(obj);
                    });
                    resolve(response);
                    this.socket.removeEventListener('message', handleResponse);
                }
                this.socket.addEventListener('message', handleResponse);
            });
        };
    
        getOne = (event) => {
    
            return new Promise((resolve, reject) => {
                const handleResponse = (event) => {
                    const parsedResponse = JSON.parse(event.data);
                    const href = location.href.split('/');
                    const length = href.length
                    const userId = href[length - 2]
                    let response = {
                        data:
                        {
                            id: userId,
                            urls: parsedResponse.userId,
                        }
                    }
    
                    resolve(response);
                    this.socket.removeEventListener('message', handleResponse);
                }
                this.socket.addEventListener('message', handleResponse);
            });
        };
    }
    
    const dataProvider = new WebSocketDataProvider;
    export default dataProvider;