question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Serve Simple Page App (angular) on root alongside a backend on starlette

See original GitHub issue

Hello, I’m developing an angular (7)+ starlette api backend and I would like to make starlette serve the angular app alongside with the api. I know that I could use ngnix to serve it but I would like to try to deploy just one server. Also I have found the https://github.com/tiangolo/uvicorn-gunicorn-starlette-docker and it would be amazing to be able to just use it with the whole app. As a normal angular app, it expects (by default) to be located to the “/” endpoint. As my angular prod files are in a folder ./angular relative to the starlette app.py file I have tried the following:

_CURDIR = dirname(abspath(__file__))
app = Starlette()
app.mount('/', StaticFiles(directory=join(_CURDIR, 'angular' )))

@app.exception_handler(404)
async def not_found(request, exc):
    return FileResponse('./angular/index.html')

if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8090)

The index.html is the usual on the angular apps:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>ArcherytimerMatrix</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.54bd9f72bfb2c07e2a6b.css"></head>
<body>
  <app-root></app-root>
<script type="text/javascript" src="runtime.253d2798c34ad4bc8428.js"></script><script type="text/javascript" src="es2015-polyfills.4a4cfea0ce682043f4e9.js" nomodule>
</script><script type="text/javascript" src="polyfills.407a467dedb63cfdd103.js"></script><script type="text/javascript" src="main.cfa2a2d950b21a173917.js"></script></body>
</html>

when I try to reach localhost:8090 the index.html is “shown” but it can’t find the styles / runtime / polyfills and main

INFO: Uvicorn running on http://0.0.0.0:8090 (Press CTRL+C to quit)
INFO: ('127.0.0.1', 34138) - "GET / HTTP/1.1" 200
INFO: ('127.0.0.1', 34138) - "GET /styles.54bd9f72bfb2c07e2a6b.css HTTP/1.1" 200
INFO: ('127.0.0.1', 34140) - "GET /runtime.253d2798c34ad4bc8428.js HTTP/1.1" 200
INFO: ('127.0.0.1', 34138) - "GET /polyfills.407a467dedb63cfdd103.js HTTP/1.1" 200
INFO: ('127.0.0.1', 34140) - "GET /main.cfa2a2d950b21a173917.js HTTP/1.1" 200

(the status is 200 as I’m catching the exception and redirecting…) If I try to access the static file directy: localhost:8090/styles.54bd9f72bfb2c07e2a6b.css Is not found either

If I serve the statics files from an endpoint: app.mount('/angular', StaticFiles(directory=join(_CURDIR, 'angular' ))) and I try to access to the “angular” localhost:8090/angular/styles.54bd9f72bfb2c07e2a6b.css the file is served. So It seems that the “/” root can’t be used for endopoint to static files (or there is a bug if this is not the intended behavior)

Maybe I’m doing it wrong, is there another way to achieve this?

Thank you in advance Regards

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:22 (9 by maintainers)

github_iconTop GitHub Comments

6reactions
dmontagucommented, Jun 18, 2019

@andrewmwhite If you create your frontend and api as separate starlette apps, and then mount each of those in a single “parent” app, you should be able to adapt the approach above without any awkward explicit excludes, since the FileResponse-generating middleware will now only apply to the frontend app:

import uvicorn

from starlette.applications import Starlette
from starlette.staticfiles import StaticFiles
from starlette.responses import FileResponse


main_app = Starlette()

frontend = Starlette()
api = Starlette()


@frontend.middleware("http")
async def add_custom_header(request, call_next):
    response = await call_next(request)
    if response.status_code == 404:
        return FileResponse('docs/authentication.md')
    return response


@frontend.exception_handler(404)
def not_found(request, exc):
    return FileResponse('docs/authentication.md')


frontend.mount('/', StaticFiles(directory='tests'))


main_app.mount('/api', app=api)
main_app.mount('/', app=frontend)


if __name__ == '__main__':
    uvicorn.run(main_app, host='0.0.0.0', port=8090)

I just ran the above in a file exactly as is, in the root folder of starlette, and I get the auth docs page for any 404 that doesn’t start /api, and I get the standard mostly-blank 404 response from any route starting /api.

Would that work for you?

5reactions
kkindercommented, Dec 19, 2019

This has been answered, but if you don’t want to override 404 – for example if you’re serving your SPA in a subdirectory like /app/', this works well enough:

from starlette.staticfiles import StaticFiles


class SPAStaticFiles(StaticFiles):
    async def get_response(self, path: str, scope: Scope) -> Response:
        response = await super().get_response(path, scope)
        if response.status_code == 404:
            response = await super().get_response('.', scope)
        return response


app.mount('/app/', SPAStaticFiles(directory=spa_directory, html=True))
Read more comments on GitHub >

github_iconTop Results From Across the Web

Developing a Single Page App with FastAPI and Vue.js
The following is a step-by-step walkthrough of how to build and containerize a basic CRUD app with FastAPI, Vue, Docker, and Postgres.
Read more >
Deploying an application
This command creates a dist folder in the application root directory with all the files that a hosting service needs for serving your...
Read more >
Using Starlette to migrate my blog across domains
My idea was to have two apps: blog : serves VuePress' static files at florimond.dev/blog . index : landing page at florimond ...
Read more >
Routing
Starlette has a simple but capable request routing system. ... endpoint=homepage), Route("/about", endpoint=about), ] app = Starlette(routes=routes).
Read more >
How To Develop and Build Angular App With NodeJS
We need some kind of mechanism that loads the index.html (single page) of Angular with all the dependencies(CSS and js files) in the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found