{"id":10860,"library":"expo-http-server","title":"Expo HTTP Server","description":"expo-http-server is an Expo module that provides a simple HTTP server implementation exclusively for iOS and Android React Native applications. It is currently at version 0.1.13 and sees active development with recent updates for Expo 53 compatibility and feature enhancements like custom headers. The module leverages native libraries, Criollo for iOS and AndroidServer for Android, offering local network communication capabilities directly from the mobile device. A key differentiator is its focus on embedding a server within a mobile app, enabling scenarios like local API mocking, inter-app communication, or serving local assets without a remote backend. It notably does not support web environments, which is a crucial limitation to understand.","status":"active","version":"0.1.13","language":"javascript","source_language":"en","source_url":"https://github.com/simonsturge/expo-http-server","tags":["javascript","react-native","expo","expo-http-server","ExpoHttpServer","typescript"],"install":[{"cmd":"npm install expo-http-server","lang":"bash","label":"npm"},{"cmd":"yarn add expo-http-server","lang":"bash","label":"yarn"},{"cmd":"pnpm add expo-http-server","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency required for Expo module functionality.","package":"expo","optional":false},{"reason":"Peer dependency for React Native environment.","package":"react","optional":false},{"reason":"Peer dependency for React Native environment.","package":"react-native","optional":false}],"imports":[{"note":"The module is primarily designed for modern JavaScript/TypeScript environments in Expo, favoring ESM imports.","wrong":"const server = require('expo-http-server');","symbol":"* as server","correct":"import * as server from 'expo-http-server';"},{"note":"Type import for handling server status events, essential for TypeScript projects.","symbol":"StatusEvent","correct":"import type { StatusEvent } from 'expo-http-server';"},{"note":"Type import for the request object passed to route handlers, useful for type safety.","symbol":"Request","correct":"import type { Request } from 'expo-http-server';"}],"quickstart":{"code":"import * as server from 'expo-http-server';\nimport { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\n\nexport default function App() {\n  const [lastCalled, setLastCalled] = useState<number | undefined>();\n\n  const html = `\n\t<!DOCTYPE html>\n\t<html>\n\t\t<body style=\"background-color:powderblue;\">\n\t\t\t<h1>expo-http-server</h1>\n\t\t\t<p>You can load HTML!</p>\n\t\t</body>\n\t</html>`;\n\n  const obj = { app: 'expo-http-server', desc: 'You can load JSON!' };\n\n  useEffect(() => {\n    server.setup(9666, (event: server.StatusEvent) => {\n      if (event.status === 'ERROR') {\n        console.error('Server error:', event.message);\n      } else {\n        console.log('Server status:', event.status);\n      }\n    });\n    server.route('/', 'GET', async (request) => {\n      console.log('Request to / (GET)', request);\n      setLastCalled(Date.now());\n      return {\n        statusCode: 200,\n        headers: {\n          'Custom-Header': 'Bazinga',\n        },\n        contentType: 'application/json',\n        body: JSON.stringify(obj),\n      };\n    });\n    server.route('/html', 'GET', async (request) => {\n      console.log('Request to /html (GET)', request);\n      setLastCalled(Date.now());\n      return {\n        statusCode: 200,\n        statusDescription: 'OK - CUSTOM STATUS',\n        contentType: 'text/html',\n        body: html,\n      };\n    });\n    server.start();\n    return () => {\n      server.stop();\n    };\n  }, []);\n\n  return (\n    <View\n      style={{\n        flex: 1,\n        backgroundColor: '#fff',\n        alignItems: 'center',\n        justifyContent: 'center',\n      }}\n    >\n      <Text>\n        {lastCalled === undefined\n          ? 'Request webserver to change text'\n          : 'Called at ' + new Date(lastCalled).toLocaleString()}\n      </Text>\n    </View>\n  );\n}","lang":"typescript","description":"This example demonstrates how to set up an HTTP server on port 9666, define two routes ('/' and '/html'), and handle incoming GET requests to serve JSON and HTML content, respectively, within an Expo React Native app."},"warnings":[{"fix":"Review existing route definitions and ensure they are compatible with the updated callback matching logic. If you were relying on implicit behavior of route matching, re-test thoroughly.","message":"The internal mechanism for matching callbacks changed from relying solely on path and method to using a UUID. This might affect advanced use cases or debugging if prior versions were implicitly relying on specific routing implementation details.","severity":"breaking","affected_versions":">=0.1.10"},{"fix":"Ensure your request handlers for iOS properly parse JSON strings for the request body. If you were expecting a different format, adjust your parsing logic accordingly.","message":"For iOS, the body of incoming requests is now consistently sent as a JSON string. This changes the expected format for request body parsing on the server side.","severity":"breaking","affected_versions":">=0.1.3"},{"fix":"Do not attempt to use this module in a web browser environment. Implement conditional logic if your project targets multiple platforms, including web.","message":"The server does not support web environments. It is strictly for iOS and Android React Native applications.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Design your application with the expectation that the server may pause on iOS when in the background. Inform users of this limitation. For persistent background operation on Android, consider implementing a foreground service, possibly with a library like Notifee.","message":"On iOS, when the app is backgrounded, the HTTP server will inevitably pause after approximately 25 seconds due to operating system limitations, even with background tasks. The server will resume when the app returns to the foreground.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Implement robust security practices. Limit access to trusted networks/devices if possible. Validate all incoming requests. Avoid exposing administrative endpoints or sensitive data. Consider using HTTPS if sensitive data is involved, though this module might not directly support it out-of-the-box.","message":"Running an HTTP server directly on a mobile device introduces potential security considerations. Ensure proper access control, input validation, and avoid exposing sensitive data or functionality without strong authentication.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure you are using `import * as server from 'expo-http-server';` and verify that the application is running on an iOS or Android device/emulator, not in a web browser.","cause":"The module was either not imported correctly (e.g., using CommonJS require in a modern Expo setup) or the application is running in an unsupported environment like a web browser.","error":"TypeError: server.start is not a function"},{"fix":"This is expected behavior on iOS. The server automatically pauses and resumes. For critical background operations, consider alternative inter-process communication or design patterns that do not rely on a continuously running HTTP server in the background on iOS.","cause":"iOS operating system limitations impose strict time limits on background tasks, causing the server to pause.","error":"Server not starting on iOS when app is backgrounded / Server stops responding after a short time in background (iOS)"},{"fix":"This module is exclusively for iOS and Android. If your project targets web, use conditional imports or a web-specific HTTP server solution.","cause":"The `expo-http-server` module does not have a web implementation and its native modules are undefined when running in a browser.","error":"TypeError: Cannot read property 'setup' of undefined in browser environment"},{"fix":"Double-check the path, method, and callback definitions for `server.route()`. Ensure there are no overlapping routes. Refer to the v0.1.10 breaking change regarding UUID-based matching.","cause":"Possible issues with route definition, conflicting routes, or changes in how routes are matched internally (e.g., v0.1.10 change).","error":"My route handler isn't being called / Route path doesn't seem to match"}],"ecosystem":"npm"}