3

I've spent a fair amount of time reading through Stack Overflow questions and can't find what I'm looking for.

I'm creating a Flask API and React frontend to be deployed on GAE. My directory structure looks like this:

application_folder
-> api
-> -> app.yaml 
-> -> main.py
-> react_frontend
-> -> app.yaml
-> -> {directories after using create-react-app}

For development

1.) The package.json of the React app, I've set a proxy:

  "proxy": "http://localhost:5000/"

My React frontend is running on port 3000.

2.) I fetch within the React frontend's App.js file as such:

fetch('api/endpoint')

Works like a dream.

For Production

However, when deploying to GAE, I have to make the following changes:

1.) Remove proxy from package.json. I couldn't find a way for this to work with a proxy as I received a 404 error on the React frontend on production.

2.) Add Access-Control-Allow-Origin to Flask API in main.py.

@app.route("/api/endpoint", methods=["GET"])
def endpoint():
    resp = make_response({"cat": 15})
    resp.headers["Access-Control-Allow-Origin"] = "*"
    return resp

3.) Now, I have to fetch with absolute path from App.js.

fetch('flask-production-domain.com/api/endpoint')

My question is, would you recommend a way of deploying to production/setting up local development such that I don't have to rewrite the code base when I deploy to production?

By the way my app.yaml files read as such:

# api app.yaml
runtime: python38

env_variables:
  none_of: "your_business"
# frontend app.yaml
runtime: nodejs12
service: banana

handlers:
  - url: /static
    static_dir: build/static
  - url: /(.*\.(json|ico|js))$
    static_files: build/\1
    upload: build/.*\.(json|ico|js)$
  - url: .*
    static_files: build/index.html
    upload: build/index.html

Thanks in advance.

EDIT

I've accepted dishant's answer, but I've done it slightly differently than they've suggested.

Firstly, these two resources are incredible. This guy wrote "the book" on Flask:

In the second video, he describes two methods of deploying. The first one is similar to what dishant suggested. In the video Miguel describes this option as his lesser favorite of the two he describes, as serving the files from python is slow. For me, it works well for now, as it's the "easier" of the two, and it seems like you can easily switch down the road.

So, I've made the following changes:

  1. I changed the directory structure to:
Top Level
-> directories after using create-react-app
application_folder
-> api
-> -> app.yaml 
-> -> main.py
  1. Instead of creating the routing in the app.yaml file, I added the following to the main.py fie:
app = Flask(__name__, static_folder="build", static_url_path="/")


@app.route("/")
def index():
    return app.send_static_file("index.html")
  1. I added the following line to the package.json to create a script to build and move the static files to the api directory.
"scripts": {
  "start": "react-scripts start",  
  "build": "react-scripts build",
  "create-app": "yarn build && rm -r api/build && cp -r build api/build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
},

So now I run yarn create-app to build.

I'm going to look into Travis for CI/CD, since I've used that before.

big-c-note
  • 58
  • 5

2 Answers2

0

If you want to use proxy within your deployment, you don't have many options indeed. For example, most of the situations where you need to use proxy with App Engine, it's needed to use an instance on Compute Engine, so you can set static IP and configure better it - as explained in this case here.

Another option would be to deploy your API's backend code with Extensible Service Proxy (ESP to an App Engine Flex environment, using Cloud Endpoints. As explained here, you can configure like this, so ESP can obtain your API's Endpoints configuration, which allows ESP to proxy requests and responses so that Endpoints can manage your API.

These are some different approaches that might give you an idea of how to do it, working more as a starting point than as a solution. However, you are kind of limited on your options here, as your use case is specific and GAE has some specific settings available as well. I would recommend you to keep doing what is working right now and then, give it a try in one of this other options.

gso_gabriel
  • 4,199
  • 1
  • 10
  • 22
0

From https://create-react-app.dev/docs/proxying-api-requests-in-development/:

Keep in mind that proxy only has effect in development (with npm start), and it is up to you to ensure that URLs like /api/todos point to the right thing in production. You don’t have to use the /api prefix. Any unrecognized request without a text/html accept header will be redirected to the specified proxy.

You would not serve your frontend from memory in a deployed environment(app engine). Instead, you should build your app and package it along with the Flask app and set routes in app.yaml to point to frontend and backend properly.

You only need 1 app.yaml file. Example:

  1. Build your react app using npm run build

  2. Copy the build files to your flask app in a new folder called build.

  3. app.yaml:

    runtime: python38
    
    env_variables:
      none_of: "your_business"
    
    handlers:
    # frontend
    - url: /static
      static_dir: build/static
    - url: /(.*\.(json|ico|js|html))$
      static_files: build/\1
      upload: build/.*\.(json|ico|js|html)$
    
    # backend
    - url: /.*
      script: auto
    

You can leverage ci/cd services like cloud build or Jenkins to package the apps and deploy them on app engine.

dishant makwana
  • 1,029
  • 6
  • 13
  • Thanks for your answer. Would you provide a directory structure of what you’re describing? I’m not sure what steps are required to package the flask and react app together. I would think they would need separate app.yaml is based on them having different runtimes. – big-c-note Oct 23 '20 at 13:47
  • An example of the routing routing the app.yaml would also help greatly. – big-c-note Oct 23 '20 at 13:51
  • React app is the front-end and it does not run on the server. It runs on the client's browser. So difference in runtimes doesn't matter. For the backend, the built app is just static html and js files – dishant makwana Oct 23 '20 at 14:36
  • Thanks dishant. I added a section to the post to ask you a clarifying question, as I don't know how to format multiple lines in comments. Is this the setup you're describing? – big-c-note Oct 23 '20 at 14:54
  • It's up in the post now, it took a minute to add it. – big-c-note Oct 23 '20 at 15:04
  • Ok, I’m picking up what you’re putting down. I like this as a solution. I’ll try it tonight before I accept your answer. Many thanks for doing the example. – big-c-note Oct 23 '20 at 17:53