Axios permite consumir un API REST desde una página web servida por un ESP32 con un código más limpio que usando XMLHttpRequest directamente.
El ejemplo está orientado a ESP32. En muchos casos también puede adaptarse a ESP8266 cambiando librerías y algunos detalles de pines.
La idea es integrar la librería Axios para realizar peticiones AJAX desde un cliente web a un API REST servido desde el ESP32.
Este paso encaja muy bien con interfaces en VueJS o Vuetify, porque separa la lógica de comunicación del resto de la aplicación y deja las llamadas al backend más ordenadas.
Para ello vamos a ver otro componente, la librería AXIOS, una popular librería en Javascript para realizar peticiones. AXIOS nos va a permitir hacer las peticiones AJAX en el cliente de una forma más “limpia”.
Para ver su uso vamos a basarnos en un API REST de ejemplo correctamente estructurado y vamos a adaptarlo para ilustrar la comunicación con el cliente usando Axios. El cambio afecta principalmente al frontend, por lo que toda la parte backend apenas necesita modificaciones.
Es decir, el programa principal del ESP32 sigue siendo.
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <ArduinoJson.h>
#include "config.h" // Sustituir con datos de vuestra red
#include "API.hpp"
#include "Server.hpp"
#include "ESP32_Utils.hpp"
void setup(void)
{
Serial.begin(115200);
SPIFFS.begin();
ConnectWiFi_STA();
InitServer();
}
void loop(void)
{
}
Así como la definición del servidor en el fichero ‘Server.hpp’.
AsyncWebServer server(80);
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
void InitServer()
{
server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
server.on("/item", HTTP_GET, getRequest);
server.on("/item", HTTP_POST, [](AsyncWebServerRequest * request){}, NULL, postRequest);
server.on("/item", HTTP_PUT, [](AsyncWebServerRequest * request){}, NULL, putRequest);
server.on("/item", HTTP_PATCH, [](AsyncWebServerRequest * request){}, NULL, patchRequest);
server.on("/item", HTTP_DELETE, deleteRequest);
server.onNotFound(notFound);
server.begin();
Serial.println("HTTP server started");
}
Y del API REST servido que tenemos definido en el fichero ‘API.hpp’
#include "ESP32_Utils_APIREST.hpp"
const char* PARAM_FILTER = "filter";
void getAll(AsyncWebServerRequest *request)
{
String message = "Get All";
Serial.println(message);
request->send(200, "text/plain", message);
}
void getFiltered(AsyncWebServerRequest *request)
{
String message = "Get filtered by " + request->getParam(PARAM_FILTER)->value();
Serial.println(message);
request->send(200, "text/plain", message);
}
void getById(AsyncWebServerRequest *request)
{
int id = GetIdFromURL(request, "/item/");
String message = String("Get by Id ") + id;
Serial.println(message);
request->send(200, "text/plain", message);
}
void getRequest(AsyncWebServerRequest *request) {
if (request->hasParam(PARAM_FILTER)) {
getFiltered(request);
}
else if(request->url().indexOf("/item/") != -1)
{
getById(request);
}
else {
getAll(request);
}
}
void postRequest(AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total)
{
String bodyContent = GetBodyContent(data, len);
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, bodyContent);
if (error) { request->send(400); return;}
String string_data = doc["data"];
String message = "Create " + string_data;
Serial.println(message);
request->send(200, "text/plain", message);
}
void patchRequest(AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total)
{
int id = GetIdFromURL(request, "/item/");
String bodyContent = GetBodyContent(data, len);
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, bodyContent);
if (error) { request->send(400); return;}
String string_data = doc["data"];
String message = String("Update ") + id + " with " + string_data;
Serial.println(message);
request->send(200, "text/plain", message);
}
void putRequest(AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total)
{
int id = GetIdFromURL(request, "/item/");
String bodyContent = GetBodyContent(data, len);
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, bodyContent);
if (error) { request->send(400); return;}
String string_data = doc["data"];
String message = String("Replace ") + id + " with " + string_data;
Serial.println(message);
request->send(200, "text/plain", message);
}
void deleteRequest(AsyncWebServerRequest *request) {
int id = GetIdFromURL(request, "/item/");
String message = String("Delete ") + id;
Serial.println(message);
request->send(200, "text/plain", message);
}
Ahora vamos con el cliente, donde sí vamos a tener cambios importantes.
El fichero ‘index.html’ cambia para incluir la librería AXIOS bien desde CDN (comentada) o desde la memoria del ESP8266.
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Axios</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div>
<div>
<button onclick="getAllRequest()">Get All</button>
<button onclick="getFilteredRequest()">Get Filtered</button>
<button onclick="getByIdRequest()">Get by Id</button>
</div>
<div>
<button onclick="postRequest()">Create</button>
</div>
<div>
<button onclick="patchRequest()">Update</button>
</div>
<div>
<button onclick="putRequest()">Replace</button>
</div>
<div>
<button onclick="deleteRequest()">Delete</button>
</div>
</div>
<!-- Desde CDN -->
<!--<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>-->
<script type="text/javascript" src="./vendor/axios.min.js"></script>
<script src="./js/API.js"></script>
</body>
</html>
Por otro lado, hemos externalizado todas las funciones de llamada a nuestro API REST en el fichero ‘API.js’, que contiene las funciones de ejemplo para las respectivas llamadas realizadas empleando la librería AXIOS.
function getAllRequest()
{
axios.get('item')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
});
}
function getFilteredRequest()
{
axios.get('item', {
params: {
filter : 'myFilter'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
});
}
function getByIdRequest()
{
id = 10;
axios.get('item/' + id)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
});
}
function postRequest()
{
axios.post('item', {
data: 'NewItem'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
});
}
function putRequest()
{
id = 10;
axios.put('item/' + id, {
data: 'NewItem'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
});
}
function patchRequest()
{
id = 10;
axios.patch('item/' + id, {
data: 'NewItem'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
});
}
function deleteRequest()
{
id = 10;
axios.delete('item/' + id)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
});
}
Resultado
Si cargamos todo en el ESP8266, y accedemos a la página web servida, veremos nuestra sencilla (y sí, feísima otra vez) interface para probar que conectamos correctamente con el API REST.

Si pulsamos en los distintos botones podremos comprobar cómo el backend en el ESP32 recibe correctamente las peticiones y muestra los datos oportunos en la consola de comandos.

Como vemos, es muy sencillo integrar Axios en el ESP32. Esta pequeña librería nos va a simplificar mucho el trabajo en nuestros proyectos, en especial al trabajar con API REST, haciendo nuestro código más sencillo y mantenible.
Por supuesto, es sólo un ejemplo para ilustrar la comunicación. En un proyecto real habría que definir el API REST, hacer el interface web completo, y hacer las acciones que queramos en el backend. Pero el ejemplo contiene todo el código necesario para hacer las peticiones CRUD (get/post/update/delete) tanto en backend como en frontend.
Con esta base podemos integrar el API REST con soluciones más completas en el frontend, como VueJS o Vuetify, sin cambiar la forma en la que el ESP32 expone sus endpoints.
Descarga el código
Todo el código de esta entrada está disponible para su descarga en Github.
Versión para el ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples
Versión para el ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples

