{"id":15199927,"url":"https://github.com/tiangolo/node-frontend","last_synced_at":"2025-10-02T15:30:48.645Z","repository":{"id":38395303,"uuid":"139137338","full_name":"tiangolo/node-frontend","owner":"tiangolo","description":"Instrutctions to buid a frontend Docker image built with Node.js and then served with Nginx. Previously a Docker image.","archived":true,"fork":false,"pushed_at":"2024-03-17T14:49:21.000Z","size":26,"stargazers_count":137,"open_issues_count":0,"forks_count":93,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-09-25T01:35:24.399Z","etag":null,"topics":["angular","chrome-headless","docker","frontend","karma","nodejs","puppeteer","react","testing","vue"],"latest_commit_sha":null,"homepage":"","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tiangolo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["tiangolo"]}},"created_at":"2018-06-29T10:40:02.000Z","updated_at":"2024-03-31T02:27:21.000Z","dependencies_parsed_at":"2024-09-19T01:06:37.249Z","dependency_job_id":"a9f884f6-07f5-4f89-a2de-99e9ca2100d8","html_url":"https://github.com/tiangolo/node-frontend","commit_stats":{"total_commits":23,"total_committers":4,"mean_commits":5.75,"dds":0.3913043478260869,"last_synced_commit":"58105139a4b8877f96bbbc2409354e697191f539"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiangolo%2Fnode-frontend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiangolo%2Fnode-frontend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiangolo%2Fnode-frontend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiangolo%2Fnode-frontend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tiangolo","download_url":"https://codeload.github.com/tiangolo/node-frontend/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219875832,"owners_count":16554706,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","chrome-headless","docker","frontend","karma","nodejs","puppeteer","react","testing","vue"],"created_at":"2024-09-28T02:21:17.035Z","updated_at":"2025-10-02T15:30:43.387Z","avatar_url":"https://github.com/tiangolo.png","language":"Dockerfile","funding_links":["https://github.com/sponsors/tiangolo"],"categories":[],"sub_categories":[],"readme":"# 🚨 DEPRECATION WARNING 🚨\n\nThis was a Docker image. I'm currently not using nor recommending the Docker image, it is no longer supported.\n\nYou are better off building a Docker image from scratch, see below how. 🤓\n\nThere are more details about the deprecation at the end.\n\n## Build a Frontend Docker Image\n\n### Previous steps\n\n* Create your frontend Node.js based code (React, etc).\n\n* Create a file `.dockerignore` (similar to `.gitignore`) and include in it:\n\n```\nnode_modules\n```\n\n...to avoid copying your `node_modules` to Docker, making things unnecessarily slower.\n\n* If you want to integrate testing as part of your frontend build inside your Docker image building process, install any dependencies you might need, for example Playwright, so that you can test it locally too and to have it in your development dependencies in your `package.json`:\n\n```bash\nnpm init playwright@latest\n```\n\n### Dockerfile\n\n* Create a file `Dockerfile` for building and name the stage `build-stage`:\n\n```Dockerfile\n# Stage 0, \"build-stage\", based on Node.js, to build and compile the frontend\nFROM node:latest as build-stage\n\n...\n\n```\n\n* Copy your `package.json` and possibly your `package-lock.json`:\n\n```Dockerfile\n...\n\nWORKDIR /app\n\nCOPY package*.json /app/\n\n...\n```\n\n...copy just the `package*.json` files to install all the dependencies once and let Docker use the cache for the next builds. By doing this before copying the whole app code, Docker will be able to use the cache and you won't have to wait for Docker to install with `npm install` every time you change the code.\n\n* Install `npm` packages inside your `Dockerfile`:\n\n```Dockerfile\n...\n\nRUN npm install\n\n...\n```\n\n* Copy your source code to the Docker image:\n\n```Dockerfile\n...\n\nCOPY ./ /app/\n\n...\n```\n\n* If you need to pass build arguments, create a default `ARG` to be used at build time:\n\n```Dockerfile\n...\n\nARG VITE_API_URL=${VITE_API_URL}\n\n...\n```\n\n* If you have integrated testing, you can run your tests now:\n\n```Dockerfile\n...\n\nRUN npm run test\n\n...\n```\n\n...if your tests didn't pass, they will throw an error and your build will stop. So, you will never ship a \"broken\" frontend Docker image to production.\n\n* Build your source frontend app as you normally would, with `npm`:\n\n```Dockerfile\n...\n\nRUN npm run build\n\n...\n```\n\n...after that, you would have a fresh build of your frontend app code inside a Docker image. But if you are serving frontend (static files) you could serve them with a high performance server as Nginx, and have a leaner Docker image without all the Node.js code.\n\n* Create a new \"stage\" (just as if it was another Docker image in the same file) based on Nginx:\n\n```Dockerfile\n...\n\n# Stage 1, based on Nginx, to have only the compiled app, ready for production with Nginx\nFROM nginx:latest\n\n...\n```\n\n* Now you will use the `build-stage` name created above in the previous \"stage\", copy the files generated there to the directory that Nginx uses:\n\n```Dockerfile\n...\n\nCOPY --from=build-stage /app/dist/ /usr/share/nginx/html\n\n...\n```\n\n* Create a file `nginx.conf` with:\n\n```Nginx\nserver {\n  listen 80;\n\n  location / {\n    root /usr/share/nginx/html;\n    index index.html index.htm;\n    try_files $uri /index.html =404;\n  }\n}\n```\n\n* This configuration routes everything to your frontend app (to your `index.html`), so that you can use full URLs and they will always work, even if your users type them directly in the browser. Make your Docker image copy that configuration to Nginx's configurations directory:\n\n```Dockerfile\n...\n\nCOPY ./nginx.conf /etc/nginx/conf.d/default.conf\n\n...\n```\n\n* Your final `Dockerfile` could look like:\n\n```Dockerfile\n# Stage 0, \"build-stage\", based on Node.js, to build and compile the frontend\nFROM node:latest as build-stage\n\nWORKDIR /app\n\nCOPY package*.json /app/\n\nRUN npm install\n\nCOPY ./ /app/\n\nARG VITE_API_URL=${VITE_API_URL}\n\nRUN npm run test\n\nRUN npm run build\n\n\n# Stage 1, based on Nginx, to have only the compiled app, ready for production with Nginx\nFROM nginx:latest\n\nCOPY --from=build-stage /app/dist/ /usr/share/nginx/html/\n\nCOPY ./nginx.conf /etc/nginx/conf.d/default.conf\n```\n\n### Building the Docker image\n\n* To build your shiny new image run:\n\n```bash\ndocker build -t my-frontend-project:prod .\n```\n\n...If you had tests and added them above, they will be run. Your app will be compiled and you will end up with a lean high performance Nginx server with your fresh compiled app. Ready for production.\n\n* If you need to pass build time arguments, for example if you have a \"staging\" environment, you can pass them like:\n\n```bash\ndocker build -t my-frontend-project:stag --build-arg VITE_API_URL=\"https://staging.example.com\" .\n```\n\n### Testing the Docker image\n\n* Now, to test it, run:\n\n```bash\ndocker run -p 80:80 my-frontend-project:prod\n```\n\n...if you are running Docker locally you can now go to `http://localhost` in your browser and see your frontend.\n\n## Tips\n\n* Develop locally, if you have a live reload server that runs with something like:\n\n```bash\nnpm run start\n```\n\n...use it.\n\nIt's faster and simpler to develop locally. But once you think you got it, build your Docker image and try it. You will see how it looks in the full production environment.\n\n* If you want to have tests using a (maybe headless) browser, run them locally first, as you normally would. Using the live normal browser. Make sure you have all the configurations right. Once you know it is running locally, you can add that to your `Dockerfile` and have \"continuous integration\" and \"continuous building\"... and if you want, add \"continuous deployment\". But first make it run locally, it's easier to debug only one step at a time.\n\n* Have fun.\n\n## Advanced Nginx configuration\n\nYou can include more Nginx configurations by copying them to `/etc/nginx/conf.d/`, beside the included Nginx configuration.\n\nBy default, this Nginx configuration routes everything to your frontend app (to your `index.html`). But if you want some specific routes to instead return, for example, an HTTP 404 \"Not Found\" error, you can include more nginx `.conf` files in the directory: `/etc/nginx/extra-conf.d/`.\n\nFor example, if you want your final Nginx to send 404 errors to `/api` and `/docs` you can create a file `nginx-backend-not-found.conf:\n\n```Nginx\nlocation /api {\n    return 404;\n}\nlocation /docs {\n    return 404;\n}\n```\n\nAnd in your `Dockerfile` add a line:\n\n```Dockerfile\nCOPY ./nginx-backend-not-found.conf /etc/nginx/extra-conf.d/nginx-backend-not-found.conf\n```\n\n### Details\n\nThese files will be included inside of an \"[Nginx `server` directive](https://nginx.org/en/docs/http/ngx_http_core_module.html#server)\".\n\nSo, you have to put contents that can be included there, like `location`.\n\n---\n\nThis functionality was made to solve a very specific but common use case:\n\nLet's say you have a load balancer on top of your frontend (and probably backend too), and it sends everything that goes to `/api/` to the backend, and `/docs` to an API documentation site (handled by the backend or other service), and the rest, `/`, to your frontend.\n\nAnd your frontend has long-term caching for your main frontend app (as would be normal).\n\nAnd then at some point, during development or because of a bug, your backend, that serves `/docs` is down.\n\nYou try to go there, but because it's down, your load balancer falls back to what handles `/`, your frontend.\n\nSo, you only see your same frontend instead of the `/docs`.\n\nThen you check the logs in your backend, you fix it, and try to load `/docs` again.\n\nBut because the frontend had long-term caching, it still shows your same frontend at `/docs`, even though your backend is back online. Then you have to load it in an incognito window, or fiddle with the local cache of your frontend, etc.\n\nBy making Nginx simply respond with 404 errors when requested for `/docs`, you avoid that problem.\n\nAnd because you have a load balancer on top, redirecting requests to `/docs` to the correct service, Nginx would never actually return that 404. Only in the case of a failure, or during development.\n\n## Deprecated Image\n\nThis used to be a Docker image to simplify the process of creating a full Node.js environment for frontend development with multistage building.\n\nIt included all the dependencies for Puppeteer, so you could just `npm install puppeteer` and it should work.\n\nIt also included a default Nginx configuration for your frontend application, with the same content above, so in multi-stage Docker builds you could copy it to an Nginx \"stage\".\n\nIt is derived from this article I wrote:\n\n\u003e Angular in Docker with Nginx, supporting configurations / environments, built with multi-stage Docker builds and testing with Chrome Headless\n\n [in Medium](https://medium.com/@tiangolo/angular-in-docker-with-nginx-supporting-environments-built-with-multi-stage-docker-builds-bb9f1724e984), and [in GitHub](https://github.com/tiangolo/medium-posts/tree/master/angular-in-docker)\n\nAs copying the `nginx.conf` file to the `Dockerfile` is not that much work, and the dependencies for Puppeteer are probably no longer relevant as Playwright is in many cases a better option, it doesn't make sense to keep supporting this Docker image, and it doesn't make sense for you to use it.\n\nYou are better off following the instructions above. 🤓\n\n## Release Notes\n\n### Latest Changes\n\n#### Fixes\n\n* 🐛 Fix 403 errors when the url points to a directory without an index.html. PR [#5](https://github.com/tiangolo/node-frontend/pull/5) by [@jchorl](https://github.com/jchorl).\n\n#### Docs\n\n* 📝 Add deprecation and update docs. PR [#20](https://github.com/tiangolo/node-frontend/pull/20) by [@tiangolo](https://github.com/tiangolo).\n\n#### Internal\n\n* ⬆ Bump tiangolo/issue-manager from 0.2.0 to 0.5.0. PR [#18](https://github.com/tiangolo/node-frontend/pull/18) by [@dependabot[bot]](https://github.com/apps/dependabot).\n* 👷 Add dependabot. PR [#13](https://github.com/tiangolo/node-frontend/pull/13) by [@tiangolo](https://github.com/tiangolo).\n* 🔧 Add funding. PR [#15](https://github.com/tiangolo/node-frontend/pull/15) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add issue-manager GitHub Action. PR [#14](https://github.com/tiangolo/node-frontend/pull/14) by [@tiangolo](https://github.com/tiangolo).\n* 👷 Add latest-changes GitHub Action. PR [#12](https://github.com/tiangolo/node-frontend/pull/12) by [@tiangolo](https://github.com/tiangolo).\n\n### Initial Release\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiangolo%2Fnode-frontend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftiangolo%2Fnode-frontend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiangolo%2Fnode-frontend/lists"}