mio
mio copied to clipboard
A powerful HTTP networking package for MoonBit.
oboard/mio
A powerful and modern HTTP networking library for MoonBit with multi-backend support.
Features
- 🚀 Async/Await Support: Built-in async operations with
@mio.run - 🌐 Complete HTTP Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, CONNECT, TRACE
- 📄 JSON Handling: Seamless JSON parsing and response handling
- 📁 File Downloads: Built-in file download capabilities with custom save paths
- 🌊 Stream Processing: Real-time data streaming with callback support
- 📦 Binary Data Support: Native handling of binary data with unified
Bytesinterface across all backends - 🎯 Multi-Backend: Support for Native (libcurl), JavaScript (Fetch API), and WASM
- ⚡ Type Safety: Full MoonBit type system support with error handling
- 🔧 Flexible Options: Headers, credentials, modes, and request customization
Backend Support
Native Backend (native, llvm)
- HTTP Engine: libcurl for robust HTTP requests
- Features: All HTTP methods, file downloads, full header support
- Dependencies: Requires libcurl system library
- Performance: Optimized for server-side and native applications
JavaScript Backend (js)
- HTTP Engine: Fetch API
- Features: Browser and Node.js compatibility
- Dependencies: No external dependencies
- Performance: Optimized for web applications and frontend development
WebAssembly Backend (wasm, wasm-gc)
- HTTP Engine: WASM-compatible implementation
- Features: Cross-platform WebAssembly support
- Dependencies: WASM runtime environment
- Performance: Optimized for WASM applications
Installation
Add to your moon.mod.json:
{
"deps": {
"oboard/mio": "0.3.0"
}
}
Quick Start
Basic GET Request
@mio.run(fn() {
match (try? @mio.get("https://api.github.com")) {
Ok(response) => {
println("Status: " + response.statusCode.to_string())
println("Response: " + response.text())
}
Err(e) => println("Error: " + e.to_string())
}
})
POST Request with JSON Data
@mio.run(fn() {
match (try? @mio.post("https://httpbin.org/post",
data={
"name": "MoonBit",
"version": "0.3.0",
"features": ["async", "http", "json"]
})) {
Ok(response) => {
println("Posted successfully!")
println(response.unwrap_json())
}
Err(e) => println("Failed: " + e.to_string())
}
})
Custom Headers and Options
@mio.run(fn() {
let headers = {
"Authorization": "Bearer your-token",
"User-Agent": "MoonBit-App/1.0",
"Accept": "application/json"
}
match (try? @mio.get("https://api.example.com/data",
headers=headers,
credentials=SameOrigin,
mode=CORS)) {
Ok(response) => {
if response.statusCode == 200 {
let data = response.json()
// Process your data
}
}
Err(e) => println("Request failed: " + e.to_string())
}
})
File Download
@mio.run(fn() {
// Download with custom filename
match (try? @mio.download("https://api.github.com/repos/moonbitlang/core",
save_path="github_repo.json")) {
Ok(_) => println("File downloaded successfully!")
Err(e) => println("Download failed: " + e.to_string())
}
// Download with dynamic filename based on headers
match (try? @mio.download("https://example.com/file.zip",
save_path_fn=fn(headers) {
match headers.get("content-disposition") {
Some(disposition) => extract_filename(disposition)
None => "downloaded_file.zip"
}
})) {
Ok(_) => println("File downloaded with dynamic name!")
Err(e) => println("Download failed: " + e.to_string())
}
})
Stream Requests
@mio.run(fn() {
// Stream processing with real-time data handling
let chunks = []
match (try? @mio.get_stream("https://api.example.com/stream",
fn(chunk) {
println("Received chunk: " + chunk)
chunks.push(chunk)
// Process each chunk as it arrives
})) {
Ok(response) => {
println("Stream completed with status: " + response.statusCode.to_string())
println("Total chunks received: " + chunks.length().to_string())
}
Err(e) => println("Stream failed: " + e.to_string())
}
})
Note: Stream functionality is currently under development for native backend due to callback compatibility issues between MoonBit and C function pointers. It works properly on JavaScript and WASM backends.
API Reference
HTTP Methods
All HTTP methods support the same optional parameters:
headers?: Map[String, String]- Custom HTTP headerscredentials?: FetchCredentials- Request credentials (Omit, SameOrigin, Include)mode?: FetchMode- Request mode (CORS, NoCORS, SameOrigin, Navigate)
// GET request
(try? @mio.get(url, headers?, credentials?, mode?))
// POST request with body or JSON data
(try? @mio.post(url, body?, data?, headers?, credentials?, mode?))
// Other HTTP methods
(try? @mio.put(url, body?, headers?, credentials?, mode?))
(try? @mio.delete(url, body?, headers?, credentials?, mode?))
(try? @mio.patch(url, body?, headers?, credentials?, mode?))
(try? @mio.options(url, body?, headers?, credentials?, mode?))
(try? @mio.head(url, body?, headers?, credentials?, mode?))
(try? @mio.connect(url, body?, headers?, credentials?, mode?))
(try? @mio.trace(url, body?, headers?, credentials?, mode?))
// Stream request with callback for real-time data processing
(try? @mio.get_stream(url, callback, headers?, credentials?, mode?))
Response Handling
// HttpResponse methods
response.text() // Get response as string
response.json() // Parse JSON (may raise ParseError)
response.unwrap_json() // Safe JSON parsing (returns Json::null() on error)
response.statusCode // HTTP status code
response.headers // Response headers as Map[String, String]
response.data // Raw response data as Bytes
Error Handling
The library defines three main error types:
IOError- File system and I/O related errorsNetworkError- Network and HTTP request errorsExecError- Execution and runtime errors
Advanced Usage
Custom Request with Full Control
@mio.run(fn() {
match (try? @mio.request("https://api.example.com/upload",
http_method=POST,
body="custom request body",
headers={
"Content-Type": "text/plain",
"X-Custom-Header": "value"
},
credentials=Include,
mode=CORS)) {
Ok(response) => {
// Handle response
}
Err(NetworkError) => {
// Handle network errors
}
}
})
Binary Data Handling
Mio provides unified binary data support across all backends through the Bytes interface. All HTTP responses contain raw binary data in response.data, which can be processed as text or kept as binary.
@mio.run(fn() {
// All requests return binary data in response.data
match (try? @mio.get("https://example.com/image.png")) {
Ok(response) => {
// response.data contains raw bytes (unified across all backends)
@fs.write_bytes_to_file("image.png", response.data)
// For text content, use response.text()
let text_content = response.text()
println("Content: " + text_content)
}
Err(e) => println("Failed to download: " + e.to_string())
}
})
Examples
Check out these real-world projects using mio:
- weatherquery - Weather query tool for Chinese locations
- Fetches real-time weather data from APIs
- Supports provinces, cities, and districts
- Cross-platform (native and web)
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.