Frontends solides: configuration

Le manifeste d'application Ă  12 facteurs a apportĂ© une contribution significative au dĂ©veloppement et au fonctionnement des applications Web, mais cela a principalement affectĂ© les backends et contournĂ© les front-ends. La plupart des clauses du manifeste ne sont pas applicables aux frontaux ou sont exĂ©cutĂ©es par elles-mĂȘmes, mais avec le numĂ©ro 3 - configuration - il y a des questions.





Le manifeste d'origine indiquait: «Conservez la configuration au moment de l'exĂ©cution». En pratique, cela signifie que la configuration ne peut pas ĂȘtre stockĂ©e dans le code source ou dans l'artefact final. Il doit ĂȘtre transmis Ă  l'application au dĂ©marrage. Cette rĂšgle a des applications pratiques, en voici quelques-unes:





  1. Une application dans différents environnements doit accéder à différents backends. En production - vers l'API de production, lors de la préparation - vers la préparation de l'API et lors de l'exécution de tests d'intégration - vers un serveur fictif spécial.





  2. Pour les tests e2e, il est nécessaire de réduire le temps d'attente pour la réaction de l'utilisateur. Par exemple, si quelque chose se produit sur le site aprÚs 10 minutes d'inactivité, vous pouvez réduire cet intervalle à une minute pour un scénario de test.





SSR

Si l'application frontale contient SSR, la tùche devient un peu plus facile. La configuration est transmise en tant que variables d'environnement à l'application sur le serveur; lorsqu'elle est rendue, elle entre dans la réponse au client en tant que variables globales déclarées au <script>



tout début de la page. Sur le client, il suffit de récupérer ces variables dans la portée globale et de les utiliser.





Récemment, chez Aviasales, nous avons réalisé une application de rendu serveur de parties du site et avons été confrontés à ce problÚme. Le résultat est mon coéquipier zaopensorsil - isomorphic-env-webpack-plugin .





Le magnifique Next.js fait cela hors de la boßte , vous n'avez rien de spécial à faire.





RSE

, — . , , . , .





:





  1. – config.js



    , . , . — -, -, — -. — PR .





  2. . DefinePlugin



    Webpack. — . — , . , , . .





.





-

- :





  1. nginx, nginx -. nginx .





  2. , — API.





— , nginx . /user-api/path



https://user.my-service.io/path



, /auth-api/path



https://auth.other-service.io/path



.





nginx Docker-





1.19 Docker- nginx . .template



/etc/nginx/templates



. , .





nginx SPA :





server {
  listen   8080;

  root /srv/www;
  index index.html;
  server_name _;

  location /user-api {
    proxy_pass ${USER_API_URL};
  }

  location /auth-api {
    proxy_pass ${AUTH_API_URL};
  }

  location / {
    try_files $uri /index.html;
  }
}
      
      



Dockerfile :





FROM node:14.15.0-alpine as build

WORKDIR /app
#   
# ...

FROM nginx:1.19-alpine

COPY ./default.conf.template /etc/nginx/templates/default.conf.template

COPY --from=build /app/public /srv/www
EXPOSE 8080
      
      



, nginx .





, , .





.





. Caddy , Traefik .





API, - .





-, . , .





JS-:





window.__ENV__ = {
  USER_API_URL: 'https://user.my-service.io/',
  AUTH_API_URL: 'https://auth.other-service.io/',
};

      
      



, . . <script>



HTML- .





nginx Docker-





, , — API, . , , env.dict



:





BACK_URL
GOOGLE_CLIENT_ID
      
      



Bash- generate_env.sh



JS-:





#!/bin/bash
filename='/etc/nginx/env.dict'

#  JS-
config_str="window._env_ = { "

#    JS-
while read line; do
variable_str="${line}: \"${!line}\""
config_str="${config_str}${variable_str}, "
done < $filename

#  JS-
config_str="${config_str} };"

#    
echo "Creating config-file with content: \"${config_str}\""
echo "${config_str}" >> /srv/www/config.env.js

#  <script>    HTML-
sed -i '/<\/body><\/html>/ i <script src="/confit.env.js"></script>' *.html
      
      



Bash, . , .





nginx , nginx. cmd.sh



, :





#!/bin/bash

bash /etc/nginx/generate_env.sh

nginx -g "daemon off;"
      
      



Dockerfile:





FROM node:14.15.0-alpine as build

WORKDIR /app
#   
# ...

FROM nginx:1.19-alpine

#    Alpine  Bash,  
RUN apk add bash

COPY ./default.conf /etc/nginx/conf.d/

COPY --from=build /app/public /srv/www

COPY ./cmd.sh /etc/nginx/cmd.sh
COPY ./generate_env.sh /etc/nginx/generate_env.sh
COPY ./env.dict /etc/nginx/env.dict
 
EXPOSE 8080

CMD ["bash", "/etc/nginx/cmd.sh"]
      
      



— env.dict



.





.





, , SSR . isomorphic-env-webpack-plugin, : HTML.





Il y a un autre petit cas de bord dans ce schĂ©ma - gĂ©nĂ©ralement un hachage de contenu est ajoutĂ© aux noms des fichiers avec des ressources, de sorte que dans le navigateur tous les fichiers peuvent ĂȘtre mis en cache pour toujours par nom sans aucun problĂšme. Dans ce cas, vous devez lĂ©gĂšrement compliquer le script pour gĂ©nĂ©rer un fichier avec des variables, hacher le contenu et ajouter le rĂ©sultat au nom du fichier.





Conclusion

Un travail correct avec les paramÚtres de l'application frontale permet de créer des systÚmes fiables et faciles à utiliser. Il suffit de découpler la configuration de l'application et de la déplacer vers le runtime pour améliorer radicalement le confort des membres de l'équipe et réduire les erreurs potentielles.





Comment passez-vous les configurations aux applications clientes?








All Articles