{"id":10859,"library":"expo-server-sdk","title":"Expo Server SDK for Node.js","description":"The `expo-server-sdk` is a server-side library for Node.js environments, designed to facilitate the sending of push notifications to mobile applications built with Expo. It provides utilities for constructing push notification messages, sending them efficiently to Expo's push notification service, and handling the resulting receipts and errors. The current stable version is 6.1.0. This SDK maintains an active release cadence, with recent major versions (v4, v5, v6) introducing significant breaking changes around module systems and Node.js version support. Key differentiators include its tight integration with the Expo ecosystem, simplifying the complex process of managing push tokens and batching messages, and providing a streamlined API for interacting with the Expo push service. It abstracts away much of the underlying HTTP request handling and error parsing.","status":"active","version":"6.1.0","language":"javascript","source_language":"en","source_url":"https://github.com/expo/expo-server-sdk-node","tags":["javascript","expo","push-notifications","typescript"],"install":[{"cmd":"npm install expo-server-sdk","lang":"bash","label":"npm"},{"cmd":"yarn add expo-server-sdk","lang":"bash","label":"yarn"},{"cmd":"pnpm add expo-server-sdk","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Replaced 'node-fetch' as the underlying HTTP client for making requests to the Expo push notification service, improving performance and adhering to web standards. This change occurred in v5.0.0.","package":"undici","optional":false}],"imports":[{"note":"Since v6.0.0, the package is built as an ECMAScript module (ESM) only. CommonJS `require()` is no longer supported for direct import of the main module.","wrong":"const Expo = require('expo-server-sdk');","symbol":"Expo","correct":"import { Expo } from 'expo-server-sdk';"},{"note":"While `type` imports are valid in TypeScript, `ExpoPushMessage` is an interface/type definition, but in JavaScript contexts or for clarity, the standard named import is usually sufficient as it doesn't represent a runtime value.","wrong":"import { type ExpoPushMessage } from 'expo-server-sdk';","symbol":"ExpoPushMessage","correct":"import { ExpoPushMessage } from 'expo-server-sdk';"},{"note":"`isExpoPushToken` is a named export, not a default export. Ensure you destructure it correctly.","wrong":"import isExpoPushToken from 'expo-server-sdk';","symbol":"isExpoPushToken","correct":"import { isExpoPushToken } from 'expo-server-sdk';"}],"quickstart":{"code":"import { Expo, ExpoPushMessage, ExpoPushTicket, ExpoPushReceiptId } from 'expo-server-sdk';\n\nconst expo = new Expo();\n\nconst sendPushNotification = async (token: string, message: string, data?: object) => {\n  if (!Expo.isExpoPushToken(token)) {\n    console.error(`Push token ${token} is not a valid Expo push token.`);\n    return;\n  }\n\n  const messages: ExpoPushMessage[] = [{\n    to: token,\n    sound: 'default',\n    body: message,\n    data: data || { withSome: 'data' },\n    channelId: 'default',\n  }];\n\n  const chunks = expo.chunkPushNotifications(messages);\n  const tickets: ExpoPushTicket[] = [];\n\n  for (const chunk of chunks) {\n    try {\n      const ticketChunk = await expo.sendPushNotificationsAsync(chunk);\n      tickets.push(...ticketChunk);\n      console.log('Push tickets received:', ticketChunk);\n    } catch (error) {\n      console.error('Error sending push chunk:', error);\n    }\n  }\n\n  const receiptIds: ExpoPushReceiptId[] = [];\n  for (const ticket of tickets) {\n    if (ticket.id) {\n      receiptIds.push(ticket.id);\n    }\n  }\n\n  if (receiptIds.length > 0) {\n    const receiptIdChunks = expo.chunkPushReceiptIds(receiptIds);\n    for (const chunk of receiptIdChunks) {\n      try {\n        const receipts = await expo.getPushNotificationReceiptsAsync(chunk);\n        console.log('Push receipts received:', receipts);\n\n        for (const receiptId in receipts) {\n          const { status, message, details } = receipts[receiptId];\n          if (status === 'error') {\n            console.error(`There was an error sending a notification: ${message}`);\n            if (details && details.error) {\n              console.error(`The error code is ${details.error}`);\n            }\n          }\n        }\n      } catch (error) {\n        console.error('Error getting push receipts:', error);\n      }\n    }\n  }\n};\n\n// Example usage (replace with actual token and message)\nconst exampleToken = process.env.EXPO_PUSH_TOKEN ?? 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]';\nif (exampleToken) {\n  sendPushNotification(exampleToken, 'Hello from Expo!', { 'foo': 'bar' });\n}","lang":"typescript","description":"This quickstart demonstrates initializing the Expo client, validating an Expo push token, constructing a push notification message, chunking and sending messages, and finally retrieving and processing push notification receipts to check for delivery status and errors."},"warnings":[{"fix":"Update all `require('expo-server-sdk')` statements to `import { SymbolName } from 'expo-server-sdk';`. Ensure your project's `package.json` specifies `\"type\": \"module\"` or uses `.mjs` file extensions for ESM compatibility.","message":"Starting with v6.0.0, 'expo-server-sdk' is built as an ECMAScript module (ESM) only. This means that CommonJS `require()` statements will no longer work for importing the main package. Projects must migrate to ESM `import` syntax.","severity":"breaking","affected_versions":">=6.0.0"},{"fix":"Remove any `useFcmV1` property from your `Expo` client initialization options, as it will cause a TypeScript error or be ignored at runtime.","message":"Version 5.0.0 removed the `useFcmV1` option. This option, which previously allowed switching between Firebase Cloud Messaging (FCM) API versions, is no longer supported or necessary, as the library now exclusively uses the recommended API version.","severity":"breaking","affected_versions":">=5.0.0"},{"fix":"No direct code change is typically required unless you were directly interacting with or patching `node-fetch` within the SDK's context. Ensure your Node.js environment is compatible with `undici` (Node.js >=16 for `undici` itself, but SDK requires >=20).","message":"Version 5.0.0 replaced `node-fetch` with `undici` as its internal HTTP client. While this is primarily an internal change, it might affect applications that relied on `node-fetch` specific behaviors, configurations, or extensions. It also potentially changes how network errors are handled internally.","severity":"breaking","affected_versions":">=5.0.0"},{"fix":"Upgrade your Node.js environment to version 20 or newer. Check your `engines` field in `package.json` and local development environment.","message":"Support for Node.js v18.x was dropped in v4.0.0. The package now requires Node.js v20.x or higher to function correctly.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"For versions before 5.0.0, consider removing the option as it would default to the recommended behavior. For v5.0.0 and above, its removal is mandatory.","message":"The `useFcmV1` option was deprecated in v3.11.0 and defaulted to `true`. It was subsequently removed entirely in v5.0.0. While deprecated, it still functioned in v3.11.0 - v4.x, but its removal in v5.0.0 is a breaking change.","severity":"deprecated","affected_versions":">=3.11.0 <5.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Change `const Expo = require('expo-server-sdk');` to `import { Expo } from 'expo-server-sdk';` and ensure your `package.json` has `\"type\": \"module\"` or use `.mjs` file extensions for files importing ESM.","cause":"Attempting to import `expo-server-sdk` using CommonJS `require()` syntax in a project that is configured as an ES Module (or with `expo-server-sdk` v6+).","error":"TypeError: require is not a function"},{"fix":"Always validate tokens using `Expo.isExpoPushToken(token)` before attempting to send notifications. Ensure you are receiving valid tokens from Expo's client-side SDK.","cause":"The string provided as a push token does not conform to the expected format for Expo push tokens (e.g., 'ExponentPushToken[xxxxxxxxxxxxxxxxx]'). This often happens with malformed tokens or tokens from other push notification services.","error":"RangeError: Push token ... is not a valid Expo push token."},{"fix":"Upgrade your Node.js installation to version 20 or newer. Use a Node.js version manager like `nvm` to easily switch versions (`nvm install 20 && nvm use 20`).","cause":"Running the `expo-server-sdk` (v4.0.0 or higher) with an incompatible Node.js version, such as Node.js 18 or older.","error":"Error: Minimum Node.js version 20.x is required."},{"fix":"Implement robust error handling for push notification receipts. Log errors and potentially remove invalid/expired tokens from your database. For rate limits, consider implementing exponential backoff or reviewing your sending strategy.","cause":"Push notification service returns an error receipt, indicating issues like an invalid token, rate limiting, or a token not being subscribed to the specified channel.","error":"{ 'status': 'error', 'message': 'The push token is not subscribed to this channel.', 'details': { 'error': 'MessageRateLimitExceeded' } } (example receipt error)"}],"ecosystem":"npm"}