Miserlou/Zappa

Dash Deployment: Route not found 404

adrianoesch opened this issue · 4 comments

I'd like to deploy dash, a powerful dashboard app which is built on top of flask, with zappa but i keep getting 404s. I have a flask route in the same app, which is working fine. So the deployment seems to work fine.

I have a hunch that there are conflicts between dash's and zappa's path route handling. Dash by default is on the root of the domain, while zappa puts an app on /dev. One can set a url_base_pathname in dash, but that didn't help so far.

Is there a way of emulating zappa's handler and route transformations? I'd like to fix this, but not sure how to best approach it? Any pointers are very welcome!

Details

Dash is actually adding a flask blueprint (see here) with a bunch of routes under the hood (serving static files, api endpoints etc).

I have a live app running at the moment, where the flask route is working as expected (here), but the dash route is returning 404 (here)

Related:

App:

from flask import Flask
from dash import Dash
import dash_html_components as html
from dash.dependencies import Output, Input


dash = Dash(__name__,
    url_base_pathname = '/dev/dash/'
)
flask = dash.server
flask.secret_key = 'notsosecret'

dash.layout = html.Div([
    html.H1('Hello Dash!'),
    html.Button('Click me!',id='button'),
    html.Div(id='response')
])

@flask.route('/flask')
def home():
    return 'OK'

@dash.callback(Output('response','children'),[Input('button','n_clicks')])
def click(n_clicks):
    return f'{n_clicks if n_clicks else 0} times clicked'

if __name__ == '__main__':
    flask.run()

Environment

Using Zappa v0.50.0 (because of a unicode error with werkzeug) on a mac with python3.8:

argcomplete==1.12.2
boto3==1.16.57
botocore==1.19.57
Brotli==1.0.9
certifi==2020.12.5
cfn-flip==1.2.3
chardet==4.0.0
click==7.1.2
dash==1.19.0
dash-core-components==1.15.0
dash-html-components==1.1.2
dash-renderer==1.9.0
dash-table==4.11.2
durationpy==0.5
Flask==1.1.2
Flask-Compress==1.8.0
future==0.18.2
hjson==3.0.2
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.2
jmespath==0.10.0
kappa==0.6.0
MarkupSafe==1.1.1
pip-tools==5.5.0
placebo==0.9.0
plotly==4.14.3
python-dateutil==2.6.1
python-slugify==4.0.1
PyYAML==5.4.1
requests==2.25.1
retrying==1.3.3
s3transfer==0.3.4
six==1.15.0
text-unidecode==1.3
toml==0.10.2
tqdm==4.56.0
troposphere==2.6.3
urllib3==1.26.2
Werkzeug==0.16.1
wsgi-request-logger==0.4.6
zappa==0.50.0
  • Your zappa_settings.json:
{
    "dev": {
        "app_function": "app.flask",
        "aws_region": "eu-west-1",
        "profile_name": "default",
        "project_name": "zappa-test2",
        "runtime": "python3.8",
        "s3_bucket": "zappa-n8kygzzt0"
    }
}

Have the exact same issue as you, spent multiple days trying to fix it, downgrading zappa to version 0.50 fixed this for me per this link:
plotly/dash#1191

Also, try updating the constructor using 'requests_pathname_prefix' instead
dash = dash.Dash(__name__, requests_pathname_prefix='/dev/')

Thanks @jjjchens235! I was using version 0.50.0 of zappa already and also experimented with various requests_pathname_prefix parameters but couldn't get it to work. It might have been due to the additional "flask" route that might have been interfering with the dash routes. I eventually started using a custom domain instead, so the stage path wasn't an issue anymore.

Glad to hear you got it working. I don't think the the additional flask route was the issue because I got your example to work.

I'm on the opposite end with this- trying to get my lambda deployment hosted on a custom domain via AWS Route 53/ API Gateway, but no luck so far.

Edit:
Below is to help anyone that encounters any issues in the future, this was incredibly frustrating to solve for me, so I hope it saves someone us the frustration.

I have gotten both the Lambda/API Gateway deployment to work using Zappa, as well as custom domain name to work using AWS Route 53.

The main thing to understand as @adrianoesch pointed out is that Dash is on the root of the domain, and Zappa uses /dev/. The custom domain also uses the root of the domain.

LocalHost
So to get Dash to work on the localhost using the root of the domain, all you have to do is

if __name__ == '__main__':
    app = dash.Dash(__name__)
    app.run_server(debug=True)

Zappa
To get Dash to work using Zappa, you have to add dev to the root of the domain, like so

if __name__ == '__main__':
    app = dash.Dash(__name__)
    app.config.update({
       'requests_pathname_prefix': '/dev/'
   })
    app.server.secret_key = <MY_SECRET>
    server = app.server

Don't forget that Zappa needs to be at version 0.50. plotly/dash#1191

Custom domain
Now, to get Dash to work on a custom domain, since the API Gateway URL is tacky, I used Route 53. This was the best tutorial I found: https://www.youtube.com/watch?v=jZ7IWmmkHKM&ab_channel=WornOffKeys

And here's a great article:
https://romandc.com/zappa-django-guide/walk_domain/#step-11-create-a-hosted-zone-in-route53

You need to get the custom domain (has to be a subdomain actually i.e api.example.com) to point to the Lambda API Gateway. The tricky part that I was stuck on forever is that you have to be at the root of the domain, just like the localhost.

So we need to revert the code back to

if __name__ == '__main__':
    app = dash.Dash(__name__)
    app.run_server(debug=True)

The API Gateway will no longer work (mine showed stuck on loading), but the custom domain should now work.

I'm really confused about this as I have yet another different behaviour.

If I use this

app = dash.Dash(__name__, url_base_pathname='/dev/')

then the URL created by the api gateway doesn't work: if I go to url_api_gateway/dev/ I get a 404. Weirdly enough if I go to url_api_gateway/dev/dev/ then the page first loads but of course it is stuck as it doesn't find the resources in /dev/dev/.

If I try

app = dash.Dash(__name__, url_base_pathname='/')

then I only get 404 on url_api_gateway/dev/ and nothing else on other combinations.

The only way that I can solve this is to use a custom domain name with the configuration of the first snippet ( url_base_pathname='/dev/') and the mapping to custom_domain_name/dev/ and then Dash loads without problems. Which means that basically I can only use this with custom domain names :(

That would seem to contradict what you posted last time @jjjchens235... I have no clue why