expofier

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2025 License: MIT Imports: 9 Imported by: 0

README

expofier

[ 📄 docs ] [ 🐙 github ]

Go package for sending push notifications to Expo (React Native) apps.

It provides both a low-level API client as well as a backgorund service taking care of delivery guarantees.

Installation

go get github.com/orsinium-labs/expofier

Usage

See the official Expo docs for instructions on configuring the client app and obtaining the push token.

Low-level usage:

client := expofier.NewClient()

// Send message:
msg := expofier.Message{
    To:   []expofier.Token{"ExpoPushToken[a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11]"},
    Body: "Hello world!",
}
ctx := context.Background()
ticket, err := client.SendMessage(ctx, msg)
if err != nil {
    return fmt.Errorf("send message: %w", err)
}

// Check message delivery status:
receipt := client.FetchReceipt(ctx, ticket)
if receipt != nil {
    if receipt.Error != nil {
        return fmt.Errorf("deliver message: %w", err)
    }
    fmt.Println("message delivered")
} else {
    fmt.Println("message not delivered yet")
}

For high-level usage, the package provides Service which takes care of grouping messages (to minimize network requests), chunking messages, retries, and checking the delivery status.

service := expofier.NewService()
ctx := context.Background()
go service.Run(ctx)

msg := expofier.Message{
    To:   []expofier.Token{"ExpoPushToken[a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11]"},
    Body: "Hello world!",
}
promise := service.Send(ctx, msg)
promise.Wait(ctx)
err := promise.Err()
if err != nil {
    return err
}

It is recommended to check for ErrDeviceNotRegistered and remove invalid tokens from your database:

if err == expofier.ErrDeviceNotRegistered {
    removeTokenFromDB(token)
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDeviceNotRegistered = errors.New("device cannot receive push notifications anymore")
	ErrMessageTooBig       = errors.New("total notification payload was too large")
	ErrMessageRateExceeded = errors.New("you are sending messages too frequently to the given device")
	ErrMismatchSenderID    = errors.New("there is an issue with your FCM push credentials")
	ErrInvalidCredentials  = errors.New("push notification credentials for your standalone app are invalid")
	ErrNoRecipients        = errors.New("message has no recipients")
	ErrEmptyToken          = errors.New("push token is empty")
	ErrTooManyMessages     = errors.New("too many messages in a single request")
	ErrTooManyTickets      = errors.New("too many tickets in a single request")
	ErrServerError         = errors.New("server error")
	ErrTooManyRequests     = errors.New("too many requests")
	ErrInvalidTicketCount  = errors.New("the number of tickets doesn't match the number of messages")
	ErrBadRequest          = errors.New("invalid request")
	ErrUnknown             = errors.New("unknown error")
	ErrExpired             = errors.New("timed out delivering message")
)

Functions

This section is empty.

Types

type Client

type Client struct {
	// The underlying client to use for HTTP requests.
	Client http.Client

	// The base API URL.
	//
	// Default: "https://exp.host"
	BaseURL string

	// The Bearer token to send in all HTTP requests.
	AccessToken string
}

func NewClient

func NewClient() *Client

func (Client) FetchReceipt

func (c Client) FetchReceipt(ctx context.Context, ticket Ticket) *Receipt

Check the delivery status for a push notification.

Returns nil if the message is not delivered yet. Returns the default Receipt (with Error == nil) is the message is successfully delivered.

func (Client) FetchReceipts

func (c Client) FetchReceipts(
	ctx context.Context,
	tickets []Ticket,
) (map[Ticket]Receipt, error)

func (Client) SendMessage

func (c Client) SendMessage(ctx context.Context, msg Message) (Ticket, error)

func (Client) SendMessages

func (c Client) SendMessages(ctx context.Context, msgs []Message) ([]Resp, error)

type Data

type Data map[string]string

type Message

type Message struct {
	// Expo push tokens specifying the recipient(s) of this message.
	To []Token `json:"to"`

	// The title to display in the notification.
	//
	// On iOS, this is displayed only on Apple Watch.
	Title string `json:"title,omitempty"`

	// The message to display in the notification.
	Body string `json:"body"`

	// A dict of extra data to pass inside of the push notification. The total notification payload must be at most 4096 bytes.
	Data Data `json:"data,omitempty"`

	// A sound to play when the recipient receives this notification.
	//
	// Specify "default" to play the device's default notification sound,
	// or omit this field to play no sound.
	Sound string `json:"sound,omitempty"`

	// The number of seconds for which the message may be kept around for redelivery
	// if it hasn't been delivered yet.
	TTL int `json:"ttl,omitempty"`

	// Delivery priority of the message.
	Priority Priority `json:"priority,omitempty"`

	// An integer representing the unread notification count.
	//
	// This currently only affects iOS. Specify 0 to clear the badge count.
	Badge int `json:"badge,omitempty"`

	// ID of the Notification Channel through which to display this notification on Android devices.
	ChannelID string `json:"channelId,omitempty"`
}

type Priority

type Priority string
const (
	PriorityNormal Priority = "normal"
	PriorityHigh   Priority = "high"
)

type Promise

type Promise struct {
	// contains filtered or unexported fields
}

func (*Promise) Callback

func (p *Promise) Callback(cb func(error))

Register function to be called when Promise is Done.

func (*Promise) Done

func (p *Promise) Done() <-chan struct{}

Channel that will be closed when the Promise is done.

func (*Promise) Err

func (p *Promise) Err() error

func (*Promise) Resolve

func (p *Promise) Resolve(err error)

Set the error (if not nil) and mark the Promise as Done.

func (*Promise) Wait

func (p *Promise) Wait(ctx context.Context)

Wait for the Promise to be Done.

type Receipt

type Receipt struct {
	Error error
}

type Resp

type Resp struct {
	Ticket Ticket
	Error  error
}

type Service

type Service struct {
	// The underlying client to use for sending requests.
	Client *Client

	// For how long to aggregate messages into chunks.
	//
	// In other words, it's the time since calling Send for the first message
	// in a chunk to sending the whole chunk to the server.
	//
	// Default: 1 second.
	SendChunk time.Duration

	// For how long to aggregate tickets into chunks.
	//
	// Default: 1 second.
	ResolveChunk time.Duration

	// How often to re-check the status of a ticket.
	//
	// Default: 1 second.
	ResolveInterval time.Duration
	// contains filtered or unexported fields
}

The background service taking care of delivering notifications, batching requests, retries, resolving promises, etc.

func NewService

func NewService() *Service

func (*Service) Run

func (s *Service) Run(ctx context.Context)

Start the background service.

Exits when the context is cancelled.

func (*Service) Send

func (s *Service) Send(ctx context.Context, msg Message) *Promise

Send the message in background, wait for its status to update, and resolve the Promise.

type Ticket

type Ticket string

func Flatten

func Flatten(resps []Resp) ([]Ticket, error)

type Token

type Token string

Directories

Path Synopsis
bin
expofier command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL