Tooling
Push
Mobile notifications through the Expo Push Service: batching, receipts and dead tokens, with no dependency.
@krizaka/push sends mobile push without an SDK: the Expo Push Service relays to Firebase Cloud Messaging on Android
and to the Apple Push Notification service on iOS, from one HTTP call and one kind of token (ExponentPushToken[…]).
The FCM and APNs credentials live in the app's EAS project — never on your server. Zero dependency: you pass fetch.
import { isExpoPushToken, sendExpoPush } from "@krizaka/push";
if (!isExpoPushToken(token)) return badRequest(); // when the phone registers
const outcome = await sendExpoPush(
devices.map((d) => ({ to: d.token, title, body, data: { path: "/inbox/42" } })),
{ fetch },
);
await forgetTokens(outcome.deadTokens); // uninstalled app, revoked permissionThe rules it carries
- Best-effort. A push repeats what the app already shows; it never decides anything. Write the fact first, push second.
- Dead tokens are forgotten.
DeviceNotRegisteredtickets come back indeadTokens: delete them. - Data, not secrets.
datareaches the device as is — a path to open, an id; never content you would not show on a lock screen. - Send from production only. Local runs and CI never call the service.
On the phone, the permission is asked in words first by Permission prompt.