Drzewiej bywało, że tworząc aplikacje web pracowaliśmy na jednym wspólnym stacku, jak np. PHP + MySQL + HTML/CSS/JavaScript. Ten układ ma wiele wad, szczególnie jeśli posadzimy frontendowców i backendowców w osobnych biurach. Backendowcy świetnie ogarniają środowisko uruchomieniowe ale nie wiedzą jak łączyć ze sobą widoki i style. Z kolei frontendowcy pracują na plikach html lub podobnych i są zależni od backendowców.

Ratunkiem dla projektów fullstackowych jest rozdzielenie dwóch warstw aplikacji – wyglądu i funkcjonowania. W tym ozolnym procesie pomagają zewsząd napływające biblioteki front i back: Composer, NPM, Vue.js, React, Angular czy choćby Docker / Kubernetes. Zadanie troski o infrastrukturę możemy powierzyć DevOpsowi, a całą resztę dzielimy na pół. Tylko jak ma ze sobą gadać frontend z backendem?

Jednym z takich pomocnych formatów jest XML i JSON. Tym drugim zajmiemy się w tym artykule i za pomocą biblioteki Axios wyświetlimy dane z backendu we frameworku Vue.js. Żeby było trudniej posłużymy się uwierzytelnianiem przez login i hasło by nasze API pozostało zabezpieczone.

Przygotowania

Do ugotowania naszej zupy potrzebujemy składników. Takim „mięchem” czyli danymi niech będzie źródło z listą użytkowników właśnie w formacie JSON. Włoszczyzna w rosole jest równie ważna, stanowi podstawę smaku zupy: użyjemy tutaj Vue.js i biblioteki Axios, która w kilku linijkach pozwala nam pobrać dane z adresu URL.

Zaczynamy od instalacji narzędzia konsolowego dla vue:

npm install -g @vue/cli
// lub
yarn global add @vue/cli

Następnie tworzymy naszą aplikację z podstawowego szkieletu:

vue create nazwaaplikacji

Mamy do wyboru dwie opcje: instalujemy podstawowy stack Vue2 (babel i eslint) lub samodzielnie wybieramy zestaw. Proponuję dla ułatwienia pierwszą opcję i naciśnięcie ENTER.

W tym momencie instalowane są wszystkie niezbędne biblioteki:

I voila! Możemy już rozpocząć pracę nad aplikacją. Przechodzimy do katalogu z nazwą projektu i jeśli wykonamy polecenie:
npm run serve

Zobaczymy ekran startowy:

Ekran powitalny Vue.js

Aplikacja

Nasza aplikacja na samym początku wygląda dość ubogo, ale pozwala łatwo rozpoznać jak konstruujemy widok z komponentu:

Aby zadziała się magia, przygotujmy komponent do wyświetlenia listy użytkowników w pliku Users.vue w katalogu components:
<template>
  <h3>Application Users:</h3>
</template>
<script>
  export default {
    name: 'Users',
  }
</script>

W tym komponencie widzimy, że przygotowaliśmy przestrzeń na wyświetlenie listy użytkowników z odpowiednim nagłówkiem ale nie mamy żadnego wskaźnika danych. Przejdź więc do App.vue i zamień blok <template></template> na taki:

<template>
  <div id="app">
    <Users />
  </div>
</template>

Następnie wskażmy, skąd pochodzi komponent Users, do którego się odnosimy. W miejsce <script></script> wstaw blok wskazujący na nasz komponent z użytkownikami:

<script>
import Users from './components/Users.vue'

export default {
  name: 'App',
  components: {
    Users
  }
}
</script>

W tym momencie powinieneś już zobaczyć nagłówek komponentu Users na pustej stronie:

Axios – Zapytanie do API

Czas na pobranie danych z repozytorium. Posłużymy się tutaj ogólnodostępną bazą jsonplaceholder.typicode.com i przykładowym zestawem danych, które w zupełności wystarczą do pokazania funkcjonalności naszej aplikacji.

W zależności od managera pakietów, którego wolimy używać, dodajemy bibliotekę Axios do naszego projektu tak:

yarn add axios

albo tak:

npm install axios

W miejscu skryptu w Users.vue wstawiamy wywołanie axiosa, oraz definicję funkcji, która pobierze dane z zewnętrznego adresu:

<script>
  import axios from 'axios';
  export default {
    name: 'Users',
    data() {
      return {
        users: null,
      };
    },
    created: function() {
      axios
        .get('https://jsonplaceholder.typicode.com/users')
        .then(res => {
          this.users = res.data;
        })
    }
  }
</script>

Powyższy kod jest bardzo prostym wywołaniem zewnętrznego zasobu: definiujemy pustą właściwość komponentu o nazwie users, po czym uzupełniamy go danymi z JSONa. Możesz wywołać adres widoczny w funkcji get w przeglądarce by sprawdzić, jakie właściwości obiektu User są dostępne: nam, username, website itp.

Zaktualizujemy też nasz blok template, by zawierał informacje o użytkownikach:

<template>
  <div class="container">
    <h3>Application Users:</h3>
    <table class="table">
      <thead>
        <tr>
          <th scope="col">ID</th>
          <th scope="col">Name</th>
          <th scope="col">E-Mail</th>
          <th scope="col">Phone</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="user in users" v-bind:key="user.id"> 
          <th scope="row">{{user.id}}</th>
          <td>{{user.name}}</td>
          <td>{{user.email}}</td>
          <td>{{user.pone}}</td>
        </tr>
      </tbody>
    </table> 
  </div> 
</template>

W tym momencie powinniśmy już widzieć na ekranie pożądane informacje:

To jeszcze nie koniec! Jako, że posługujemy się synchronicznym zapytaniem, może się zdarzyć, że dane których zażądamy będą dostępne z opóźnieniem. Generalnie zawsze należy zakładać, że po drugiej stronie jest system, który potrzebuje czasu na wygenerowanie zestawu danych. Na szczęście mamy:

Wywołania Asynchroniczne

Dzięki async i await, konstrukcjach zgodnych z ES8, możemy zdefiniować asynchroniczność w naszych funkcjach i dzięki temu, bez dodatkowych bibliotek i wysiłków wskazać, że odpowiedź może przyjść nieco później. Dlatego dodajmy do wywołania naszej funkcji pobierającej dane przedrostek async, a przed nazwą axios wskażmy, że to właśnie to wywołanie będzie oczekiwało na odpowiedź:

created: async function() {
      await axios
        .get('https://jsonplaceholder.typicode.com/users/')
        .then(res => {
          this.users = res.data;
        })
    }

W tym momencie nie zmienia się zbyt wiele, jednak w przypadku pobierania dużej ilości danych z zewnętrznego, odległego źródła zauważymy, że szkielet aplikacji jest gotowy, a dane pojawiają się na ekranie po czasie.

Extra: Uwierzytelnianie

Przykładowe dane są dobre do testowania aplikacji w wersji developerskiej ale w prawdziwym wydaniu będziemy potrzebowali prawdziwych, często wrażliwych czy osobistych informacji. Nie możemy oczekiwać, że te dane będą ogólnodostępne.

Bardzo wiele aplikacji zabezpiecza się rozmaitymi tokenami, protokołem OAuth czy tzw. Basic Authentication, czyli loginem i hasłem. Gdyby zaszła potrzeba komunikacji z aplikacją z pomocą takiego interfejsu, wykorzystaj funkcę btoa, która jest dostępna w Vue.js, ale np. w React została już zastąpiona pakietem base-64:

created: async function() {

      var uname = 'login';
      var pword = 'haslo';
      var credentials = btoa(uname + ':' + pword);
      var basicAuth = 'Basic ' + credentials;
      await axios
        .post('https://adresapi.com', {}, {
            headers: { 
                'Authorization': + basicAuth,
                Accept: 'application/json',
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            auth: {
              username: uname,
              password: pword
            }
        })
        .then(res => {
          this.offers = res.data.offers;
        })
    }

Powyższy przykład pokazuje jak wygodnie możemy zdefiniować parametry połączenia, które przed pobraniem danych spróbuje się uwierzytelnić na docelowym serwerze. Oczywiście to przykład roboczy – w aplikacji produkcyjnej warto dane logowania trzymać w oddzielnym pliku lub skorzystać ze zmiennej środowiskowej.

To już koniec, mam nadzieję, że tutorial się przydał, by zrozumieć pracę z api w Vue.js, oraz wyświetlanie danych, które są dostępne na zewnątrz naszej warstwy frontendowej. Może się także zdarzyć, że przeglądarka nie zaakceptuje tzw. żądania krzyżowego (CORS) i dlatego warto po stronie backendu udostępnić dane tylko wybranym klientom:

Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Methods: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Allow-Headers: Authorization
Access-Control-Allow-Type: application/json