npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

expo-web-server

v0.1.3

Published

expo web server

Downloads

11

Readme

Expo Web Server

A native web server module for Expo/React Native applications that allows you to create HTTP servers directly on mobile devices.

Features

  • 🚀 Native Performance: Built with native code for optimal performance
  • 📱 Cross-Platform: Supports both iOS and Android
  • 🔧 Easy Integration: Simple API for starting and managing web servers
  • 🌐 Full HTTP Support: Supports all HTTP methods (GET, POST, PUT, DELETE, OPTIONS)
  • 📄 File Serving: Serve static files from your app
  • 🔒 Error Handling: Built-in error handling and timeout protection
  • 📍 IP Detection: Automatically detects device IP address
  • 🎯 TypeScript Support: Full TypeScript definitions included

Installation

Prerequisites

  • Expo SDK 53 or higher
  • React Native 0.79.1 or higher
  • iOS 13+ / Android API 21+

Install the package

npx expo install expo-web-server

Quick Start

import { start, stop, getDeviceIP } from 'expo-web-server';

// Start the web server
start(3000, async (request) => {
  console.log('Received request:', request);
  
  return {
    requestId: request.requestId,
    statusCode: 200,
    contentType: 'application/json',
    body: JSON.stringify({
      message: 'Hello from Expo Web Server!',
      timestamp: new Date().toISOString(),
    }),
  };
});

// Get device IP address
const deviceIP = getDeviceIP();
console.log('Device IP:', deviceIP);

// Stop the server when done
stop();

API Reference

start(port: number, callback: (request: Request) => Promise<WebResponse> | WebResponse)

Starts the web server on the specified port.

Parameters:

  • port (number): The port number to start the server on
  • callback (function): Request handler function that receives a Request object and returns a WebResponse

Example:

start(3000, async (request) => {
  if (request.path === '/api/hello') {
    return {
      requestId: request.requestId,
      statusCode: 200,
      contentType: 'application/json',
      body: JSON.stringify({ message: 'Hello World!' }),
    };
  }
  
  return {
    requestId: request.requestId,
    statusCode: 404,
    contentType: 'text/plain',
    body: 'Not Found',
  };
});

stop()

Stops the web server.

Example:

stop();

getDeviceIP(): string

Returns the device's IP address as a string.

Returns: Device IP address (e.g., "192.168.1.100")

Example:

const ip = getDeviceIP();
console.log('Device IP:', ip);

Types

Request

interface Request {
  requestId: string;
  method: HttpMethod;
  path: string;
  body: string;
  headers: { [key: string]: string };
  params: { [key: string]: string };
}

WebResponse

interface WebResponse {
  requestId: string;
  statusCode?: number;        // Default: 200
  statusDescription?: string; // Default: "OK"
  contentType?: string;       // Default: "application/json"
  headers?: Record<string, string>;
  body?: string | null;
  file?: string | null;       // Path to file to serve
}

HttpMethod

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS";

Examples

Basic JSON API

import { start, stop } from 'expo-web-server';

start(3000, async (request) => {
  switch (request.path) {
    case '/api/users':
      return {
        requestId: request.requestId,
        statusCode: 200,
        contentType: 'application/json',
        body: JSON.stringify([
          { id: 1, name: 'John Doe' },
          { id: 2, name: 'Jane Smith' }
        ]),
      };
      
    case '/api/health':
      return {
        requestId: request.requestId,
        statusCode: 200,
        contentType: 'application/json',
        body: JSON.stringify({ status: 'healthy', timestamp: new Date().toISOString() }),
      };
      
    default:
      return {
        requestId: request.requestId,
        statusCode: 404,
        contentType: 'text/plain',
        body: 'Not Found',
      };
  }
});

File Serving

import { start } from 'expo-web-server';
import * as FileSystem from 'expo-file-system';

start(3000, async (request) => {
  if (request.path === '/') {
    return {
      requestId: request.requestId,
      statusCode: 200,
      contentType: 'text/html',
      body: '<h1>Welcome to Expo Web Server!</h1>',
    };
  }
  
  if (request.path.startsWith('/static/')) {
    const filePath = FileSystem.documentDirectory + request.path.substring(8);
    return {
      requestId: request.requestId,
      statusCode: 200,
      contentType: 'application/octet-stream',
      file: filePath,
    };
  }
  
  return {
    requestId: request.requestId,
    statusCode: 404,
    contentType: 'text/plain',
    body: 'File not found',
  };
});

REST API with POST Support

import { start } from 'expo-web-server';

start(3000, async (request) => {
  if (request.method === 'POST' && request.path === '/api/data') {
    try {
      const data = JSON.parse(request.body);
      
      // Process the data here
      console.log('Received data:', data);
      
      return {
        requestId: request.requestId,
        statusCode: 201,
        contentType: 'application/json',
        body: JSON.stringify({ 
          success: true, 
          message: 'Data received successfully',
          receivedData: data 
        }),
      };
    } catch (error) {
      return {
        requestId: request.requestId,
        statusCode: 400,
        contentType: 'application/json',
        body: JSON.stringify({ 
          success: false, 
          error: 'Invalid JSON data' 
        }),
      };
    }
  }
  
  return {
    requestId: request.requestId,
    statusCode: 405,
    contentType: 'text/plain',
    body: 'Method not allowed',
  };
});

React Native Component Example

import React, { useEffect, useState } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import { start, stop, getDeviceIP } from 'expo-web-server';

export default function WebServerComponent() {
  const [isRunning, setIsRunning] = useState(false);
  const [deviceIP, setDeviceIP] = useState('');

  useEffect(() => {
    setDeviceIP(getDeviceIP());
  }, []);

  const handleStartServer = () => {
    try {
      start(3000, async (request) => {
        return {
          requestId: request.requestId,
          statusCode: 200,
          contentType: 'application/json',
          headers: {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
          },
          body: JSON.stringify({
            message: 'Hello from React Native!',
            request: request,
            timestamp: new Date().toISOString(),
          }),
        };
      });
      setIsRunning(true);
      Alert.alert('Success', `Server started on port 3000\nAccess via: http://${deviceIP}:3000`);
    } catch (error) {
      Alert.alert('Error', `Failed to start server: ${error}`);
    }
  };

  const handleStopServer = () => {
    try {
      stop();
      setIsRunning(false);
      Alert.alert('Success', 'Server stopped');
    } catch (error) {
      Alert.alert('Error', `Failed to stop server: ${error}`);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Text style={{ fontSize: 18, marginBottom: 10 }}>
        Server Status: {isRunning ? 'Running' : 'Stopped'}
      </Text>
      <Text style={{ marginBottom: 20 }}>
        Device IP: {deviceIP}
      </Text>
      <Button
        title={isRunning ? 'Stop Server' : 'Start Server'}
        onPress={isRunning ? handleStopServer : handleStartServer}
      />
    </View>
  );
}

Development

Building from source

git clone https://github.com/ittat/expo-web-server.git
cd expo-web-server
npm install
npm run build

Running the example

cd example
npm install
npx expo start

Troubleshooting

Common Issues

  1. Port already in use: Make sure the port you're trying to use isn't already occupied by another application.

  2. Permission denied: On Android, ensure your app has the INTERNET permission in AndroidManifest.xml.

  3. Server not accessible: Make sure your device and the client are on the same network.

Android Emulator Issues

When running on Android emulator, you need to set up port forwarding to access the web server from your host machine:

Manual Setup

# Set up port forwarding for port 3000
adb forward tcp:3000 tcp:3000

# Check current port forwards
adb forward --list

Automatic Setup

Use the provided script:

# Set up port forwarding for default port (3000)
./setup-port-forward.sh

# Or specify a custom port
./setup-port-forward.sh 8080

After setting up port forwarding, you can access the web server at:

  • http://localhost:3000 (from host machine)
  • http://10.0.2.2:3000 (from emulator)

Why Port Forwarding is Needed

Android emulator runs in a virtual environment with its own network stack. The emulator's internal IP (10.0.2.15) is not directly accessible from the host machine. Port forwarding creates a tunnel between the host and emulator.

Debug Mode

Enable debug logging by checking the console output in your Expo development tools.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

If you encounter any issues or have questions, please:

  1. Check the Issues page
  2. Create a new issue with detailed information about your problem
  3. Include your Expo SDK version, React Native version, and device information

Changelog

v0.1.0

  • Initial release
  • Basic HTTP server functionality
  • Support for all HTTP methods
  • File serving capability
  • TypeScript definitions
  • iOS and Android support