MicroAPIgRESTion is a lightweight, intuitive library tailored for building Async Restful APIs in MicroPython for use on microcontrollers. It streamlines the process of defining routes and handlers for various HTTP methods and query parameters. Initially crafted for the Raspberry Pi Pico W due to its lightweight nature.
- Route definition simplified with decorators
- Support for all HTTP methods, with explicit support for
GET
,POST
,PUT
,DELETE
, andPATCH
- Automatic parsing of query parameters
- WiFi connection management
- Method & Query parameters specific route handlers
- Network setup
- Routing
- Webpage Serving
- Handling other http methods
- Starting the server
- A more comprehensive example
To get started, save MicroAPIgRESTion.py
onto the device and create a WIFI_CREDENTIALS.py
file following the pattern/structure from WIFI_CREDENTIALS(example).py
, then save it on the device.
WIFI_CREDENTIALS.py
SSID = "SSID"
PASS = "PASS"
The ip of the device is being output in serial when the device connects to WI-Fi.
Then begin using by importing the library:
from MicroAPIgRESTion import *
You can define a simple route handler as follows:
@route('/hello')
async def greet_handler():
return 'Hello!'
Whatever the handler function returns will be the response of the request.
The @route
decorator accepts three attributes: url
, method
, and queryParams
.
For example:
@route('/hello', 'GET', 'first_name', 'last_name')
async def greet_handler(first_name, last_name):
return f'Hello, {first_name} {last_name}!'
In this example, /hello
is the url
, GET
is the method
, and first_name
& last_name
are the queryParams
. The queryParams
need to also be included as parameters for the handler function.
You can also use the @GET
, @POST
, @PUT
, @DELETE
, @PATCH
decorators for routing:
@GET('/nips', 'email', 'nrOfNips')
async def nips_handler(email, nrOfNips):
return f'{email}, {nrOfNips}'
Webpages can be served, while passing query parameters like so:
@GET('/index', 'name')
async def index_handler(name):
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Hello, {name}`s World!</title>
</head>
<body>
<h1 id="greeting">Loading...</h1>
<script>
document.getElementById('greeting').textContent = 'Hello, {name}`s World!';
</script>
</body>
</html>
"""
Or using a separate html file by returning the html_content function and passing as arguments the relative or absolute path of the html file and the query parameters like so:
@GET('/index0', 'name')
async def handler(name):
return html_content('index.html', params={'name': name})
When using the html_content function:
- Using css and js is following the usual way by linking the css file and the script file inside the html file like so:
<head>
<title>Hello, {name}`s World!</title>
<link rel="stylesheet" type="text/css" href="styles.css">
<script src="script.js"></script>
</head>
notes for noobs: html, css, js files should be saved on the micropython device
- query parameters can be used by using {param} inside any of the html, css, js files like so:
<title>Hello, {name}`s World!</title>
And now {name} from the title will have the value of the name query parameter.
Other http methods can be handled by using @route
.
For example:
@route('/options', 'OPTIONS')
async def options_handler():
#Do some options or smthn
return 'Did some options!'
This will handle requests made with the method OPTIONS
at http://(device-ip)/options
.
(device-ip)
is a placeholder for the actual ip of the device which is being output in serial when the device connects to WI-Fi.
After route handlers have been defined, running
asyncio.run(main())
will start the Async Rest Api, being able to handle requests at routes defined previously.
example.py
# import lib
from MicroAPIgRESTion import *
# importing Pin for use in future handler
from machine import Pin
# basic example
@GET('/nips', 'email', 'nrOfNips')
async def nips_handler(email, nrOfNips):
return f'{email}, {nrOfNips}'
# using device pins
@POST('/initializeDigitalPin', 'pin', 'mode')
async def digitalPin_init_handler(pin, mode):
pin = int(pin)
if mode == 'input':
Pin(pin, Pin.IN)
elif mode == 'output':
Pin(pin, Pin.OUT)
else:
return "Invalid"
return f"Digital pin nr.{pin} initialized as {mode}"
@POST('/digitalOutput', 'pin', 'state')
async def digitalPin_out_handler(pin, state):
pin = int(pin)
if state == 'high':
Pin(pin).value(1)
elif state == 'low':
Pin(pin).value(0)
else:
return "Invalid"
return f"Pin nr.{pin} set to {state}"
# HTML website serving
@GET('/index0', 'name')
async def handler(name):
return html_content('index.html', params={'name': name})
@GET('/index', 'name')
async def index_handler(name):
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Hello, {name}`s World!</title>
</head>
<body>
<h1 id="greeting">Loading...</h1>
<script>
document.getElementById('greeting').textContent = 'Hello, {name}`s World!';
</script>
</body>
</html>
"""
# different handlers based on query parameters
@route('/hello', 'GET', 'name')
async def hello_handler(name):
return f'Hello, {name}!'
@route('/hello')
async def greet_handler():
return 'Hello!'
# other http methods handlingthon
@route('/options', 'OPTIONS')
async def options_handler():
#Do some options or smthn
return 'Did some options!'
# Running server
asyncio.run(main())