Node.js + static content served by Google App Engine - node.js

The Google Cloud documentation isn't very precise on the available syntax for the app.yaml file used for my Node.js application.
I used the syntax described in the Python documentation for GAE, from which I've found the handlers mecanism:
handlers:
- url: /index.html
static_files: /public/index.html
upload: /public/index.html
I've avoid my expressjs rule to serve the /public/index.html content and finally I got a 404 error, meaning GAE is not serving my page as a static content:
$ curl -i "http://my-project/index.html"
HTTP/1.1 404 Not Found
...
Do you have any clue on this? Node.js is relevant for making APIs, generating dynamic content... But I prefer using the Google backends or even a Nginx server to handle static contents.
Update
Removing the leading slashes didn't fixed the issue. I slightly changed my app.yaml configuration:
handlers:
- url: /api/.*
secure: always
script: app.js
- url: /.*
secure: always
static_dir: public
And I still get 404 Not found on /index.html, and getting
the correct 200 OK answser when calling /api/stuff.
Here is my project tree structure:
Project root
|- app.js
|- app.yaml
|- package.json
|- public/
| `-- css/
| `-- style.css
| `-- js/
| `-- main.js
| `-- index.html

The examples at the very documentation page should normally suffice.
You have a leading / in the static_files and upload values, which should be just relative paths to the top of your app dir.
There could be other reasons as well, the starting point would be the logs for your app, either on your development server or on GAE if already deployed.
Update:
According to the Static directory handlers doc:
A static directory example:
handlers:
# All URLs beginning with /stylesheets are treated as paths to static files in
# the stylesheets/ directory.
- url: /stylesheets
static_dir: stylesheets
url
A URL prefix. This value uses regular expression syntax (and so regexp
special characters must be escaped), but it should not contain
groupings. All URLs that begin with this prefix are handled by this
handler, using the portion of the URL after the prefix as part of the
file path.
Based on this quote I'd suspect the wildcards in the url of the app.yaml's handler spec may be causing issues (for example the /index.html might actually be expanded to /index.html/ by the static_dir parsing logic), I'd replace the url to clearly indicate a directory, like this:
- url: /
secure: always
static_dir: public
I'm not a fan of tying the top level / of the app's namespace to a static dir, but it may be OK for a generally static app. Make sure you always keep this handler spec last in your app.yaml file to avoid issues.

Related

Next.js Run midddleware only for the homepage

Is there any way to execute middleware function only for the homepage (root page of the website)
I'm using the following execution order:
- /pages
- _middleware.js
- index.jsx
- /about
- index.jsx
The following _middleware.js will run for both / and for /about pages. Is it possible to run middleware only for / page?
P.S. I know that I can just check if current page path is the root of the website in the middleware itself. However, it is still going to be executed in sub-directories just without my custom checks.

GAE Python 3: Static files are not loading on localhost but do load when deployed online

I am trying to migrate a Google App Engine Standard Flask web app from Python 2.7 to Python 3. It works perfectly when deployed to appspot but I'm still experiencing issues while running a local dev server.
The issue is that when running on the local dev server my app is unable to find my static files. I get this error Uncaught SyntaxError: Unexpected token '<' and it's against my jquery-3.3.1.min.js: file. I'm reasonably sure that what's happening is that it's trying to find my JQuery file, fails, receives a 404 error html page, and throws a syntax error because it's expecting a JavaScript file and not HTML.
My JQuery file isn't the only static file affected. For example, my CSS isn't loading and my app can't find my local images.
Here are some code snippets:
app.yaml
runtime: python38
instance_class: F4
automatic_scaling:
max_concurrent_requests: 30
handlers:
- url: /_ah/start
script: auto
- url: /app_shared/app_setup/app_style/*
static_dir: app_shared/app_setup/app_style
- url: /app_shared/app_setup/app_images/*
static_dir: app_shared/app_setup/app_images
- url: /app_shared/app_static/jquery/v_3_3_1/*
static_dir: app_shared/app_static/jquery/v_3_3_1
- url: /app_shared/app_static/util/*
static_dir: app_shared/app_static/util
- url: .*
script: auto
error_handlers:
- file: app_code/templates/errors/default.htm
- error_code: timeout
file: app_code/templates/errors/408_timeout.htm
h_main_header_menu.htm (this is within the head tag)
<script src="/app_shared/app_static/jquery/v_3_3_1/jquery-3.3.1.min.js"></script>
Here is the hierarchy to the JQuery file from the root of the project: app_shared/app_static/jquery/v_3_3_1_jquery-3.3.1.min.js
To run the local dev server I follow Google's documentation here and here. So I run it with the commands venv\Scripts\activate followed by python main.py.
Does anybody know what's going wrong here? I'm at a loss. Any help here is appreciated. Thanks ahead of time.
I figured it out. I needed to rename my app_shared folder to static. You can find Flask documentation on it here

App Engine service with apollo-express-server v2.x.x serving React app statically

Rather than having an issue, this is more of a knowledge question. I've been diving deep on GAE's docs, Youtube videos and posts, on how to serve a full MERN stack app from a single App Engine service.
I honestly don't have a fine-grained understanding of what App Engine deployment does behind the scenes, but I understand that it exposes your service at port 8080 from a default route assigned (i.e. https://xxxxxxxx.uc.r.appspot.com).
Based on this knowledge, I thought it would be totally possible to statically serve a built React app and handle incoming queries from this same app through /graphql endpoint.
For instance:
This solves CORS since everything shares the same origin.
You limit yourself to a single App Engine service which allows you to remain on the free tier service :)
This is my project structure:
root/
app/
node_modules/
build/ <----- React app built
src/
App.js
index.js
...
server/ <----- In here is the Apollo Express Server v2.x.x serving app/build generated above^
index.js
package.json
node_modules/
app.yaml <---- This file tells App Engine what to do and which routes to expose
...
My app.yaml
runtime: nodejs12
env_variables:
PORT: 4000
handlers:
- url: /
static_files: app/build/index.html
upload: app/build/index.html
- url: /
static_dir: app/build
- url: /graphql
script: auto <---- Supposedly App Engine is smart enough to understand what to do with this route.
I am just not being able to query my App Engine service back from the served to React app.
Does anyone know if what I'm doing is even possible, or there's just no way to configure App Engine with Apollo Server to serve a static webapp + expose a /graphql endpoint at the same time.
Any suggestions or ideas are extremely welcome!
--------------------------------- Update ---------------------------------
After GAEfan suggestion, I updated the app.yaml file:
Yeah okay! I think this is indeed working, I just still get a few errors on the console about resources not found so, here is the internal structure of the build/
build/
static/
css/
js/
asset-manifest.json
favicon.ico
index.html
logo192.png
manifest.json
robots.txt
service-worker.js
I already added to this suggestion json|txt|map|ico resources in the same fashion.
- url: /(.*\.(gif|png|jpg|css|js|json|txt|map|ico))$
static_files: app/build/\1
upload: app/build/.*\.(gif|png|jpg|css|js|json|txt|map|ico)$
This resolved the not found resources issue. And your wildcards already resolved the /graphql endpoint. For some reason, the WebSocket handshake is not being correctly established, but I'm not sure if that's a direct limitation from App Engine, or that the protocol is blocked by default.
Does anyone have knowledge of Websockets over App Engine?
Still more questions, but I'll add this as a partial/temporary answer...
Your first 2 url handlers in app.yaml are duplicates. URL handling with match the first one, and send everything to ...index.html. I assume you have some js, css, or other static files in the build directory. Those will never be made available to the browser. So, you need to use better regex routing, with wildcards, etc.
Let's do these one at a time:
handlers:
- url: /$ # this '$' ends the match, so the url matches only the root domain
static_files: app/build/index.html
upload: app/build/index.html
Next, let's test for .js, .css, etc.:
- url: /(.*\.(gif|png|jpg|css|js))$
static_files: app/build/\1
upload: app/build/.*\.(gif|png|jpg|css|js)$
Then (assuming you have graphql working properly, you can send all other requests there:
- url: /graphql
script: auto
or even:
- url: /(.*) # catches everything else!
script: auto
Show the files or directory tree inside your /build directory, and we can make sure we've accounted for everything.

App Engine: How to configure app.yaml for a flask app? Error: index.html not found

I have built a Flask app using Python 3.7. The setup works locally. When uploaded to GAE, I get an Internal Server Error. The GAE dashboard says index.html not found. Is this issue related to the app.yaml file?
My project structure is as follows:
root/
|-app.yaml
|-requirements.txt
|-main.py
|-other model files that feed into main.py
|-templates/
|-index.html
|-index2.html
|-js
|-css
|-images
app.yaml
runtime: python37
# [START handlers]
handlers:
- url: /.*
script: auto
- url: /index.html
static_files: templates/index.html
upload: Templates/index.html
- url: /templates
static_dir: Templates
- url: /(.*\.(css|js|png|jpg))
static_files: templates/\1
upload: templates/(.*)
# [END handlers]
The App Engine handlers element documentation states:
Patterns are evaluated in the order they appear in the app.yaml file, from top to bottom. The first mapping whose pattern matches the URL is the one used to handle the request.
Put the catch-all handler at the end.
The templates directory does not need to be added to the URL handlers.
The templates directory should be lower case as a matter of convention. In addition, in the event that URL handlers do need to reference templates, the app.yaml patterns are case sensitive (as mentioned by Dan Cornilescu), and using lowercase will help avoid mismatches.
The standard setup is to create a separate static (and/or assets) directory next to the templates directory. The static directory would contain files such as Javascript, CSS, and images.
Project Structure
root/
|-app.yaml
|-requirements.txt
|-main.py
|-other model files that feed into main.py
|-static/
|-js
|-css
|-images
|-templates/
|-index.html
|-index2.html
app.yaml
runtime: python37
handlers:
- url: /static
static_dir: static
- url: /.*
script: auto

Webpack config with React

I am creating a React application, I used npm eject so I could get to the Webpack config. I want to change the paths to assets so they don't have the leading slash. This is because when I run my application I copy the files over to a Ratpack server, so when the path is /assets/js/main.js it points to my route rather than my assets folder.
So my current webpack config is
output: {
// The build folder.
path: paths.appBuild,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: 'assets/static/js/[name].[chunkhash:8].js',
chunkFilename: 'assets/static/js/[name].[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: publicPath
},
However it always adds on the leading slash.
Like so
<script type="text/javascript" src="/assets/static/js/main.4d5cbecd.js">
Is there a way to have it so it builds like
<script type="text/javascript" src="assets/static/js/main.4d5cbecd.js">
Assuming you are talking about create-react-app's config, you could remove the line publicPath: publicPath, and it will generate it without leading slash.
However, I think your server should serve files too, when they are requested specifically, instead of redirecting to index page. If that's what's happening.
Another advice is for production, use production build instead of development build.

Resources