Client side data from external source

Hello!

I am trying to figure out if there is a way to get data from an external url (that returns a json), but with the data fetched by the client side and then returned to the dash app.

Here is an example that uses data of an external API to make a simple plot. I have commented a working serverside solution, but I need to do the API query on the client side, which I cannot make it work.

import pandas as pd
import numpy as np
import plotly.graph_objects as go

import dash
import dash_core_components as dcc
import dash_html_components as html

from dash.dependencies import Input, Output, State, ClientsideFunction
from dash.exceptions import PreventUpdate


dash_app = dash.Dash(__name__)
server = dash_app.server

def makeplot(df):
    if df is None:
        X=[]
        Y=[]
        name=''
        tt=''
    else:
        X=df['Date']
        Y=np.log10(df['Cases'])
        name='Covid_{0}'.format(df['CountryCode'][0])
        tt=df['Country'][0]
    fig = go.Figure()
    fig.add_trace(go.Scatter(x=X, y=Y,
                        mode='lines',
                        name=name))
    fig.update_layout(title=tt)
    return fig

dash_app.layout = html.Div(
    id='app-body',
    children=[
        html.Div(dcc.Input(id='input-on-submit', type='text')),
        html.Button('Submit', id='submit-val'),
        html.Div(dcc.Graph(figure=makeplot(None), id='plot',config={'scrollZoom': True})),
        dcc.Store(id='store_clientside',data=['123'])
    ]

)
############################
#Working server-side option#
############################
# @dash_app.callback(
#     Output('plot','figure'),
#     [Input('submit-val', 'n_clicks')],
#     [State('input-on-submit','value')])
# def update_data(n,value):
#     if value is None:
#         raise PreventUpdate
#     url='https://api.covid19api.com/dayone/country/{0}/status/confirmed'.format(value.lower())
#     df=pd.read_json(url)
#     return makeplot(df)
############################

############################
#####Clientside attemp #####
############################
dash_app.clientside_callback(
    ClientsideFunction(
        namespace='clientside',
        function_name='test_url_fetch'
    ),
    Output('store_clientside', 'data'),
    [Input('submit-val', 'n_clicks')],
    [State('input-on-submit','value')]
)

@dash_app.callback(
    Output('plot','figure'),
    [Input('store_clientside', 'data')])
def update_data(data):
    if data is None:
        raise PreventUpdate
    df=pd.read_json(data)
    return makeplot(df)
############################

if __name__ == '__main__':
    # dash_app.run_server(debug=True)
    dash_app.run_server(host='0.0.0.0', port=8080, debug=True)

and the .js

if(!window.dash_clientside) {window.dash_clientside = {};}
window.dash_clientside.clientside = {
	test_url_fetch: function (n,country){
		console.log(n,country)
		return fetch('https://api.covid19api.com/dayone/country/' + country + '/status/confirmed')
	}
}


I understand that dash clientside does not handle promises, so this might be a dead end and that is ok, I was just wondering if there is a workaround, or something that could be done in the javascript side that I am missing.

For completeness, this is the error I get on the browser console:

The clientside function clientside.test_url_fetch returned a Promise instead of a value. Promises are not supported in Dash clientside right now, but may be in the future.

Many thanks for the help!

1 Like

I wanted this feature as well…
It would help to take a lot of the load from the server…

But I am stuck same as you… Guess we have to hope they implement this feature.

I created an Issue for this feature in the project’s github.

Try commenting on it so we can get more visibility for this feature.

If you are rendering the plots on the server anyway, is there any benefit in fetching the data client side?

How would I render an updated graph on the clientside after also retrieving the data on the clientside?

Some alternatives to waiting :smiley::

  • You can implement this feature yourself by making a pull request into the project! Most of the clientside callbacks improvements have been made by community members. We will review your work in a friendly and timely manner :slight_smile:
  • You can fund our development to get this feature on our immediate roadmap: Consulting, Training & Open-Source Development

This is a good point - You could fetch the data in Python and then store that data in a dcc.Store. Then, all of your updates thereafter could be made clientside.

Hi guys tank you for the replies!
@Emil If we are talking about just one user using your app, then there isn’t a great benefit to it… But if we are talking about hundreds/thousands of users simultaneously using the app we are putting the server in a lot of load.
Especially if it is something that needs to be updated in shorts intervals of time.

Well I can try looking into it… in my free time. Any pointers on were to start looking??

I am not sure that you are right. If you are rendering the figure on the server, i don’t see why it should be any faster fecthing the data client side and then sending them to the server as compared to fetching the data to the server directly. If you are rendering the figure clientside, it is of course a completely different story.

If multiple clients would need the same data, the server side fetching solution could be made even more performant by caching the data and reusing them across clients.

In my particular use case it’s user specific data, each user as it own data. That needs to be fetch from an external server.
I can see your point if the data was the same for all users. But that is not the case for me.

And I can also see what you mean in terms of speed. But imagine that you max or are closing to max out your server resources (e.g cpu, memory, etc…). The requests between the clientside and the server will take longer to be resolved.

Performing external calls in the clientside would allow to offload some of that load to the clients.
Imagine you have 1000 clients connected to your dash server. The server has to perform 1000 requests and wait for the responses + render the layout.

While if it was in the clientside each user would perform 1 request to the external server and the server only needs to render the layouts with the information received.

I have managed to do this with XMLHTTP in javascript. Now fetching data is made on client side and rendering on the server (maybe later on i will migrate the rendering into client aswell, let’s see how it goes):

.js file:

if(!window.dash_clientside) {window.dash_clientside = {};}
window.dash_clientside.callback_filters = {
    get_vertical: function (country){
        const request = new XMLHttpRequest();
        request.open('GET', 'some_url', false); // true creates a promise, so it wont work
        request.setRequestHeader('authorization', 'Bearer {API_KEY}');
        request.send();
        const vertical = [];
        for (const key of JSON.parse(request.response).filters) {
              vertical.push({'label': key, 'value': key})
         }
        // console.log(vertical);

        return vertical;
    }
}