react-native-nitro-http-server
v1.5.0
Published
React Native HTTP Server with Nitro Modules
Downloads
1,166
Maintainers
Readme
React Native HTTP Server
A high-performance React Native HTTP server library, implemented in Rust, supporting dynamic request handling and static file serving.
✨ Features
- 🚀 High Performance: Built on Rust's Actix-web framework, delivering exceptional performance.
- 📱 Cross-Platform: Supports iOS and Android.
- 🔄 Asynchronous: Uses Nitro Modules to provide native async APIs.
- 📁 Static File Serving: Built-in static file server support.
- 📂 Directory Listing: Automatically generate directory listing pages.
- 🎯 Easy to Use: TypeScript-friendly API design.
- ⚡ Zero Copy: Direct FFI calls to Rust code.
- 🔌 Plugin System: Support for WebDAV, Zip mounting, and extensible plugins.
- 🌊 Streaming APIs: Support for streaming request/response bodies.
- 📤 File Upload Plugin: Support for handling
multipart/form-datafile uploads efficiently (save to disk). - 💾 Buffer Upload Plugin: Handle file uploads in memory with direct
ArrayBufferaccess. - 🔀 URL Rewrite Plugin: Support pattern-based URL rewriting using regular expressions.
- 🔌 WebSocket Plugin: Real-time bidirectional communication with full handshake info access.
- 🔄 Node.js Compatible: Compatible with Node.js
httpmodule API.
📦 Installation
npm install react-native-nitro-http-server
# or
yarn add react-native-nitro-http-serveriOS Configuration
Run pod install:
cd ios && pod installAndroid Configuration
No extra configuration needed, autolinking is supported.
🚀 Quick Start
Basic HTTP Server
import { HttpServer } from 'react-native-nitro-http-server';
const server = new HttpServer();
// Start server
await server.start(8080, async (request) => {
console.log(`Received request: ${request.method} ${request.path}`);
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'Hello from React Native!',
path: request.path,
}),
};
});
console.log('Server running at http://localhost:8080');
// Stop server
// await server.stop();Binary Response Example
import { HttpServer } from 'react-native-nitro-http-server';
const server = new HttpServer();
await server.start(8080, async (request) => {
// Return a binary image
const imageBuffer = new ArrayBuffer(1024); // Your binary data
return {
statusCode: 200,
headers: {
'Content-Type': 'image/png',
},
body: imageBuffer, // direct ArrayBuffer support
};
});Static File Server
import { StaticServer } from 'react-native-nitro-http-server';
import RNFS from 'react-native-fs';
const server = new StaticServer();
// Start static file server
const staticDir = RNFS.DocumentDirectoryPath + '/www';
await server.start(8080, staticDir);
console.log(`Static file server running at http://localhost:8080`);
console.log(`Serving directory: ${staticDir}`);
// Stop static server
// await server.stop();App Server (Hybrid Mode)
Supports both static file serving and dynamic API handling. It prioritizes serving static files; if the file does not exist, it invokes the callback function.
import { AppServer } from 'react-native-nitro-http-server';
import RNFS from 'react-native-fs';
const server = new AppServer();
const staticDir = RNFS.DocumentDirectoryPath + '/www';
// Start app server (hybrid mode)
await server.start(8080, staticDir, async (request) => {
// This callback is executed when the static file is not found
return {
statusCode: 200,
body: `Dynamic response for ${request.path}`,
};
});Config Server (With Plugins)
Supports advanced features like WebDAV and Zip file mounting through plugin configuration.
import { createConfigServer } from 'react-native-nitro-http-server';
import RNFS from 'react-native-fs';
const staticDir = RNFS.DocumentDirectoryPath + '/www';
// Configure plugins
const config = {
root_dir: staticDir, // Static file root (Optional)
verbose: 'info', // Log level: 'off' | 'error' | 'warn' | 'info' | 'debug' (default: 'off')
mounts: [
{
type: 'webdav',
path: '/webdav',
root: RNFS.DocumentDirectoryPath + '/webdav'
},
{
type: 'zip',
path: '/archive',
zip_file: RNFS.DocumentDirectoryPath + '/content.zip'
},
{
type: 'static',
path: '/static',
root: staticDir,
dir_list: {
enabled: true, // Enable directory listing
show_hidden: false
}
},
{
type: 'upload',
path: '/upload',
temp_dir: RNFS.CachesDirectoryPath + '/uploads'
},
{
type: 'buffer_upload',
path: '/buffer-upload'
},
{
type: 'rewrite',
rules: [
{ pattern: '^/old/(.*)', replacement: '/static/$1' },
{ pattern: '^/api/v1/(.*)', replacement: '/api/v2/$1' }
]
},
{
type: 'websocket',
path: '/ws'
}
],
mime_types: {
"myext": "application/x-custom-type" // Custom MIME type
}
};
// Start server with plugin configuration
const server = await createConfigServer(8080, async (request) => {
// Handle dynamic requests
return {
statusCode: 200,
body: `API response for ${request.path}`,
};
}, config);
// Now you can:
// - Access WebDAV at http://localhost:8080/webdav
// - Access zip content at http://localhost:8080/archive
// - Browse directories if index file is missing
// - Static files from staticDir
// - Dynamic API responses
// - WebSocket at ws://localhost:8080/wsWebSocket Server
Provides real-time bidirectional communication with full access to handshake information.
import { ConfigServer } from 'react-native-nitro-http-server';
const server = new ConfigServer();
// Register WebSocket handler (before starting the server)
server.onWebSocket('/ws', (ws, request) => {
// Access handshake info
console.log('Path:', request.path);
console.log('Query:', request.query); // e.g., "token=abc&user=123"
console.log('Headers:', request.headers); // Full HTTP handshake headers
// Handle events
ws.onmessage = (e) => {
console.log('Received:', e.data);
ws.send('Echo: ' + e.data);
};
ws.onclose = (e) => {
console.log('Closed:', e.code, e.reason);
};
});
// Start server with WebSocket mount
await server.start(8080, httpHandler, {
mounts: [{ type: 'websocket', path: '/ws' }]
});RESTful API Example
import { HttpServer } from 'react-native-nitro-http-server';
const server = new HttpServer();
// Mock database
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
await server.start(8080, async (request) => {
const { method, path } = request;
// GET /api/users - Get all users
if (method === 'GET' && path === '/api/users') {
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(users),
};
}
// GET /api/users/:id - Get a single user
const userMatch = path.match(/^\/api\/users\/(\d+)$/);
if (method === 'GET' && userMatch) {
const userId = parseInt(userMatch[1]);
const user = users.find(u => u.id === userId);
if (user) {
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user),
};
} else {
return {
statusCode: 404,
body: JSON.stringify({ error: 'User not found' }),
};
}
}
// POST /api/users - Create a new user
if (method === 'POST' && path === '/api/users') {
const newUser = JSON.parse(request.body || '{}');
newUser.id = users.length + 1;
users.push(newUser);
return {
statusCode: 201,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser),
};
}
// 404 - Route not found
return {
statusCode: 404,
body: JSON.stringify({ error: 'Route not found' }),
};
});Node.js Compatible API
Provides an interface compatible with Node.js http module, facilitating migration of existing code or using adapters for frameworks like Express/Koa.
import { createServer } from 'react-native-nitro-http-server';
const server = createServer((req, res) => {
console.log(req.method, req.url);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello from Node.js compatible API!');
});
server.listen(8080, () => {
console.log('Server listening on port 8080');
});📖 API Documentation
HttpServer
The basic HTTP server class for handling dynamic requests.
start(port: number, handler: RequestHandler): Promise<boolean>
Starts the HTTP server.
Parameters:
port: Port number (1024-65535)handler: Request handler function, receivesHttpRequestand returnsHttpResponse
Returns: true if started successfully.
Example:
const success = await server.start(8080, async (request) => {
return {
statusCode: 200,
body: 'Hello World',
};
});stop(): Promise<void>
Stops the HTTP server.
Example:
await server.stop();StaticServer
The static file server class.
start(port: number, rootDir: string, host?: string): Promise<boolean>
Starts the static file server.
Parameters:
port: Port numberrootDir: Absolute path to the static file root directoryhost: (Optional) IP address to bind to, defaults to127.0.0.1
Returns: true if started successfully.
Example:
import { StaticServer } from 'react-native-nitro-http-server';
import RNFS from 'react-native-fs';
const server = new StaticServer();
const success = await server.start(
8080,
RNFS.DocumentDirectoryPath + '/www'
);stop(): Promise<void>
Stops the static file server.
isRunning(): boolean
Checks if the static server is running.
AppServer
The app server class (hybrid mode) for both static files and dynamic requests.
start(port: number, rootDir: string, handler: RequestHandler, host?: string): Promise<boolean>
Starts the app server (hybrid mode). The server will first attempt to find the corresponding static file in rootDir. If found and the method is GET, it returns the file content directly. Otherwise, it forwards the request to the handler.
Parameters:
port: Port numberrootDir: Static file root directoryhandler: Request handlerhost: (Optional) IP address to bind to, defaults to127.0.0.1
stop(): Promise<void>
Stops the app server.
isRunning(): boolean
Checks if the app server is running.
Type Definitions
HttpRequest
interface HttpRequest {
requestId: string; // Unique request ID
method: string; // HTTP Method (GET, POST, PUT, DELETE, etc.)
path: string; // Request path
headers: Record<string, string>; // Request headers
body?: string; // Request body (optional)
binaryBody?: ArrayBuffer; // Binary request body (used by buffer_upload)
}HttpResponse
interface HttpResponse {
statusCode: number; // HTTP Status Code (200, 404, 500, etc.)
headers?: Record<string, string>; // Response headers (optional)
body?: string | ArrayBuffer; // Response body (string or ArrayBuffer)
}stopAppServer(): Promise<void>
Stops the app server.
ConfigServer
Server with plugin configuration support (WebDAV, Zip mounting, etc.).
start(port: number, handler: RequestHandler, config: ServerConfig, host?: string): Promise<boolean>
Starts the config server with plugin configuration.
Parameters:
port: Port numberhandler: Request handlerconfig: Plugin configuration object (includesroot_dir)host: (Optional) IP address to bind to, defaults to127.0.0.1
Example:
const config = {
root_dir: staticDir,
mounts: [
{
type: 'webdav',
path: '/webdav',
root: RNFS.DocumentDirectoryPath + '/webdav'
},
{
type: 'zip',
path: '/archive',
zip_file: RNFS.DocumentDirectoryPath + '/content.zip'
}
]
};
const server = new ConfigServer();
await server.start(8080, handler, config, '0.0.0.0');stop(): Promise<void>
Stops the config server.
isRunning(): boolean
Checks if the config server is running.
Helper Functions
createHttpServer(port: number, handler: RequestHandler, host?: string): Promise<HttpServer>
Creates and starts a basic HTTP server.
createStaticServer(port: number, rootDir: string, host?: string): Promise<StaticServer>
Creates and starts a static file server.
createAppServer(port: number, rootDir: string, handler: RequestHandler, host?: string): Promise<AppServer>
Creates and starts an app server (hybrid mode).
createConfigServer(port: number, handler: RequestHandler, config: ServerConfig, host?: string): Promise<ConfigServer>
Creates and starts a config server with plugin configuration.
Type Definitions
HttpRequest
interface HttpRequest {
requestId: string; // Unique request ID
method: string; // HTTP Method (GET, POST, PUT, DELETE, etc.)
path: string; // Request path
headers: Record<string, string>; // Request headers
body?: string; // Request body (optional)
binaryBody?: ArrayBuffer; // Binary request body (used by buffer_upload)
}HttpResponse
interface HttpResponse {
statusCode: number; // HTTP Status Code (200, 404, 500, etc.)
headers?: Record<string, string>; // Response headers (optional)
body?: string | ArrayBuffer; // Response body (string or ArrayBuffer)
}ServerConfig
interface ServerConfig {
root_dir?: string; // Static file root (Optional, as default static mount)
verbose?: boolean | 'off' | 'error' | 'warn' | 'info' | 'debug'; // Log level (default: 'off')
mime_types?: MimeTypesConfig;
mounts?: Mountable[]; // Unified mount list
}
type Mountable = WebDavMount | ZipMount | StaticMount | UploadMount | BufferUploadMount | RewriteMount | WebSocketMount;
interface WebDavMount {
type: 'webdav';
path: string; // Mount path, e.g., "/webdav"
root: string; // WebDAV root directory
}
interface ZipMount {
type: 'zip';
path: string; // Mount path, e.g., "/zip"
zip_file: string; // Zip file path
}
interface UploadMount {
type: 'upload';
path: string; // Mount path, e.g., "/upload"
temp_dir: string; // Temporary directory for uploaded files
}
interface BufferUploadMount {
type: 'buffer_upload';
path: string; // Mount path, e.g., "/buffer-upload"
}
interface RewriteMount {
type: 'rewrite';
rules: RewriteRule[];
}
interface WebSocketMount {
type: 'websocket';
path: string; // WebSocket endpoint, e.g., "/ws"
max_message_size?: number; // Max message size in bytes (default: 64MB)
}
// WebSocket Connection Request
interface WebSocketConnectionRequest {
path: string; // Connection path
query: string; // Query string
headers: Record<string, string>; // HTTP handshake headers
}
// WebSocket Connection Handler
type WebSocketConnectionHandler = (
ws: ServerWebSocket,
request: WebSocketConnectionRequest
) => void;
interface RewriteRule {
pattern: string; // Regex pattern
replacement: string; // Replacement string (supports $1, $2...)
}
interface StaticMount {
type: 'static';
path: string; // Mount path, e.g., "/images"
root: string; // Local file system directory
dir_list?: DirListConfig;
default_index?: string[];
}
type MimeTypesConfig = Record<string, string>;
interface DirListConfig {
enabled: boolean; // Enable directory listing
show_hidden?: boolean; // Show hidden files (default: false)
}RequestHandler
type RequestHandler = (request: HttpRequest) => Promise<HttpResponse> | HttpResponse;The request handler can return a Promise or a response object directly.
Node.js Compatible Layer
Exports the following objects and functions compatible with Node.js http module:
createServer(requestListener?: (req: IncomingMessage, res: ServerResponse) => void): ServerServerclassIncomingMessageclassServerResponseclassSTATUS_CODESMETHODS
Streaming APIs
The library also provides low-level streaming APIs for advanced use cases:
readRequestBodyChunk(requestId: string): Promise<string>- Read request body in chunkswriteResponseChunk(requestId: string, chunk: string): Promise<boolean>- Write response body in chunksendResponse(requestId: string, statusCode: number, headersJson: string): Promise<boolean>- End streaming responsesendBinaryResponse(requestId: string, statusCode: number, headersJson: string, body: ArrayBuffer): Promise<boolean>- Send binary response
These APIs are used internally by the Node.js compatible layer for streaming support.
🏗️ Architecture
┌─────────────────────────────────────┐
│ JavaScript / TypeScript │
│ (React Native App) │
└──────────────┬──────────────────────┘
│ Nitro Modules
┌──────────────┴──────────────────────┐
│ C++ Bridge Layer │
│ (HybridHttpServer) │
└──────────────┬──────────────────────┘
│ FFI (C ABI)
┌──────────────┴──────────────────────┐
│ Rust Core │
│ (Actix-web + Tokio) │
└─────────────────────────────────────┘Tech Stack
- JavaScript Layer: TypeScript, React Native
- Bridge Layer: Nitro Modules (C++)
- Core Layer: Rust (Actix-web, Tokio)
Data Flow
- Request Arrival: Rust Actix-web server receives HTTP request.
- C Callback: Calls C callback function via FFI.
- C++ Conversion: C++ converts C struct to Nitro types.
- JavaScript Call: Calls JavaScript handler via Nitro Modules.
- Response Return: JavaScript returns response → C++ → C → Rust → HTTP Client.
🔧 FAQ
Q: Why does the server fail to start?
A: Possible reasons:
- Port In Use: Try changing the port number.
- Insufficient Permissions: Some ports (like 80, 443) require root privileges.
- Firewall: Check firewall settings.
Q: How to handle large file uploads?
A: The body field in the current version is a string type, which is not suitable for large files. Suggestions:
- Use the
UploadPlugin(Recommended): Configure anuploadmount. It intercepts multipart uploads, saves files to a temporary directory, and injects file paths into request headers (x-uploaded-file-path), keeping the JS payload light. - Use the
BufferUploadPlugin: Process files in memory (limit 100MB). Access data viarequest.binaryBody. - Use the static file server (for downloads).
- Add streaming support in the Rust layer (advanced).
Q: Is HTTPS supported?
A: The current version does not directly support HTTPS. It is recommended to use a reverse proxy (like Nginx) to provide HTTPS support.
Q: How is the performance?
A: Built on Rust's Actix-web framework, performance is excellent. Here are the benchmark results (Test Environment: MacMini M4, 1 Thread, 2 Connections):
| Mode | QPS (Req/Sec) | Latency (Avg) | | :--- | :--- | :--- | | Basic HTTP | ~41.85k | ~58.14us | | Node.js Compatible API | ~21.60k | ~274.81us | | Koa Framework | ~13.32k | ~313.10us | | Binary Mode | ~35.46k | ~124.29us |
Note: The Node.js compatible layer has lower performance due to additional JavaScript bridging and object conversion, but it is still sufficient for most application scenarios.
Q: Can I run dynamic and static servers simultaneously?
A: Yes. You can either start the dynamic server and static server separately (using different ports) or use startAppServer to provide both static file and dynamic API services on the same port.
// Method 1: Use startAppServer (Recommended)
await server.startAppServer(8080, staticDir, apiHandler);
// Method 2: Start separately (Different ports)
await server.start(8080, handler);
// Static server on 8081
await server.startStaticServer(8081, staticDir);Q: How to debug server issues?
A:
- Check server logs (Xcode/Logcat).
- Use
getStats()to view statistics. - Use tools to test (curl, Postman).
# Test server
curl http://localhost:8080/api/test📝 Changelog
1.0.0 (2025-12-08)
- 🎉 Initial release.
- ✅ Full implementation based on Nitro Modules.
- ✅ Dynamic request handling support.
- ✅ Static file serving support.
- ✅ iOS and Android support.
📄 License
ISC
🤝 Contribution
Issues and Pull Requests are welcome!
🔗 Related Links
Made with ❤️ using Rust, C++, and React Native
