api-rest-con-esp8266-axios-vuejs-y-vuetify

Cómo hacer un Api Rest con ESP8266 o ESP32, Axios, VueJs y Vuetify

Continuamos con nuestra sección de tutoriales del ESP8266 y el ESP32 viendo cómo integrar una aplicación que combine VueJs, Vuetify, Axios y se comunique con el ESP8266 a través de un API Rest.

Haremos referencia al ESP8266, pero el mismo código es compatible para el ESP32, ajustando el nombre de las librerías. Al final tenéis el código tanto para el ESP8266 como para el ESP32.

En la entrada anterior de esta serie nos quedamos en que habíamos integrado una aplicación en VueJs, que empleaba la librería Axios para comunicarse a través de un API Rest con el ESP8266 actuando como backend.

Nuestra aplicación podría ser un buen ejemplo de arquitectura pero… reconozcamos que era fea como ella sola. ¡Pero esto está a punto de cambiar! Para ello vamos a incorporar a nuestro coctel Vuetify, que ya vimos en esta entrada.

esp8266-vue-vuetify-axios-resultado

El objetivo es conseguir una funcionalidad parecida a la anterior, es decir una demo que muestre cómo realizar las distintas acciones a través del API Rest, pero con esta estética más moderna y adecuada.

¿Tiene mejor pinta verdad? Pues “al tajo”. El código del backend, es decir, del ESP8266, es idéntico al de las dos últimas entradas. Así que, nuevamente, me evito copiarlo otra vez y os remito a las entradas anteriores o al código en Github.

Lo que va a cambiar es la página web servida.

El fichero index.html pasa a ser

<!DOCTYPE html>
<html>
  <head>
    <title>ESP8266 Vuetify + Axios</title>
    <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Desde CDN -->
    <!--<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">-->
    <!--<link href="https://cdn.jsdelivr.net/npm/vuetify@1.5.16/dist/vuetify.min.css" rel="stylesheet">-->

    <link href="./vendor/google-fonts.css" rel="stylesheet">
    <link href="./vendor/vuetify.min.css" rel="stylesheet">
   </head>
<body>
    <div id="app">
      <v-app id="inspire">
        <v-container style="max-width: 600px">
          <v-form>
            <v-container>
              <v-layout row>
                <v-flex xs12 sm6 md3>
                  <v-btn color="info" @click="getAll">Get All</v-btn>
                </v-flex>
              </v-layout>

              <v-layout row>
                <v-flex xs12 sm6 md3>
                  <v-btn color="info" @click="getById">Get</v-btn>
                </v-flex>

                <v-flex xs12 sm6 md3>
                  <v-text-field v-model.number="getId" label="Id" solo type="number"></v-text-field>
                </v-flex>
              </v-layout>

              <v-layout row>
                <v-flex xs12 sm6 md3>
                  <v-btn color="info" @click="updateById">Update</v-btn>
                </v-flex>

                <v-flex xs12 sm6 md3>
                  <v-text-field v-model.number="updateId" label="Id" solo type="number"></v-text-field>
                </v-flex>

                <v-flex xs12 sm6 md3>
                  <v-text-field v-model="updateText" label="Text" solo></v-text-field>
                </v-flex>
              </v-layout>
            </v-container>
          </v-form>

          <v-divider class="mb-3"></v-divider>

          <v-text-field v-model="newItem" label="Add new item" solo @keydown.enter="create">
            <v-fade-transition slot="append">
              <v-icon v-if="newItem" @click="create">add_circle</v-icon>
            </v-fade-transition>
          </v-text-field>

          <v-card>
            <v-toolbar color="cyan" dark>
              <v-toolbar-title>Total: {{ items.length }}</v-toolbar-title>
            </v-toolbar>

            <v-fade-transition class="py-0" group tag="v-list">
              <template v-for="(item, i) in items">
                <v-divider v-if="i !== 0" :key="`${i}-divider`" ></v-divider>

                <v-list-tile :key="`${i}-${item.text}`">
                  <v-list-tile-action>
                    <v-list-tile-title v-text="item.id"></v-list-tile-title>
                  </v-list-tile-action>

                  <v-list-tile-action>
                    <v-list-tile-content>

                      <v-list-tile-title v-text="item.text"></v-list-tile-title>
                    </v-list-tile-content>
                  </v-list-tile-action>

                  <v-spacer></v-spacer>
                  <div>
                    <v-btn color="error" v-model="item" @click="remove(item)">Delete</v-btn>
                  </div>
                </v-list-tile>
              </template>
            </v-fade-transition>

            <v-snackbar v-model="snackbar" :bottom="true" :timeout="timeout">
              {{ text }}
              <v-btn color="pink" flat @click="snackbar = false">
                Close
              </v-btn>
            </v-snackbar>
          </v-card>
        </v-container>
      </v-app>
    </div>

  <!-- Desde CDN -->
  <!--<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>-->
  <!--<script src="https://cdn.jsdelivr.net/npm/vuetify@1.5.16/dist/vuetify.min.js"></script>-->
  <!--<script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script>-->

  <script type="text/javascript" src="./vendor/vue.min.js"></script>
  <script type="text/javascript" src="./vendor/vuetify.min.js"></script>
  <script type="text/javascript" src="./vendor/axios.min.js"></script>
    
  <script type="text/javascript" src="./js/API.js"></script>
  <script type="text/javascript" src="./js/app.js"></script>
   </body>
</html>

El fichero JavaScript de la página también debe modificarse para adaptarse al nuevo Framework Vuetify, quedando así.

new Vue({
  el: '#app',
  
  data: () => ({
    items: [
      {
        id: 0,
        done: false,
        text: 'Init 0'
      }
    ],
    
    newItem: null,
    getId: null,
    updateId: null,
    updateText: null,
    
    snackbar: false,
    timeout: 1500,
    text: '',
    
    lastId : 0,
  }),
  
  computed: {
  },
  
  methods: {
    create () {
      lastId = Math.max(...this.items.map(d => d.id));
      this.items.push({
        id: ++this.lastId,
        done: false,
        text: this.newItem
      })
      
      API_post(this.newItem)
      this.newItem = null
    },
    getAll() {
      this.text = "Getting all"
      this.snackbar = true
      
      API_getAll()
      
      lastId = Math.max(...this.items.map(d => d.id));
      this.items.push({id: ++this.lastId, text: 'Loaded all '})
      this.items.push({id: ++this.lastId, text: 'Loaded all '})
    },
    getById() {
      if(this.getId == null) return;
      if(((typeof this.items.find(x => x.id === this.getId) !== 'undefined'))) return;
      
      this.text = "Getting by Id " + this.getId
      this.snackbar = true
      
      API_get(this.getId)
      
      this.items.push({id: this.getId, text: 'Loaded by Id'})
    },
    updateById(id) {
      if(this.updateId == null || this.updateId == null) return;
      
      this.text = "Updating " + this.updateId + " with " + this.updateText
      this.snackbar = true
      
      API_put(this.updateId, this.updateText)
      
      this.items.find(x => x.id === this.updateId).text = this.updateText
    },
    remove(item) {
      this.text = "Deleting " + item.id
      this.snackbar = true
      
      API_delete(item.id)
      
      this.items.splice(this.items.indexOf(item), 1)
    }
  }
})

Resultado

Subimos nuestros contenidos al SPIFFS como estamos acostumbrados, y pulsamos los distintos botones para comprobar que todo funciona correctamente en la consola de desarrollo del navegador.

esp8266-vuejs-axios-console

Así como que llegan correctamente al ESP8266 como backend, a través del terminal del puerto serie.

esp8266-vuejs-axios-serial

¡Y ya hemos terminado con esta serie de tutoriales! Hemos integrado un API Rest, Json, Axios, Vue y Vuetify en una app de demo funcional y bonita. ¡Podéis estar orgullosos! (palmadita en la espalda)

¿Entonces ya hemos terminado? Para nada. En las próximas tres entradas veremos cómo crear un API para el ESP8266 que permita leer entradas analógicas/digitales y realizar acciones, a través de Websocket, empleando también Vue en el frontend. ¡Hasta pronto!

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github.

github-full

Versión para el ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples

Versión para el ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples