{"id":16179234,"url":"https://github.com/jonashackt/microservice-ui-nuxt-js","last_synced_at":"2026-03-11T13:14:24.970Z","repository":{"id":37622333,"uuid":"362380606","full_name":"jonashackt/microservice-ui-nuxt-js","owner":"jonashackt","description":"Example project showing how to create \u0026 deploy a Nuxt.js / Vue.js based frontend and how to interact with a Spring Boot microservice","archived":false,"fork":false,"pushed_at":"2026-03-05T00:44:41.000Z","size":4619,"stargazers_count":8,"open_issues_count":13,"forks_count":9,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-05T06:45:09.520Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Vue","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/jonashackt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-04-28T07:40:20.000Z","updated_at":"2026-02-26T11:46:50.000Z","dependencies_parsed_at":"2023-02-02T16:17:49.953Z","dependency_job_id":"a789f400-edbb-4b95-a561-0851c4f0d6a3","html_url":"https://github.com/jonashackt/microservice-ui-nuxt-js","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jonashackt/microservice-ui-nuxt-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fmicroservice-ui-nuxt-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fmicroservice-ui-nuxt-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fmicroservice-ui-nuxt-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fmicroservice-ui-nuxt-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonashackt","download_url":"https://codeload.github.com/jonashackt/microservice-ui-nuxt-js/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonashackt%2Fmicroservice-ui-nuxt-js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30382600,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T12:49:11.341Z","status":"ssl_error","status_checked_at":"2026-03-11T12:46:41.342Z","response_time":84,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-10-10T05:26:11.256Z","updated_at":"2026-03-11T13:14:24.964Z","avatar_url":"https://github.com/jonashackt.png","language":"Vue","funding_links":[],"categories":[],"sub_categories":[],"readme":"# microservice-ui-nuxt-js\n[![static-site-pulumi-aws-s3-deploy](https://github.com/jonashackt/microservice-ui-nuxt-js/actions/workflows/static-site-pulumi-aws-s3-deploy.yml/badge.svg)](https://github.com/jonashackt/microservice-ui-nuxt-js/actions/workflows/static-site-pulumi-aws-s3-deploy.yml)\n[![server-side-rendering-nodejs-container-paketo](https://github.com/jonashackt/microservice-ui-nuxt-js/actions/workflows/server-side-rendering-nodejs-container-paketo.yml/badge.svg)](https://github.com/jonashackt/microservice-ui-nuxt-js/actions/workflows/server-side-rendering-nodejs-container-paketo.yml)\n[![License](http://img.shields.io/:license-mit-blue.svg)](https://github.com/jonashackt/spring-boot-vuejs/blob/master/LICENSE)\n[![renovateenabled](https://img.shields.io/badge/renovate-enabled-yellow)](https://renovatebot.com)\n[![versionnuxtjs](https://img.shields.io/badge/dynamic/json?color=brightgreen\u0026url=https://raw.githubusercontent.com/jonashackt/microservice-ui-nuxt-js/main/package.json\u0026query=$.devDependencies.nuxt\u0026label=nuxt\u0026logo=nuxt.js)](https://nuxtjs.org/)\n[![versionvitest](https://img.shields.io/badge/dynamic/json?color=brightgreen\u0026url=https://raw.githubusercontent.com/jonashackt/microservice-ui-nuxt-js/main/package.json\u0026query=$.devDependencies.vitest\u0026label=vitest\u0026logo=vitest)](https://vitest.dev/)\n\nExample project showing how to create \u0026amp; deploy a Nuxt.js / Vue.js based frontend and how to interact with a Spring Boot microservice (https://github.com/jonashackt/microservice-api-spring-boot)\n\n![npm-run-dev](screenshots/npm-run-dev.png)\n\n\u003e The purpose of this project is elaborate the differences of a Nuxt.js / SSR or Static Site Generation based project to https://github.com/jonashackt/spring-boot-vuejs\n```shell\n┌────────────────────────────────┐\n│                                │\n│                                │\n│    microservice-ui-nuxt-js     │\n│                                │\n│                                │\n└───────────────┬────────────────┘\n                │\n                │\n┌───────────────▼────────────────┐\n│                                │\n│                                │\n│  microservice-api-spring-boot  │\n│                                │\n│                                │\n└────────────────────────────────┘\n```\n\n## Universal Web Apps?\n\nhttps://m.heise.de/developer/artikel/Interaktive-Websites-mit-Nuxt-js-fuer-Geschwindigkeit-SEO-und-Social-Media-4516901.html?seite=all\n\n\"Universal Webapps\" --\u003e faster loading of web sites!\n\n\u003e How? server does pre-rendering of the \"start\" HTML \u0026 CSS on server-side - and then loads JavaScript \u0026 dynamic CSS in the background, but user can already scroll and read.\n\nAll 3 popular web frameworks support Universal Web Apps:\n\n* Angular: https://angular.io/guide/universal\n* React: https://nextjs.org/ and https://www.gatsbyjs.com/\n* Vue.js: https://nuxtjs.org/\n\n\n\n## Nuxt.js rendering modes \n\nFor all concepts see https://vueschool.io/articles/vuejs-tutorials/hybrid-rendering-in-nuxt-js-3/\n\n[There are 4 modes now with Nuxt 3](https://nuxt.com/docs/guide/concepts/rendering): Universal Rendering (aka Server Side Rendering (SSR)), Client Side Rendering (aka Static Site Generation, Hybrid Rendering \u0026 Edge-Side Rendering\n\n\n* __[Universal Rendering (aka Server Side Rendering (SSR))](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering)__: SSR sends fully rendered page from server to the client -\u003e which then gets **hydrated** (https://vuejs.org/guide/scaling-up/ssr.html), which means that Vue.js turns the server rendered page into dynamic DOM that can react to client-side data changes\n\n* __[Client Side Rendering (aka Static Site Generation)](https://nuxt.com/docs/guide/concepts/rendering#client-side-rendering)__: the application gets rendered during build phase and can then be deployed to any static hosting service (Netlify, GitHub Pages, AWS S3 static site hosting, Static hosting on Azure Storage Accounts etc.). There's no server needed for deployment \u0026 the content is delivered via Content Delivery Networks (CDNs). Additionally in Static Side Generation mode there's also [a SPA Fallback for sites that should be rendered on client side and won't be served through the CDN](https://nuxtjs.org/docs/2.x/concepts/static-site-generation#spa-fallback).\n\nClient Side Rendering aka Static Site Generation can be enabled inside the [`nuxt.config.ts`](nuxt.config.ts):\n\n```typescript\nexport default defineNuxtConfig({\n  ssr: false\n})\n// If you do use ssr: false, you should also place an HTML file in ~/app/spa-loading-template.html with some HTML you would like to use to render a loading screen that will be rendered until your app is hydrated.\n```\n\n* Hybrid Rendering: Route Rules to decide which rendering method to use for every route\n\n* Edge-Side Rendering: With ESR, the rendering process is pushed to the 'edge' of the network - the CDN's edge servers. ESR is more a deployment target than an actual rendering mode.\n\n\n## Getting Started\n\nSee https://nuxt.com/docs/getting-started/installation\n\nIf you have `node` and `npm` installed, the `npx` command should also work (see https://linuxtldr.com/npx-package-runner/).\n\n```shell\nnpx nuxi@latest init microservice-ui-nuxt-js\n```\n\nA wizard will take you through the setup process.\n\nIf you want, you can add some UI candy like Element Plus https://nuxt.com/modules/element-plus (which leverages https://github.com/element-plus/element-plus). In the project root simply run:\n\n```shell\nnpm i element-plus @element-plus/nuxt -D\n```\n\nAnd add the following lines to your [`nuxt.config.ts`](nuxt.config.ts):\n\n```typescript\n  modules: [\n    '@element-plus/nuxt'\n  ],\n  elementPlus: { /** Options */ }\n```\n\nAfter project generation has finished, let's finally run our project skelleton with:\n\n```shell\nnpm run dev -- -o\n```\n\n\n\n## app.vue and Components, Pages, Layouts\n\nhttps://nuxt.com/docs/getting-started/views\n\n\u003e By default, Nuxt will treat this file as the entrypoint and render its content for every route of the application.\n\nSo our file [`app.vue`](app.vue) is the starting point for everything.\n\nhttps://nuxt.com/docs/getting-started/views#components\n\nYou can also create reusable Components in the `components` directory, which may be an AppAlert, a Logo or something the like.\n\nhttps://nuxt.com/docs/getting-started/views#pages\n\n\u003e Pages represent views for each specific route pattern. Every file in the pages/ directory represents a different route displaying its content.\n\nhttps://nuxt.com/docs/getting-started/views#layouts\n\n\u003e Layouts are wrappers around pages that contain a common User Interface for several pages, such as a header and footer display\n\n\n\n\n## Multiple Nuxt Pages: Routing \u0026 Navigation\n\nhttps://nuxt.com/docs/guide/directory-structure/app#minimal-usage\n\nThe CLI generates a file `app.vue`, which is only useful for minimal sites like landing pages without the need for a Vue router:\n\n\u003e \"With Nuxt 3, the pages/ directory is optional. If not present, Nuxt won't include vue-router dependency. This is useful when working on a landing page or an application that does not need routing.\"\n\nIf you use `app.vue` together with `/pages` directory, you should use the `\u003cNuxtPage/\u003e` component:\n\n```javascript\n\u003ctemplate\u003e\n  \u003cdiv\u003e\n    \u003cNuxtLayout\u003e\n      \u003cNuxtPage/\u003e\n    \u003c/NuxtLayout\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n\u003e \"`app.vue` acts a the main component of your Nuxt application. Anything you add to it (JS and CSS) will be global and included in every page\"\n\n\n### Automatic Routes\n\nhttps://nuxt.com/docs/getting-started/routing\n\n\u003e Most websites will have more than one page (i.e. a home page, about page, contact page etc.). In order to show these pages, we need a Router. That's where vue-router comes in. When working with the Vue application, you have to set up a configuration file (i.e. router.js) and add all your routes manually to it. Nuxt.js automatically generates the vue-router configuration for you, based on your provided Vue files inside the pages directory. That means you never have to write a router config again! Nuxt.js also gives you automatic code-splitting for all your routes.\n\n\u003e In other words, all you have to do to have routing in your application is to create .vue files in the pages folder.\n\nPretty cool :) Simply put your `.vue` files into `/pages` dir.\n\n\n### Where is / mapped to?\n\nhttps://nuxt.com/docs/guide/directory-structure/pages#usage\n\n\u003e \"The pages/index.vue file will be mapped to the / route of your application.\"\n\nSo [`index.vue`](pages/index.vue) is the `HOME` of the site.\n\n\n### Navigation \u0026 where's my App.vue gone aka Layouts\n\nhttps://nuxt.com/docs/guide/directory-structure/pages#navigation \n\n\u003e To navigate between pages of your app, you should use the NuxtLink component. This component is included with Nuxt.js and therefore you don't have to import it as you do with other components\n\nSo if you come from Vue.js development like me, you may like the following mapping:\n\n* `\u003crouter-link` in Nuxt is `\u003cNuxtLink\u003e` (see https://nuxtjs.org/docs/2.x/get-started/routing#navigation)\n* `\u003crouter-view/\u003e` is `\u003cNuxt\u003e`, which is the component you use to display your page components (see https://nuxtjs.org/docs/2.x/features/nuxt-components#the-nuxt-component)\n\n\n\n\n# Testing with Nuxt and Vitest \n\nhttps://nuxt.com/docs/getting-started/testing\n\nhttps://dev.to/tao/adding-vitest-to-nuxt-3-2023-lpa  \n\nAfter I used jest some time ago, there's a new testing framework in town: https://vitest.dev\n\n### Install dependencies\n\nFirst we need to add Testing capabilities to our Nuxt project:\n\n```shell\nnpm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core\n```\n\n### Add Vitest integration to Nuxt DevTools via nuxt.config.ts\n\nNow we can add `@nuxt/test-utils/module` to our [`nuxt.config.ts`](nuxt.config.ts) (optional). This adds a Vitest integration to our Nuxt DevTools which supports running our unit tests in development:\n\n```typescript\nexport default defineNuxtConfig({\n  devtools: { enabled: true },\n\n  modules: [\n    '@element-plus/nuxt',\n    '@nuxt/test-utils/module'\n  ],\n  ...\n})\n```\n\n### Create vitest.config.ts\n\nWe also need to create a [`vitest.config.ts`](vitest.config.ts):\n\n```typescript\nimport { defineVitestConfig } from '@nuxt/test-utils/config'\n\nexport default defineVitestConfig({\n  // any custom Vitest config you require\n})\n```\n\nHere [the Nuxt docs](https://nuxt.com/docs/getting-started/testing#setup) are a bit vary and if you leave out the configuration, you get the following error (see https://stackoverflow.com/a/70771291/4964553):\n\n```shell\n FAIL  test/Logo.spec.ts \u003e Logo \u003e is a Vue instance\nReferenceError: document is not defined\n```\n\nTo fix this, configure `happy-dom` (or `jsdom`, if you installed that alternatively):\n\n```typescript\nimport { defineVitestConfig } from '@nuxt/test-utils/config'\n\nexport default defineVitestConfig({\n  // any custom Vitest config you require\n  test: {\n    globals: true,\n    environment: 'happy-dom',\n  },\n})\n```\n\n\n\n### Add test goal to package.json\n\nWe also need to have a `npm run test` goal, that will execute our tests e.g. in CI. Therefore add the following line to your [`package.json`](package.json):\n\n```json\n  \"scripts\": {\n    ...\n    \"test\": \"vitest\",\n    ...\n```\n\n### Write our first testcase\n\nAs already used from jest we create a new test case in the `/test` directory (which we need to create beforehand). As an example here I use [`Logo.spec.ts`](test/Logo.spec.ts):\n\n```typescript\nimport { describe, it, expect } from 'vitest'\nimport { mount } from '@vue/test-utils'\n\nimport Logo from '../components/Logo.vue'\n\ndescribe('Logo', () =\u003e {\n  it('is a Vue instance', () =\u003e {\n    const wrapper = mount(Logo)\n    expect(wrapper.vm).toBeTruthy()\n  })\n})\n```\n\n### Run our test\n\nNow we can finally run our test using `npm run test`:\n\n```shell\n$ npm run test\n\n\u003e test\n\u003e vitest\n\n DEV  v1.2.2 /home/jonashackt/dev/microservice-ui-nuxt-js\n\n ✓ test/Logo.spec.ts (1)\n   ✓ Logo (1)\n     ✓ is a Vue instance\n\n Test Files  1 passed (1)\n      Tests  1 passed (1)\n   Start at  10:20:20\n   Duration  523ms (transform 127ms, setup 10ms, collect 103ms, tests 11ms, environment 117ms, prepare 89ms)\n\n\n PASS  Waiting for file changes...\n       press h to show help, press q to quit\n```\n\n\n\n\n\n\n# API calls with Nuxt 3 \u0026 useFetch()\n\n### Configure baseUrl and Port in Nuxt 3 $fetch API\n\n#### 1.) .evn file\n\nhttps://nuxt.com/docs/guide/directory-structure/env\n\n\u003e if you have a .env file in your project root directory, it will be automatically loaded at dev, build and generate time. Any environment variables set there will be accessible within your nuxt.config file and modules.\n\nThus we need to create a [`.env`](.env) file:\n\n```shell\nBASE_URL = http://localhost:8098\n```\n\n`.env` files aren't checked into source control per default (`.gitignore`). So you need to recreate them in every development environment.\n\n\n#### 2.) runtimeConfig in nuxt.config.ts\n\nhttps://nuxt.com/docs/guide/going-further/runtime-config#exposing \n\n\u003e Nuxt provides a runtime config API to expose configuration and secrets within your application.\n\nAccording to https://stackoverflow.com/a/73636631 add the following to your [`nuxt.config.ts](nuxt.config.ts):\n\n```javascript\nexport default defineNuxtConfig({\n  ...\n  runtimeConfig: {\n    public: {\n      baseUrl: process.env.BASE_URL || 'http://localhost:8098',\n    },\n  },\n  ...\n})\n```\n\n\n#### 3.) Use composable \n\nhttps://serversideup.net/using-environment-variables-in-nuxt-3/\n\nhttps://stackoverflow.com/a/75870302\n\nNow we need a composable to add our `baseUrl` every time we want to use `$fetch`. So let's create the `composable` directory, if not already created and than create a file [`useApiFetch.ts`](useApiFetch.ts):\n\n```javascript\nimport { useFetch } from \"#app\"\n\ntype useFetchType = typeof useFetch\n\n// wrap useFetch with configuration needed to talk to our API\nexport const useAPIFetch: useFetchType = (path, options = {}) =\u003e {\n  const config = useRuntimeConfig()\n\n  // baseURL is configured in nuxt.config.ts \n  options.baseURL = config.public.baseUrl + '/api'\n  return useFetch(path, options)\n}\n```\n\nWe now also use `useFetch` instead of directly using `$fetch`. That's why:\n\nhttps://nuxt.com/docs/api/composables/use-fetch\n\n\u003e `useFetch` is a composable meant to be called directly in a setup function, plugin, or route middleware. It returns reactive composables and handles adding responses to the Nuxt payload so they can be passed from server to client without re-fetching the data on client side when the page hydrates.\n\n\n\n\n## Nuxt.js 3 now features new isomorphic $fetch API based on unjs/ofetch\n\nHere's a great introduction: https://masteringnuxt.com/blog/data-fetching-basics\n\n\u003e \"How can I use the return values from useFetch, called in an event handler, in my template?\": https://github.com/nuxt/nuxt/discussions/19447\n\n[As the docs about axios state](https://axios.nuxtjs.org/), there's something new in town for fetching data from an API: https://nuxt.com/docs/getting-started/data-fetching\n\n\u003e Nuxt provides composables to handle data fetching within your application.\n\n\u003e `useFetch` is the most straightforward way to handle data fetching in a component setup function.\n\u003e `$fetch` is great to make network requests based on user interaction.\n\u003e `useAsyncData`, combined with $fetch, offers more fine-grained control.\n\nThe new fetching API is based on https://unjs.io's https://github.com/unjs/ofetch \n\n\nHere are the docs for the most used `useFetch` https://nuxt.com/docs/api/composables/use-fetch\n\nHere's a basic example:\n\n```typescript\n\u003ctemplate\u003e\n  \u003cdiv class=\"service\"\u003e\n    \u003ch1\u003e{{ msg }}\u003c/h1\u003e\n    \u003ch2\u003eREST service call results\u003c/h2\u003e\n\n    \u003cbutton @click=\"fetchHelloApi\"\u003eCALL Spring Boot REST backend service\u003c/button\u003e\n    \n    \u003ch4 v-if=\"!apiCallPending \u0026\u0026 !apiCallError\"\u003eBackend response: {{ helloMsg }}\u003c/h4\u003e\n\n    \u003cp v-if=\"apiCallError\"\u003eError: {{ apiCallError.message }}\u003c/p\u003e\n\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript setup lang=\"ts\"\u003e\n\nconst msg = 'HowTo call APIs using Nuxt3 useFetch():';\n\nconst helloMsg = ref('');\nlet apiCallPending = ref(false);\nlet apiCallError: any;\n\n// See https://github.com/nuxt/nuxt/discussions/19447\nasync function fetchHelloApi() {\n  const { data, pending, error } = await useAPIFetch\u003cstring\u003e('/hello', );\n  console.log(data);\n  helloMsg.value = data.value || '';\n  apiCallPending = pending;\n  apiCallError = error;\n}\n\u003c/script\u003e\n```\n\nThe combination of calling an `async` function and then `await` for it's promise to be filled is described also here https://stackoverflow.com/a/45018619/4964553\n\n\n\n\n### How to declare values \u0026 update them from async reactive fetch \n\nCommon errors while using the new Nuxt 3 fetch api (ofetch):\n\n#### ts(2322): Type 'Ref\u003cnumber | null\u003e' is not assignable to type 'number'.\n\nHow to access values from await createNewUser():\n\n```javascript\ninterface User {\n    id: number;\n    firstName: string;\n    lastName: string;\n};\n\nlet user: User = { id: 0, firstName: '', lastName: ''};\n\n...\n\nvar userId = await createNewUser();\n\nuser.id = userId; // --\u003e ts(2322): Type 'Ref\u003cnumber | null\u003e' is not assignable to type 'number'.\n```\n\nSolution: `useFetch` returns refs as `Ref\u003cnumber | null\u003e`.\n\nSo in order to access the value returned by `useFetch`, we need to append `.value`:\n\n```javascript\nvar userId = await createNewUser();\n\nuser.id = userId.value;\n```\n\n\n\n#### ts(2322): Type 'unknown' is not assignable to type 'number'.\n\n```javascript\ninterface User {\n    id: number;\n    firstName: string;\n    lastName: string;\n};\n\nlet user: User = { id: 0, firstName: '', lastName: ''};\n\nasync function createNewUser () {\n  const {data: id, pending, error, refresh} = await useAPIFetch(`/user/` + user.firstName + '/' + user.lastName);\n  return id;\n};\n\nvar userId = await createNewUser();\n\nuser.id = userId.value; // --\u003e ts(2322): Type 'unknown' is not assignable to type 'number'.\n```\n\nSolution: https://stackoverflow.com/a/72529632/4964553\n\nUse `useFetch` with explicit type definition `\u003cnumber\u003e`:\n\n```javascript\nawait useAPIFetch\u003cnumber\u003e\n```\n\n\n\n#### ts(2322): Type 'number | null' is not assignable to type 'number'\n\n```javascript\ninterface User {\n    id: number;\n    firstName: string;\n    lastName: string;\n};\n\nlet user: User = { id: 0, firstName: '', lastName: ''};\n\nasync function createNewUser () {\n  const {data: id, pending, error, refresh} = await useAPIFetch\u003cnumber\u003e(`/user/` + user.firstName + '/' + user.lastName);\n  return id;\n};\n\nvar userId = await createNewUser();\n\nuser.id = userId.value; // --\u003e error ts(2322): Type 'number | null' is not assignable to type 'number'\n```\n\nWhen Typescript is used with strict null checks (see https://www.typescriptlang.org/tsconfig#strictNullChecks), the compiler checks if there could be situations, where your variables are undefined. In this case it emits the error. And it's definitely unclear, if our API responds finally. So we could circumvent this by defining a default value (see https://stackoverflow.com/a/74796026/4964553). In our case the `userId.value` is maybe of type `number`, but maybe not defined. So we use an appended  `|| 0` here:\n\n```javascript\nthisUser.id = userId.value || 0\n```\n\n\n\n#### The UI (\u003ctemplate\u003e) doesn't get updated\n\nWe have the following in our template:\n\n```html\n\u003cbutton @click=\"createNewUser\"\u003eCreate User\u003c/button\u003e\n\n\u003cdiv v-if=\"showResponse\"\u003e\u003ch6\u003eUser created with Id: {{ user.id }}\u003c/h6\u003e\u003c/div\u003e\n```\n\nwith the `\u003cscript setup lang=\"ts\"\u003e` section:\n\n```javascript\nvar showResponse: boolean = false;\n\nasync function createNewUser () {\n\n  const {data: id, pending, error} = await useAPIFetch\u003cnumber\u003e(`/user/` + user.firstName + '/' + user.lastName, {\n    method: 'post'\n  });\n  ...\n  showResponse = true;\n  ...\n};\n```\n\nNow clicking on the `Create User` button, doesn't update the UI :(\n\nSolution: We need to use `ref()` here! And `Why refs?` https://vuejs.org/guide/essentials/reactivity-fundamentals.html#why-refs:\n\n\u003e You might be wondering why we need refs with the .value instead of plain variables. To explain that, we will need to briefly discuss how Vue's reactivity system works. \n\n\u003e When you use a ref in a template, and change the ref's value later, Vue automatically detects the change and updates the DOM accordingly. This is made possible with a dependency-tracking based reactivity system. When a component is rendered for the first time, Vue tracks every ref that was used during the render. Later on, when a ref is mutated, it will trigger a re-render for components that are tracking it.\n\n\u003e In standard JavaScript, there is no way to detect the access or mutation of plain variables. However, we can intercept the get and set operations of an object's properties using getter and setter methods. The .value property gives Vue the opportunity to detect when a ref has been accessed or mutated.\n\n\nLuckily our template can stay the same:\n\n\u003e Notice that we did not need to append .value when using the ref in the template. For convenience, refs are automatically unwrapped when used inside templates\n\nBut our `script` section now changes slightly:\n\n```javascript\nimport { ref } from 'vue'\n\nconst showResponse = ref(false);\n\nasync function createNewUser () {\n\n  const {data: id, pending, error} = await useAPIFetch\u003cnumber\u003e(`/user/` + user.firstName + '/' + user.lastName, {\n    method: 'post'\n  });\n  ...\n  showResponse.value = true;\n  ...\n};\n```\n\nNow our UI gets updated as expected :)\n\nAlso we need to use `ref()` for our User objects:\n\n```javascript\n// from this\nlet user: User = { id: 0, firstName: '', lastName: ''};\n\n// to this\nlet user = ref({ id: 0, firstName: '', lastName: ''});\n```\n\nThe type is automatically inferred. We could alternatively write the equivalent:\n\n```javascript\nlet user: Ref\u003cUser\u003e = ref({ id: 0, firstName: '', lastName: ''});\n```\n\n\n\n\n\n\n### Configure Axios \u0026 Spring Boot to handle CORS/SOP\n\nSee https://github.com/jonashackt/spring-boot-vuejs#the-problem-with-sop\n\nAs our Spring Boot App is deployed separately, we don't really need the CORS setup right? Nah, we need at least something!\n\nSee https://stackoverflow.com/a/56168756/4964553 and https://stackoverflow.com/questions/52230470/how-to-use-webpack-dev-proxy-with-nuxt\n\nSo let's configure the correct header in Axios. Therefore head over to our [plugins/axios.ts](plugins/axios.ts) and add the `'Access-Control-Allow-Origin': '*',` header:\n\n```javascript\n// TODO: We need to make the baseURL configurable through environment variables for sure in the next step!\nconst axiosApi = axios.create({\n  baseURL: `http://localhost:8098/api`,\n  timeout: 1000,\n  headers: {\n    'Access-Control-Allow-Origin': '*',\n    'Content-Type': 'application/json'\n  }\n});\n```\n\n##### Don't forget the backend to get CORS enabled\n\nOur Spring Boot based backend https://github.com/jonashackt/microservice-api-spring-boot needs to support CORS too.\n\nSee https://spring.io/guides/gs/rest-service-cors/\n\nSo inside backend Controller [BackendController.java](https://github.com/jonashackt/microservice-api-spring-boot/blob/main/src/main/java/de/jonashackt/springbootvuejs/controller/BackendController.java) we need to use the `org.springframework.web.bind.annotation.CrossOrigin` and should mind our Spring Security configuration also (see https://stackoverflow.com/a/67583232/4964553).\n\n\u003e We should change the `allowedOrigins` from the wildcard `*` to a concrete URL as we go to production.\n\n\n### Make baseUrl configurable via environment variables\n\nA common problem is how to make the `baseUrl` variable configurable inside our [axios.ts](plugins/axios.ts):\n\n```javascript\nimport axios, {AxiosResponse} from 'axios'\n\n// TODO: We need to make the baseURL configurable through environment variables for sure in the next step!\nconst axiosApi = axios.create({\n    //baseURL: `http://fargatealb-81c02c2-1301929463.eu-central-1.elb.amazonaws.com:8098/api`,\n    baseURL: `http://localhost:8098/api`,\n    timeout: 1000,\n    headers: {\n      'Access-Control-Allow-Origin': '*',\n      'Content-Type': 'application/json'\n    }\n});\n```\n\nUsing comments isn't elegant in any way - and the production `baseUrl` will differ every time we do a new Pulumi/IaC deployment...\n\nBut there's help inside the Nuxt.js docs: https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-env We can use the `env` Property:\n\n\u003e Nuxt.js lets you create environment variables client side, also to be shared from server side.\n\nAll we have to do is to add the following lines to our [nuxt.config.js](nuxt.config.js):\n\n```javascript\n  env: {\n    baseUrl: process.env.BASE_URL || 'http://localhost:8098'\n  }\n```\n\nWe can even define a default using `||` which comes in really handy for local development where our Spring Boot backend runs on `http://localhost:8098`!\n\nNow inside our [axios.ts](plugins/axios.ts) we can use the `env` property like this:\n\n```javascript\nimport axios, {AxiosResponse} from 'axios'\n\nconst axiosApi = axios.create({\n    baseURL: process.env.baseUrl,\n    timeout: 1000,\n    headers: {\n      'Access-Control-Allow-Origin': '*',\n      'Content-Type': 'application/json'\n    }\n});\n```\n\nThe `baseUrl` property that will be equal to the `BASE_URL` server side environment variable if available or defined.\n\nNow on your local dev machine you can create a `BASE_URL` env var like this:\n\n```\nexport BASE_URL=http://fargatealb-81c02c2-1301929463.eu-central-1.elb.amazonaws.com:8098/api\n```\n\nRunning `npm run dev` should now use the Fargate URL as definded.\n\n\n## Generate Static Site from Nuxt.js application \n\nNow we should generate our Nuxt.js static site. So inside our project's root directory let's run:\n\n```shell\nnpm run generate\n```\n\nThis will result in a normal NPM build, followed by the static site generation:\n\n```shell\n\n\u003e generate\n\u003e nuxt generate\n\nNuxt 3.9.3 with Nitro 2.8.1                                                                                                                                                                            2:33:39 PM\nℹ Using Nitro server preset: static                                                                                                                                                                   2:33:39 PM\nℹ Building client...                                                                                                                                                                                  2:33:41 PM\nℹ vite v5.0.11 building for production...                                                                                                                                                             2:33:41 PM\nℹ ✓ 1553 modules transformed.                                                                                                                                                                         2:33:44 PM\nInspect report generated at /home/jonashackt/dev/microservice-ui-nuxt-js/.nuxt/analyze/.vite-inspect\nℹ .nuxt/dist/client/manifest.json                     2.94 kB │ gzip:  0.58 kB                                                                                                                        2:33:44 PM\n...\nℹ .nuxt/dist/client/_nuxt/useAPIFetch.77dgfZ2z.js    12.37 kB │ gzip:  5.06 kB                                                                                                                        2:33:44 PM\nℹ .nuxt/dist/client/_nuxt/Element.70MerkGn.js        29.38 kB │ gzip: 10.27 kB                                                                                                                        2:33:44 PM\nℹ .nuxt/dist/client/_nuxt/entry.FHaf2s3N.js         158.96 kB │ gzip: 60.07 kB                                                                                                                        2:33:44 PM\nℹ ✓ built in 3.46s                                                                                                                                                                                    2:33:44 PM\n✔ Client built in 3473ms                                                                                                                                                                              2:33:44 PM\nℹ Building server...                                                                                                                                                                                  2:33:44 PM\nℹ vite v5.0.11 building SSR bundle for production...                                                                                                                                                  2:33:44 PM\nℹ ✓ 825 modules transformed.                                                                                                                                                                          2:33:46 PM\nInspect report generated at /home/jonashackt/dev/microservice-ui-nuxt-js/.nuxt/analyze/.vite-inspect\nℹ .nuxt/dist/server/_nuxt/entry-styles.r_RyA2SE.mjs            0.08 kB                                                                                                                                2:33:46 PM\nℹ .nuxt/dist/server/_nuxt/User-styles.J7VmNqcw.mjs             0.14 kB                                                                                                                                2:33:46 PM\n...\nℹ .nuxt/dist/server/_nuxt/Element-vW6V9Woz.js                 40.91 kB │ map:  72.14 kB                                                                                                               2:33:46 PM\nℹ .nuxt/dist/server/server.mjs                                49.94 kB │ map: 115.45 kB                                                                                                               2:33:46 PM\nℹ ✓ built in 2.19s                                                                                                                                                                                    2:33:46 PM\n✔ Server built in 2198ms                                                                                                                                                                              2:33:46 PM\nℹ Initializing prerenderer                                                                                                                                                                      nitro 2:33:46 PM\nℹ Prerendering 6 initial routes with crawler                                                                                                                                                    nitro 2:33:47 PM\n  ├─ /Service (72ms)                                                                                                                                                                             nitro 2:33:47 PM\n  ├─ /200.html (71ms)                                                                                                                                                                            nitro 2:33:47 PM\n  ├─ /User (73ms)                                                                                                                                                                                nitro 2:33:47 PM\n  ├─ /404.html (71ms)                                                                                                                                                                            nitro 2:33:47 PM\n  ├─ /Element (78ms)                                                                                                                                                                             nitro 2:33:47 PM\n  ├─ / (76ms)                                                                                                                                                                                    nitro 2:33:47 PM\n  ├─ /service (17ms)                                                                                                                                                                             nitro 2:33:47 PM\n  ├─ /user (8ms)                                                                                                                                                                                 nitro 2:33:47 PM\n  ├─ /element (14ms)                                                                                                                                                                             nitro 2:33:47 PM\n  ├─ /Service/_payload.json (18ms)                                                                                                                                                               nitro 2:33:47 PM\n  ├─ /User/_payload.json (7ms)                                                                                                                                                                   nitro 2:33:47 PM\n  ├─ /Element/_payload.json (1ms)                                                                                                                                                                nitro 2:33:47 PM\n  ├─ /_payload.json (0ms)                                                                                                                                                                        nitro 2:33:47 PM\n  ├─ /service/_payload.json (0ms)                                                                                                                                                                nitro 2:33:47 PM\n  ├─ /user/_payload.json (0ms)                                                                                                                                                                   nitro 2:33:47 PM\n  ├─ /element/_payload.json (1ms)                                                                                                                                                                nitro 2:33:47 PM\n✔ Generated public .output/public                                                                                                                                                               nitro 2:33:47 PM\n✔ You can preview this build using npx serve .output/public                                                                                                                                     nitro 2:33:47 PM\n✔ You can now deploy .output/public to any static hosting! \n```\n\nHave a look into the `.output/public` folder - it should contain all files necessary for your site to host in a static hosting service like AWS S3 (or GitHub Pages etc.).\n\n\n## Deploy Static Site Generated Nuxt.js app to AWS S3 with Pulumi\n\nhttps://www.pulumi.com/docs/reference/pkg/aws/s3/bucket/\n\nhttps://www.pulumi.com/docs/reference/pkg/aws/s3/bucketobject/\n\nhttps://www.pulumi.com/docs/tutorials/aws/s3-website/\n\nLet's create a separate `deployment` directory for our Pulumi sources (since we can't override our `package.json` etc. of our root project):\n\n```shell\nmkdir deployment \u0026\u0026 cd deployment\npulumi new aws-typescript\n```\n\nI named the Pulumi project after my root project `microservice-ui-nuxt-js-deployment`\n\nNow inside our [deployment/index.ts](deployment/index.ts) Pulumi TypeScript program let's create an S3 Bucket for static website hosting:\n\n```javascript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as aws from \"@pulumi/aws\";\n\n// Create an AWS resource (S3 Bucket)\nconst nuxtBucket = new aws.s3.Bucket(\"microservice-ui-nuxt-js-hosting-bucket\", {\n  acl: \"public-read\",\n  website: {\n    indexDocument: \"index.html\",\n  }\n});\n\n// S3 Objects from Nuxt.js static site generation will be added through aws CLI instead of Pulumi like this\n// (see https://www.pulumi.com/docs/tutorials/aws/aws-ts-static-website/#deployment-speed):\n// aws s3 sync ../.output/public/ s3://$(pulumi stack output bucketName) --acl public-read\n\n// Export the name of the bucket\nexport const bucketName = nuxtBucket.id;\nexport const bucketUrl = nuxtBucket.websiteEndpoint;\n```\n\nAnd for every file inside the Nuxt.js target dir `.output/public` we create a new S3 object inside the S3 Bucket.\n\n\u003e But this shouldn't be done using Pulumi's `BucketObject` for multiple files really - see this so Q\u0026A for more details: https://stackoverflow.com/questions/67318524/pulumi-typescript-aws-how-to-upload-multiple-files-to-s3-incl-nested-files\n\nInstead we should use AWS CLI directly to copy (and later incrementally sync, when new builds ran) our static website files to our S3 Bucket like this:\n\n```shell\naws s3 sync ../.output/public/ s3://$(pulumi stack output bucketName) --acl public-read\n```\n\nUsing $(pulumi stack output bucketName) we simply get the S3 Bucket name that was created by Pulumi. Mind the --acl public-read parameter at the end, since you have to enable public read access on each of your static web files in S3, although the Bucket itself already has public read access!\n\n\u003e Before we finally run our Pulumi program, make sure to have an apropriate AWS `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` configured.\nIf you don't have them, you can generate them inside the `IAM` service for your AWS user in the AWS management console.\nMake sure to run `aws configure` to configure both to your local terminal.\n\nNow it's time to run our Pulumi deployment. `cd` into `deployment` and run:\n\n```shell\npulumi stack select dev\npulumi up\n```\n\n\n## Use Pulumi with GitHub Actions CI\n\nAs already described here: https://github.com/jonashackt/azure-training-pulumi#pulumi-with-github-actions there are some steps to take in order to use Pulumi with GitHub Actions.\n\nhttps://www.pulumi.com/docs/guides/continuous-delivery/github-actions/\n\nIt's really cool to see that there's a Pulumi GitHub action project https://github.com/pulumi/actions already ready for us.\n\n\n#### Create needed GitHub Repository Secrets\n\nFirst we need to create 5 new GitHub Repository Secrets (encrypted variables) in your repo under `Settings/Secrets`.\n\nWe should start to create a new Pulumi Access Token `PULUMI_ACCESS_TOKEN` at https://app.pulumi.com/jonashackt/settings/tokens\n\nNow we need to create the AWS specific variables: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` (they need to be exactly named like this, see https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/#environment-variables). Create them all as GitHub Repository Secrets.\n\nThere should be all these vars defined:\n\n![github-actions-pulumi-secrets](screenshots/github-actions-pulumi-secrets.png)\n\n\n\n#### Create GitHub Actions workflow\n\nLet's create a GitHub Actions workflow [static-site-pulumi-aws-s3-deploy.yml](.github/workflows/static-site-pulumi-aws-s3-deploy.yml):\n\n```yaml\nname: static-site-pulumi-aws-s3-deploy\n\non:\n  push:\n  pull_request:\n\nenv:\n  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}\n  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n  PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}\n\njobs:\n  static-site-pulumi-aws-s3-deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@master\n\n      - name: Setup node env\n        uses: actions/setup-node@v2.1.2\n        with:\n          node-version: '14'\n\n      - name: Cache node_modules\n        uses: actions/cache@v2\n        with:\n          path: ~/.npm\n          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}\n          restore-keys: |\n            ${{ runner.os }}-node-\n\n      - name: Install Nuxt.js dependencies\n        run: npm install\n\n      - name: Install Pulumi dependencies before npm run generate to prevent it from breaking the build\n        run: npm install\n        working-directory: ./deployment\n\n      - name: Run tests\n        run: npm run test\n\n      - name: Generate Static Site from Nuxt.js application\n        run: npm run generate\n\n      - name: Install Pulumi CLI\n        uses: pulumi/action-install-pulumi-cli@v1.0.2\n\n      - name: Run pulumi preview \u0026 pulumi up\n        run: |\n          pulumi stack select dev\n          pulumi preview\n          pulumi up -y\n        working-directory: ./deployment\n\n      - name: Configure AWS credentials for GitHub pre-installed AWS CLI\n        uses: aws-actions/configure-aws-credentials@v1\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: eu-central-1\n\n      - name: Deploy Nuxt.js generated static site to S3 Bucket via AWS CLI\n        run: |\n          aws s3 sync ../.output/public/ s3://$(pulumi stack output bucketName) --acl public-read\n          echo \"Access the Nuxt.js app at the following URL:\"\n          pulumi stack output bucketUrl\n        working-directory: ./deployment\n```\n\nWe use the possibility [to define the environment variables on the workflow's top level](https://docs.github.com/en/actions/reference/environment-variables) to reduce the 3 definition to one. \n\nThen after using the great cache Action, we need to install our Nuxt project's dependencies. And we also need to install our Pulumi project's npm packages - otherwise `npm run generate` (which generates the Nuxt.js Static Site) won't work ([see this build for example](https://github.com/jonashackt/microservice-ui-nuxt-js/runs/2499420502)):\n\n```shell\n$ Run npm run generate\n\n\u003e microservice-ui-nuxt-js@1.0.0 generate /home/runner/work/microservice-ui-nuxt-js/microservice-ui-nuxt-js\n\u003e nuxt generate\n\n[fatal] Nuxt build error\n  ERROR in deployment/index.ts:1:25\n  TS2307: Cannot find module '@pulumi/pulumi' or its corresponding type declarations.\n  \u003e 1 | import * as pulumi from \"@pulumi/pulumi\";\n  |                         ^^^^^^^^^^^^^^^^\n  2 | import * as aws from \"@pulumi/aws\";\n  3 |\n  4 | // Create an AWS resource (S3 Bucket)\n  \n  ERROR in deployment/index.ts:2:22\n  TS2307: Cannot find module '@pulumi/aws' or its corresponding type declarations.\n  1 | import * as pulumi from \"@pulumi/pulumi\";\n  \u003e 2 | import * as aws from \"@pulumi/aws\";\n  |                      ^^^^^^^^^^^^^\n  3 |\n  4 | // Create an AWS resource (S3 Bucket)\n  5 | const nuxtBucket = new aws.s3.Bucket(\"microservice-ui-nuxt-js-hosting-bucket\", {\n\n   ╭─────────────────────────────╮\n   │                             │\n   │   ✖ Nuxt Fatal Error        │\n   │                             │\n   │   Error: Nuxt build error   │\n   │                             │\n   ╰─────────────────────────────╯\n\nnpm ERR! code ELIFECYCLE\nnpm ERR! errno 1\nnpm ERR! microservice-ui-nuxt-js@1.0.0 generate: `nuxt generate`\nnpm ERR! Exit status 1\nnpm ERR! \nnpm ERR! Failed at the microservice-ui-nuxt-js@1.0.0 generate script.\nnpm ERR! This is probably not a problem with npm. There is likely additional logging output above.\n\nnpm ERR! A complete log of this run can be found in:\nnpm ERR!     /home/runner/.npm/_logs/2021-05-04T09_54_52_203Z-debug.log\nError: Process completed with exit code 1.\n```\n\nThe nuxt generate seems to look for `package.json` files in subdirectories also.\n\nAfter the obligatory jest test run via `npm run test`, we finally need to generate our Nuxt.js static site files with\n\n```shell\nnpm run generate\n```\n\nThis will generate all files into the `.output/public` directory we'll later use with the AWS CLI to sync into our S3 Bucket.\n\nAfter having installed the Pulumi CLI with the [pulumi/action-install-pulumi-cli](https://github.com/pulumi/action-install-pulumi-cli) action, we can use Pulumi to create our AWS resources - which is our static website hosting enabled S3 Bucket mainly.\n\nWe also use GitHub Actions ability to define a `working-directory: ./deployment` ([as discussed here](https://stackoverflow.com/a/58142276/4964553)) so that we can issue our `pulumi up` in the right directory.\n\nTo not run into problems using the pre-installed AWS CLI on GitHub Actions we should also configure our AWS credentials using the [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) action. Otherwise we'll run into errors like this:\n\n```shell\nRun aws s3 sync ../.output/public/ s3://$(pulumi stack output bucketName) --acl public-read\n\n\u003cbotocore.awsrequest.AWSRequest object at 0x7f01644bd070\u003e\n```\n\nFinally we can use the AWS CLI to sync our Nuxt.js generated static site files into the Pulumi created S3 Bucket!\n\n\n#### Create GitHub environment with the S3 Bucket URL\n\n[As the docs state](https://docs.github.com/en/actions/reference/environments#creating-an-environment):\n\n\u003e Running a workflow that references an environment that does not exist will create an environment with the referenced name. The newly created environment will not have any protection rules or secrets configured. Anyone that can edit workflows in the repository can create environments via a workflow file, but only repository admins can configure the environment.\n\nSo first we need to define the environment inside our [static-site-pulumi-aws-s3-deploy.yml](.github/workflows/static-site-pulumi-aws-s3-deploy.yml):\n\n```yaml\n    environment:\n      name: microservice-ui-nuxt-js-deployment\n      url: ${{ steps.step_name.outputs.url_output }}\n```\n\nNow we need to give our step Deployment step a `id` so that we can reference it inside the `environment:url`. Also we need to\nset a variable like `s3_url` that will hold the S3 Buckets url with `echo \"::set-output name=s3_url::http://$(pulumi stack output bucketUrl)\"` (see [this so answer also](https://stackoverflow.com/a/57989070/4964553)):\n\n```yaml\n      - name: Deploy Nuxt.js generated static site to S3 Bucket via AWS CLI\n        id: aws-sync\n        run: |\n          aws s3 sync ../.output/public/ s3://$(pulumi stack output bucketName) --acl public-read\n          echo \"Access the Nuxt.js app at the following URL:\"\n          pulumi stack output bucketUrl\n          echo \"::set-output name=s3_url::http://$(pulumi stack output bucketUrl)\"\n        working-directory: ./deployment\n```\n\nWith this we can use the output inside our `environment:url`:\n\n```yaml\n    environment:\n      name: microservice-ui-nuxt-js-deployment\n      url: ${{ steps.aws-sync.outputs.s3_url }}\n```\n\nNow we should be able to see (and click on) the URL as an environment inside the GitHub Actions UI:\n\n![github-actions-environment-link-s3](screenshots/github-actions-environment-link-s3.png)\n\n\n\n#### Configure BASE_URL of microservice-api-spring-boot in frontend deployment\n\nInitally let's simply define the environment variable `BASE_URL` inside our GitHub Actions workflow [static-site-pulumi-aws-s3-deploy.yml](.github/workflows/static-site-pulumi-aws-s3-deploy.yml):\n\n```yaml\nname: static-site-pulumi-aws-s3-deploy\n\non:\n  push:\n  pull_request:\n\nenv:\n  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}\n  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n  PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}\n  BASE_URL: \"http://fargatealb-81c02c2-1301929463.eu-central-1.elb.amazonaws.com:8098/api\"\n\n...\n```\n\n\n\n## Server-Side Rendering (SSR) incl. Node.js Container Build with Paketo\n\n\u003e Here's a great description of the differences between `static` and `server` mode in Nuxt.js: https://nuxtjs.org/blog/going-full-static#commands\n\nNuxt.js also provides us with a `server` mode inside the [nuxt.config.js](nuxt.config.js):\n\n```json\nexport default {\n  // Target: https://go.nuxtjs.dev/config-target\n  target: 'server',\n```\n\nNow instead of using `nuxt generate` (configured inside our `package.json`) or `npm run generate` to generate a static version of our app, we should use `nuxt build` - or `npm run build` to trigger the SSR mode build:\n\n```shell\n$ npm run build\nℹ Production build                                                                                                                                                                  11:12:27\nℹ Bundling for server and client side                                                                                                                                               11:12:27\nℹ Target: server                                                                                                                                                                    11:12:27\nℹ Using components loader to optimize imports                                                                                                                                       11:12:27\nℹ Discovered Components: .nuxt/components/readme.md                                                                                                                                 11:12:27\n✔ Builder initialized                                                                                                                                                               11:12:27\n✔ Nuxt files generated\n...\n```\n\n#### Use Paketo.io CNB to build a Node.js Container for our Nuxt app\n\nBe sure to have `pack CLI` installed (see https://buildpacks.io/docs/tools/pack/) and then run:\n\n```shell\npack build microservice-ui-nuxt-js --path . --builder paketobuildpacks/builder-jammy-base\n```\n\nI had several errors starting with `node js npm ERR! missing:` and read through https://paketo.io/docs/buildpacks/language-family-buildpacks/nodejs/#npm-installation-process\n\nPaketo runs `npm rebuild` when a local `node_modules` directory \u0026 `package-lock.json` is present, which ran into the errors. \n\nSimply deleting the local `node_modules` folder causes Paketo to run a `npm ci` instead, which worked like a charm:\n\n```shell\n$ pack build microservice-ui-nuxt-js --path . --builder paketobuildpacks/builder-jammy-base\nbase: Pulling from paketobuildpacks/builder\nDigest: sha256:3e2ee17348bd901e7e0748e0e1ddccdf8a602b624e418927145b5f84ca26f264\nStatus: Image is up to date for paketobuildpacks/builder-jammy-base\nbase-cnb: Pulling from paketobuildpacks/run\nDigest: sha256:b6b1612ab2dfa294514fff2750e8d724287f81e89d5e91209dbdd562ed7f7daf\nStatus: Image is up to date for paketobuildpacks/run:base-cnb\n===\u003e DETECTING\n4 of 7 buildpacks participating\npaketo-buildpacks/ca-certificates 2.2.0\npaketo-buildpacks/node-engine     0.4.0\npaketo-buildpacks/npm-install     0.3.0\npaketo-buildpacks/npm-start       0.2.0\n===\u003e ANALYZING\nPrevious image with name \"microservice-ui-nuxt-js\" not found\n===\u003e RESTORING\n===\u003e BUILDING\n\nPaketo CA Certificates Buildpack 2.2.0\n  https://github.com/paketo-buildpacks/ca-certificates\n  Launch Helper: Contributing to layer\n    Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper\nPaketo Node Engine Buildpack 0.4.0\n  Resolving Node Engine version\n    Candidate version sources (in priority order):\n                -\u003e \"\"\n      \u003cunknown\u003e -\u003e \"\"\n\n    Selected Node Engine version (using ): 14.17.0\n\n  Executing build process\n    Installing Node Engine 14.17.0\n      Completed in 5.795s\n\n  Configuring build environment\n    NODE_ENV     -\u003e \"production\"\n    NODE_HOME    -\u003e \"/layers/paketo-buildpacks_node-engine/node\"\n    NODE_VERBOSE -\u003e \"false\"\n\n  Configuring launch environment\n    NODE_ENV     -\u003e \"production\"\n    NODE_HOME    -\u003e \"/layers/paketo-buildpacks_node-engine/node\"\n    NODE_VERBOSE -\u003e \"false\"\n\n    Writing profile.d/0_memory_available.sh\n      Calculates available memory based on container limits at launch time.\n      Made available in the MEMORY_AVAILABLE environment variable.\n\nPaketo NPM Install Buildpack 0.3.0\n  Resolving installation process\n    Process inputs:\n      node_modules      -\u003e \"Not found\"\n      npm-cache         -\u003e \"Not found\"\n      package-lock.json -\u003e \"Found\"\n\n    Selected NPM build process: 'npm ci'\n\n  Executing build process\n    Running 'npm ci --unsafe-perm --cache /layers/paketo-buildpacks_npm-install/npm-cache'\n      Completed in 14.988s\n\n  Configuring launch environment\n    NPM_CONFIG_LOGLEVEL -\u003e \"error\"\n\n  Configuring environment shared by build and launch\n    PATH -\u003e \"$PATH:/layers/paketo-buildpacks_npm-install/modules/node_modules/.bin\"\n\n\nPaketo NPM Start Buildpack 0.2.0\n  Assigning launch processes\n    web: nuxt start\n\n===\u003e EXPORTING\nAdding layer 'paketo-buildpacks/ca-certificates:helper'\nAdding layer 'paketo-buildpacks/node-engine:node'\nAdding layer 'paketo-buildpacks/npm-install:modules'\nAdding layer 'paketo-buildpacks/npm-install:npm-cache'\nAdding 1/1 app layer(s)\nAdding layer 'launcher'\nAdding layer 'config'\nAdding layer 'process-types'\nAdding label 'io.buildpacks.lifecycle.metadata'\nAdding label 'io.buildpacks.build.metadata'\nAdding label 'io.buildpacks.project.metadata'\nSetting default process type 'web'\nSaving microservice-ui-nuxt-js...\n*** Images (5eb36ba20094):\n      microservice-ui-nuxt-js\nAdding cache layer 'paketo-buildpacks/node-engine:node'\nAdding cache layer 'paketo-buildpacks/npm-install:modules'\nAdding cache layer 'paketo-buildpacks/npm-install:npm-cache'\nSuccessfully built image microservice-ui-nuxt-js\n```\n\n#### Running our Paketo build Nuxt.js container (the Nuxt HOST configuration)\n\nNow everything should be straight forward, right?! Simply run our app with:\n\n```shell\ndocker run --rm -i --tty -p 3000:3000 microservice-ui-nuxt-js\n```\n\nBut this doesn't show up in our browser!\n\nDoes it work inside the container? Install curl...\n\nBut still not in the browser\n\n\nI came upon it in https://nuxtjs.org/docs/2.x/deployment/deployment-cloud-run\n\nand then read https://medium.com/i22digital/development-setup-with-nuxt-node-and-docker-b008a241c11d\n\n\u003e Last thing is the environment variable HOST . Nuxt needs this variable when started from a container, otherwise you won’t be able to reach it. Host 0.0.0.0 is designated to tell Nuxt to resolve a host address, which is accessible to connections outside of the host machine.\n\nSee https://stackoverflow.com/a/67871934/4964553\n\n```shell\ndocker run --rm -i --tty --env \"HOST=0.0.0.0\" -p 3000:3000 microservice-ui-nuxt-js\n```\n\nAnother problem arose after upgrading to Nuxt 3.x / Vue.js 3.x: \n\nExecuting the `docker run` gave a:\n\n```shell\nERROR: failed to launch: determine start command: when there is no default process a command is required\n```\n\nLuckily I stumbled upon the depts of the Paketo node.js docs, where it is described [how to specify a custom entrypoint right at the Paketo build time](https://paketo.io/docs/howto/nodejs/#specify-a-custom-entrypoint) - for more details see https://stackoverflow.com/a/78000323/4964553\n\nSo a working `docker run` could be achieved by using the following `pack build` command:\n\n```shell\npack build ghcr.io/jonashackt/microservice-ui-nuxt-js:latest \\\n          --builder paketobuildpacks/builder-jammy-base \\\n          --env BP_LAUNCHPOINT=\".output/server/index.mjs\" \\\n          --path . \n```\n\n\n\n\n#### Running the Paketo build on GitHub Actions\n\nNow simply add a new GitHub Actions workflow [server-side-rendering-nodejs-container-paketo.yml](.github/workflows/server-side-rendering-nodejs-container-paketo.yml):\n\n```yaml\nserver-side-rendering-nodejs-container-paketo:\n  runs-on: ubuntu-latest\n\n  steps:\n    - name: Checkout 🛎\n      uses: actions/checkout@master\n\n    - name: Login to GitHub Container Registry\n      uses: docker/login-action@v1\n      with:\n        registry: ghcr.io\n        username: ${{ github.actor }}\n        password: ${{ secrets.GITHUB_TOKEN }}\n\n    - name: Install pack CLI via the official buildpack Action https://github.com/buildpacks/github-actions#setup-pack-cli-action\n      uses: buildpacks/github-actions/setup-pack@v4.1.1\n\n    # Caching Paketo Build see https://stackoverflow.com/a/66598693/4964553\n    # BP_OCI_SOURCE as --env creates the GitHub Container Registry \u003c-\u003e Repository link (https://paketo.io/docs/buildpacks/configuration/#applying-custom-labels)\n    - name: Build app with pack CLI \u0026 publish to bc Container Registry\n      run: |\n        pack build ghcr.io/jonashackt/microservice-ui-nuxt-js:latest \\\n            --builder paketobuildpacks/builder-jammy-base \\\n            --path . \\\n            --env \"BP_OCI_SOURCE=https://github.com/jonashackt/microservice-ui-nuxt-js\" \\\n            --cache-image ghcr.io/jonashackt/microservice-ui-nuxt-js-paketo-cache-image:latest \\\n            --publish\n\n```\n\nLet's try to run our container which got published to the GitHub Container Registry with:\n\n```shell\ndocker run --rm -i --tty --env \"HOST=0.0.0.0\" -p 3000:3000 ghcr.io/jonashackt/microservice-ui-nuxt-js:latest\n```\n\nThis will result in the following error:\n\n```shell\n$ docker run --rm -i --tty --env \"HOST=0.0.0.0\" -p 3000:3000 ghcr.io/jonashackt/microservice-ui-nuxt-js:latest\nUnable to find image 'ghcr.io/jonashackt/microservice-ui-nuxt-js:latest' locally\nlatest: Pulling from jonashackt/microservice-ui-nuxt-js\nd0c52a206d00: Already exists\n1879a9e18f37: Already exists\n1320020c14ba: Already exists\n0c300ce812d0: Pull complete\n09e2d34e431f: Pull complete\n49948b98bd8d: Pull complete\nd18fee29c74f: Pull complete\n49d2052d2301: Pull complete\nfeeffabb64e4: Pull complete\n0fae4bb31aa5: Pull complete\n87f3d5cd60d8: Pull complete\naa7a1571fd83: Pull complete\n0b0022959657: Pull complete\n5a44e4f7b58d: Pull complete\nDigest: sha256:1d1e3125a8fdab6136b490891c7d9717bb9e108a3df870f55f77f64ecea4d597\nStatus: Downloaded newer image for ghcr.io/jonashackt/microservice-ui-nuxt-js:latest\n\n FATAL  No build files found in /workspace/.nuxt/.output/public/server.                                                                                                                       13:09:38\nUse either `nuxt build` or `builder.build()` or start nuxt in development mode.\n\n  Use either `nuxt build` or `builder.build()` or start nuxt in development mode.\n  at VueRenderer._ready (/layers/paketo-buildpacks_npm-install/modules/node_modules/@nuxt/vue-renderer/.output/public/vue-renderer.js:758:13)\n  at async Server.ready (/layers/paketo-buildpacks_npm-install/modules/node_modules/@nuxt/server/.output/public/server.js:637:5)\n  at async Nuxt._init (/layers/paketo-buildpacks_npm-install/modules/node_modules/@nuxt/core/.output/public/core.js:482:7)\n\n\n   ╭─────────────────────────────────────────────────────────────────────────────────────╮\n   │                                                                                     │\n   │   ✖ Nuxt Fatal Error                                                                │\n   │                                                                                     │\n   │   Error: No build files found in /workspace/.nuxt/.output/public/server.                      │\n   │   Use either `nuxt build` or `builder.build()` or start nuxt in development mode.   │\n   │                                                                                     │\n   ╰─────────────────────────────────────────────────────────────────────────────────────╯\n\n```\n\nIt seems that our Paketo build [only runs `npm install` or `npm ci`](https://paketo.io/docs/buildpacks/language-family-buildpacks/nodejs/#npm-installation-process), but doesn't run `npm run generate` (which in turn runs `nuxt generate`) to generate our site.\n\nSo in order to get our Container working, we need to run the `npm run generate` first (as we did locally) - and then the Paketo build should pick up our build artifacts.\n\nThis is not ideal, since there should be everything handled inside the container build - but [reading the docs](https://paketo.io/docs/buildpacks/language-family-buildpacks/nodejs/#npm-installation-process) I don't see a hook where I could place the `npm run generate` inside the Paketo build.\n\nSo here's the full GitHub Action job:\n\n```yaml\n  server-side-rendering-nodejs-container-paketo:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout 🛎\n        uses: actions/checkout@master\n\n      - name: Setup node env 🏗\n        uses: actions/setup-node@v2.1.5\n        with:\n          node-version: '14'\n\n      - name: Cache node_modules 📦\n        uses: actions/cache@v2\n        with:\n          path: ~/.npm\n          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}\n          restore-keys: |\n            ${{ runner.os }}-node-\n\n      - name: Install Nuxt.js dependencies\n        run: npm install\n\n      - name: Install Pulumi dependencies before npm run generate to prevent it from breaking the build\n        run: npm install\n        working-directory: ./deployment\n\n      - name: Run tests 🧪\n        run: npm run test\n\n      - name: Generate Static Site from Nuxt.js application (nuxt build, see package.json)\n        run: npm run build\n\n      - name: Login to GitHub Container Registry\n        uses: docker/login-action@v1\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Install pack CLI via the official buildpack Action https://github.com/buildpacks/github-actions#setup-pack-cli-action\n        uses: buildpacks/github-actions/setup-pack@v4.1.1\n\n      # Caching Paketo Build see https://stackoverflow.com/a/66598693/4964553\n      # BP_OCI_SOURCE as --env creates the GitHub Container Registry \u003c-\u003e Repository link (https://paketo.io/docs/buildpacks/configuration/#applying-custom-labels)\n      - name: Build app with pack CLI \u0026 publish to bc Container Registry\n        run: |\n          pack build ghcr.io/jonashackt/microservice-ui-nuxt-js:latest \\\n              --builder paketobuildpacks/builder-jammy-base \\\n              --path . \\\n              --env \"BP_OCI_SOURCE=https://github.com/jonashackt/microservice-ui-nuxt-js\" \\\n              --cache-image ghcr.io/jonashackt/microservice-ui-nuxt-js-paketo-cache-image:latest \\\n              --publish\n\n```\n\n\n## Update to the new AWS S3 API (which relates to Pulumi's BucketV2 objects)\n\nSadly there's no fully working example with the new BucketV2 api online, but here I provide one (after tinkering :D ).\n\nYou have to use:\n\n* https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketv2/\n* https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketpublicaccessblock/\n* https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketownershipcontrols/\n* https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketaclv2/\n* https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketwebsiteconfigurationv2/\n\nas already showcased in my example project based on Crossplane: https://github.com/jonashackt/crossplane-aws-azure/blob/main/upbound/provider-aws-s3/composition.yaml\n\n\n\n```typescript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as aws from \"@pulumi/aws\";\n\n// Create an AWS resource (S3 Bucket)\nconst nuxtBucket = new aws.s3.BucketV2(\"microservice-ui-nuxt-js-hosting-bucket\");\n\nconst bucketPublicAccessBlock = new aws.s3.BucketPublicAccessBlock(\"bucketPublicAccessBlock\", {\n    bucket: nuxtBucket.id,\n    blockPublicAcls: false,\n    blockPublicPolicy: false,\n    ignorePublicAcls: false,\n    restrictPublicBuckets: false,\n});\n\nconst bucketOwnershipControls = new aws.s3.BucketOwnershipControls(\"bucketOwnershipControls\", {\n    bucket: nuxtBucket.id,\n    rule: {\n        objectOwnership: \"ObjectWriter\",\n    },\n});\n\nconst bucketAclV2 = new aws.s3.BucketAclV2(\"bucketAclV2\", {\n    bucket: nuxtBucket.id,\n    acl: \"public-read\",\n}, {\n    dependsOn: [bucketOwnershipControls],\n});\n\n// We need to use the BucketWebsiteConfigurationV2 with the new AWS S3 API\nconst websiteConf = new aws.s3.BucketWebsiteConfigurationV2(\"websiteConf\", {\n    bucket: nuxtBucket.id,\n    indexDocument: {\n        suffix: \"index.html\",\n    }\n});\n\n// S3 Objects from Nuxt.js static site generation will be added through aws CLI instead of Pulumi like this\n// (see https://www.pulumi.com/docs/tutorials/aws/aws-ts-static-website/#deployment-speed):\n// aws s3 sync ../dist/ s3://$(pulumi stack output bucketName) --acl public-read\n\n// Export the name of the bucket\nexport const bucketName = nuxtBucket.id;\nexport const bucketUrl = websiteConf.websiteEndpoint;\n```\n\n\n## Links\n\nNuxt.js TypeScript Components cookbook: https://typescript.nuxtjs.org/cookbook/components/\n\nVSCode with Vetur Plugin: https://marketplace.visualstudio.com/items?itemName=octref.vetur\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonashackt%2Fmicroservice-ui-nuxt-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonashackt%2Fmicroservice-ui-nuxt-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonashackt%2Fmicroservice-ui-nuxt-js/lists"}