We continue with the series of posts dedicated to ESP8266 and ESP32 by seeing how to serve a properly structured REST API from an ESP8266.
We will refer to the ESP8266, but the same code is compatible with the ESP32, adjusting the library names. At the end, you have the code for both ESP8266 and ESP32.
In the previous post, we saw how to consume a REST API acting as a client. Let’s remember that the REST API has become a standard in Web communication where, in a very summarized way, actions are transmitted through HTTP requests and we use the JSON format for data exchange.
Just as in the previous post we learned to connect to an existing REST API, for example, on another server, to perform actions from the ESP8266, it is interesting that when the ESP8266 acts as a server it provides its own REST API as a form of communication.
This is precisely what we are going to dedicate this post to: configuring an ESP8266 as a REST API server, interpreting requests and their data to execute actions, and exchanging information using the Arduino Json library that we saw in this post.
For example, we could return data from a temperature or humidity sensor. Or execute actions like moving a robot or activating a mechanism. Or even, simply communicate and exchange information with another device, such as a Raspberry PI or even another ESP8266.
Basically, we are going to emulate the REST API we use in all the posts, which is available at this link in Node.js version, and which we used as a server in the previous post where the ESP8266 was the client.
The code here starts to be somewhat advanced. Actually, we are going to see that it is not so difficult. But it is interesting that you have read and well understood the previous tutorials about the ESP8266.
So, let’s go!
Our main program code ends up being really simple, as usual.
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <ArduinoJson.h>
#include "config.h" // Replace with your network data
#include "API.hpp"
#include "Server.hpp"
#include "ESP8266_Utils.hpp"
void setup()
{
Serial.begin(115200);
ConnectWiFi_STA();
InitServer();
}
void loop()
{
}
Most of the ‘magic’ happens in the ‘Server.hpp’ file, where we associate the different verbs and actions available in the example to the corresponding functions of our API.
AsyncWebServer server(80);
void homeRequest(AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello, world");
}
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
void InitServer()
{
server.on("/", HTTP_GET, homeRequest);
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");
}
These API functions are defined in the ‘API.hpp’ file (logical, right?).
#include "ESP8266_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);
}
}
Finally, following the philosophy of the series to create reusable components to improve the cleanliness of our projects, we have the ‘ESP8266_Utils_APIREST.hpp’ file that we already used in the previous post, with useful functions for our Rest API.
This file contains reusable functions that we can use in our project, and that are used by the ‘API.hpp’ file to do the work of the Rest API.
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);
}
int GetIdFromURL(AsyncWebServerRequest *request, String root)
{
String string_id = request->url();
string_id.replace(root, "");
int id = string_id.toInt();
return id;
}
String GetBodyContent(uint8_t *data, size_t len)
{
String content = "";
for (size_t i = 0; i < len; i++) {
content .concat((char)data[i]);
}
return content;
}
Result
If we load this code into the ESP8266, and make the corresponding requests to our Rest API from Postman, we will see that we receive the responses correctly.

We can also verify that everything works correctly from the Arduino serial port console.

There you have it! We have already learned to configure and serve a correctly formatted Rest API from an ESP8266 using Json format for data exchange. You can use the basis of this example in your projects to provide a standardized form of communication.
So, are we done? Not at all! Consuming and, especially, serving Rest APIs is an important milestone on the road, but it is not an end. It is rather a good start. We still have to see how to use it in our projects, for example, to control a robot or make a datalogger.
And for that, in the next posts we will focus on interacting with our brand new Rest APIs from web pages served to the client from the ESP8266. See you soon!
Download the code
All the code from this post is available for download on Github.
Version for the ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples
Version for the ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples

