# Build Lifecycle
From the code that you write until the code that runs into the client's browser
there is many stages of compilation, packing, deployment, etc. It's important to
understand these steps and how the data can flow through them.
Because we want a deployment system that automatically provides the domain name
and the SSL certificate and copies the database and many other goodies that
happen under the hood, we need to follow a given set of rules.
## Important stages
There is no official list of stages for an app and this list is not exhaustive,
however those are the important parts of the life of our apps through the lens
if what is available to you and what you can do with it.
### Coding
When you code, it's usually happening on your machine. It's easy because you
have full control over it and no constraints. This means that it's very easy to
start writing code that won't work once onto the servers.
A few attention points and techniques:
- Make sure to have configured properly the Django storages to use S3 for
media storage. Don't use local media storage.
- Use the Goku endpoint (or any other kind of tunnel like ngrok) in order to
be able to test your app through HTTPS. If you have content policy or CORS
errors, it's the time to work out a solution. If the tunnel is too slow, at
least test once through it.
- Make sure that all the files that your code requires can be committed and
are committed (see the {doc}`version-control` section for more details on
what can be committed)
- Don't use environment variables outside safe locations (more on that later).
In practice, it means **don't use** `process.env` in Vue code.
The code that you write must work in such a way that there is no need to change
(or create) a single file in your repository to make it work. No matter the
configuration, the context or the server this code must be able to run as-is on
all the target platforms.
### Building
The build time is when you transform your code into something that can be
executed by the target platform. This includes:
- Transpiling JS/CSS code
- Creating minified bundles of code for the client
- Regrouping static assets into one directory
- Compressing static assets
What you're building is simply another form of your code. If you build your code
twice it should give exactly the same output. Hence, you can understand that at
the time of build there is **no access to environment variables**. You simply
cannot depend on environment variables to build your code, just like your code
is not generated from environment variables.
```{note}
There is a slight exception to this in Django's world. Indeed, `manage.py` will
not accept to start of `SECRET_KEY` and `DATABASE_URL` are not set. Fortunately
you don't need to have the right values of those variables for `collectstatic`
to work properly. This is an example out of a `Dockerfile`:
env DATABASE_URL='postgresql://dum:dum@localhost/dummy' SECRET_KEY='dummy' python manage.py collectstatic --no-input --clear
As you can see, the values here are dummy and don't need to be anything "real"
as they will be used by the build process.
On the other hand, please make sure that you don't create in your code a
build-time dependency to environment variables.
```
The output of this build step will be stored somewhere before going to the next
phase. For example if you're building a Felix project then the build artefacts
are sent to a Git repo under `with-madrid/deploy` while if you're building for
Kubernetes it will be sent to a private Docker repository.
### Deploying
The deployment phase can vary _a lot_ depending on what you're doing, but
basically it will take the build artifacts from the build phase and copy them to
the server while also registering the service in proper locations and starting
the whole thing.
This, again, it depends on if you're using Felix or Kubernetes or something
else, but it's the same underlying idea every time.
```{note}
On Felix deploys, the build and deploy phase are distinct but happen indeed
automatically one after the other. It's easy to recognize: the weird bash script
is the build phase and when it moves to Ansible it's the deploy phase.
```
### Run time
When the code starts running on the server, that's when it finally gets access
to environment variables. Hooray!
But wait. Didn't way say that we can't use environment variables from Vue code?
Ain't we fucked already?
Fortunately, no! There is easy solutions to the most common problems that we
face.
```{note}
Not all existing projects follow these guidelines, for many legacy reasons.
There is no point trying to reproduce past mistakes, please follow the patterns
below instead.
```
#### Finding the API
Ok so you've got an `API_URL` variable that gives you the base path to the API.
How do you get this to the client?
Let's say that your front-end is `https://front` and your API is `https://api`.
Suppose that you want to create an item on the API by doing a
`POST https://api/api/item`. What do you need to do that?
- First you need to know the URL of the API, aka the content of `API_URL`. But
but we said no environment variables.
- Second you need to avoid getting your ass busted by CORS, because a POST
from `https://front` to `https://api` ain't gonna fly.
The easiest solution to this is to use the magic of proxies 🪄
Here we're going to explore a Nuxt-specific solution. Typically, in your
`nuxt.config.js` file you can have something like that:
```javascript
module.exports = {
plugins: ["~/plugins/axios"],
modules: [["@nuxtjs/axios", { proxy: true }]],
proxy: {
"/api/": process.env.API_URL,
},
};
```
```{note}
We're putting the `axios` plugin in this demo to outline the fact that there
is a `proxy` option that you need to set to true. However it's not strictly
required to use the `axios` module to make the proxy work (but save yourself
some time and use the `axios` module).
```
```{note}
The `proxy` section of this configuration makes use of environment variables.
This is one of the few authorized places to use them.
```
By doing so, we're solving both problems at the same time:
- From your code you make a `POST /api/items`
- Which results in a `POST https://front/api/items`
- Which is proxied by the server and generates a `POST https://api/api/items`
No CORS issue, no environment variables at build time. Of course there many
[options](https://github.com/http-party/node-http-proxy#options) to the proxy
module that you can modulate to fit your needs but this is the basic idea.
#### Using GTM
Project managers are always eager to add a lot of JavaScript into our pages and
this generally starts with GTM. In a nutshell, GTM (or other tag managers like
Matomo Tag Manager) are systems which allow to add tracking pixels and other
analytical tools into the page without changing the code of the page itself.
It's basically some JS code that will load as soon as the page is displayed and
will do as instructed by the project managers from their management interface.
To sum up, it's a good practice to include a tag manager into the project
because it will increase the flexibility of the app to include different
tracking tools without making changes to the code.
Another item is that the code must be integrated exactly following Google's
instructions (which are kind of not nice) because this helps proving the
ownership of the website on other Google tools. If the guidelines from Google
are not followed exactly, this breaks the authentication mechanism and it can
become annoying.
##### Django
From a purely Django app, using something like GTM (or any of the
marketing/tracking tools that we use) is usually fairly easy. You just have to
include the code into the `base.html` of your project's template and then inject
the `GTM_ID` into it.
To do so:
1. Create a `GTM_ID` environment variable
2. Read this variable from `settings.py`, something like
`GTM_ID = os.getenv('GTM_ID')`
3. Create a
[template context processor](https://docs.djangoproject.com/en/3.2/ref/templates/api/#writing-your-own-context-processors)
that will inject this value in all template renders
```{note}
It is much simpler to use a context processor than to manually inject the value
each time you call `render()` to create a custom template tag.
```
##### Wagtail
In some cases, users are going to expect more configurability over what they can
put in the HTML. In those cases, instead of reading the values from environment
variables it is best to let some option to the user to configure this and store
it into database.
You can for example create the `raw_html_head` and `raw_html_body` fields which
you let the admin change from the Promote tab of the root page and then in the
template you fetch those values from DB.
##### Nuxt
That's not the simplest thing to do. There is an example in the TFS project so
I'll make links from here.
1. Create a plugin that will manage the JS you want to include. By example the
[GTM](https://gitlab.com/with-madrid/tfs/front/-/blob/37858c749525c75f40f6d0ca710b6254cf19be5c/plugins/gtm.ts)
plugin from TFS declares the `dataLayer` variable and sends an event to it
every time there is a new page displayed (because GTM doesn't understand
HTML5 routing)
2. List that plugin into `nuxt.config.js`
3. Hook up to `vue-renderer:ssr:templateParams`
[to inject](https://gitlab.com/with-madrid/tfs/front/-/blob/37858c749525c75f40f6d0ca710b6254cf19be5c/nuxt.config.js#L151)
the parameters you need
4. Modify/create `app.html`
[to include the snippets](https://gitlab.com/with-madrid/tfs/front/-/blob/37858c749525c75f40f6d0ca710b6254cf19be5c/app.html)
provided by GTM and inject there the value received thanks to the
`vue-renderer:ssr:templateParams` hook
```{note}
Maybe you noticed that we could have done the same thing using the `head`
section of `nuxt.config.js` but for some reason we're doing this complicated
maneuver. It's simple: the `head` is evaluated at build time and cannot take
environment variables into account. Sad but true.
```
```{note}
You'll also realize that there is a `@nuxt-gtm` module which we don't use. The
reasons are the same: it is simply too annoying to give it runtime config. The
TFS implementation is simple enough on top of complying with Google's
requirements, we don't need to use that module.
```
This is of course fairly artisanal and will have to be adapted to the specific
needs of your project, but that can serve as a blueprint.
#### Getting a token to the front-end
Very, very rarely you'll need to get a token or some kind of value from the Vue
code. If you reach this point it's probably that you've ignored other advices in
this guide.
Nuxt provides you with a way to safely get bits of data available in Vue code.
It's the
[runtime config](https://nuxtjs.org/docs/2.x/directory-structure/nuxt-config#runtimeconfig)
section of the configuration. As the name might suggest, this is configuration
that is available at runtime.
Imagine that we have a website with 2 CSS styles that were made for two brands
of the same company that have the same website. Let's say it's brands
`very-nice` and `super-great`. As they ask you to host both websites separately,
you've created an environment variable `BRAND=very-nice` or `BRAND=super-great`
depending on the instance.
In that case your nuxt configuration file would look like:
```javascript
module.exports = {
publicRuntimeConfig: {
brand: process.env.BRAND || "very-nice",
},
};
```
```{note}
The `publicRuntimeConfig` and `privateRuntimeConfig` options are authorized
locations for environment variables, as they are specifically designed for that
purpose.
```
Doing so allows you to get this information from Nuxt code:
```html
{{ brandTitle }}
```
What happens under the hood is that when you ask Nuxt to serve you the page it
will modify its internal `__NUXT__` object (look for it in the HTML) to contain
this value. This way when the JS on the client side starts running, it can get
the value from there and use it.
If instead of `this.$config.brand` you used `process.env.BRAND`, the value would
have been replaced at build time and hardcoded as-is in the minified code. This
would go against the goal of being able to deploy the same code in different
environments.
```{note}
This can only work with Nuxt in `universal` mode. But we use Nuxt specifically
for its `universal` mode and its ability to do SSR, so all is fine. If you want
to use Nuxt in another mode then you need to seriously question your choices as
this guide won't be able to help you.
```
If you look into TFS, there is also a
[Iubenda](https://gitlab.com/with-madrid/tfs/front/-/blob/37858c749525c75f40f6d0ca710b6254cf19be5c/plugins/iubenda.js)
plugin integrated that makes use of this technique to start itself.
### Server vs Client-side rendering
You'd think that once the code is running and your environment variables are
injected through the proper channels you'd be out of your misery. Haha. Lol.
The **core** feature of Nuxt and the main reason why we use it instead of, let's
say, vue-cli, is its ability to do Server Side Rendering. Maybe let's do a bit
of history at this point:
- Initially it was simple. The server generates HTML, sends HTML to the
browser, the browser displays the page, that's it.
- Then it became pretty common to have JavaScript on the client side to alter
details of the page (zoom on images, display details about a post, click on
some stars, etc). This is the jQuery era.
- Then those details became huge parts of the page, at which point the JS
framework craze started. The idea was: render 100% of the content on the
client side and communicate with the server through an API. If apps can do
it why can't web apps do it? This is the SPA era.
- That was fantastic but presented one main big problem: Google doesn't run
SPAs (or at least did not at the time). Which means that your whole app was
just a blank page for Google. This is when JS frameworks decided that
instead of just sending an blank page and doing the rendering on the client
they would do a first render on the server, send the HTML output and then
keep on rendering on the client. This is the famous SSR that we're talking
about here.
In theory SSR is the best of both worlds and in practice it is indeed a lot of
things that were complicated before. Using Vue components and splitting them by
page, having the whole HTML code + just the required CSS to boost up the first
paint, etc. These things are really important for performance and using Nuxt
makes them super easy to get (as you don't actually do anything to get them).
However this comes at a price, which is that you need to understand the
limitations of SSR. Specifically, on the server there is no DOM, no canvas, no
video, no audio, none of all that. You can render HTML but not attach events.
Let's see how to navigate in those waters.
#### DOM stuff
The good news about most of the DOM operations is that it's Vue's job to
manipulate the DOM. So all you have to do is to only rely on Vue APIs (either
through the `template` part of your `.vue` files either through the `render()`
function).
If you absolutely need to use native DOM functions (like `findElementById()`,
`addEventListener()`, etc) then think again because it is highly unlikely that
you actually need them. As a reminder, Vue covers
[event binding](https://vuejs.org/v2/guide/events.html),
[changing the style or class of elements](https://vuejs.org/v2/guide/class-and-style.html)
and other [input bindings](https://vuejs.org/v2/guide/forms.html).
If you still absolutely need to use them, you can create sections of the code
that will not execute if you're on the server:
```javascript
if (process.client) {
console.log("this is only executed on the browser");
}
```
This is also true if you're invoking a component that uses some kind of canvas
or other things that Vue doesn't manage. In those cases you can do:
```html
Hello