Jonathan Bennett

Reliable Web Push Delivery with Error Handling

Notifications usually work behind the scenes, so they don’t typically include much in the way of error handling for users.

Currently, we try to push notifications and silently handle most errors:

# app/models/web_push_subscription.rb
class WebPushSubscription < ApplicationRecord
  # …
  
  def deliver_payload(data)
	WebPush.payload_send(
	  message: data.to_json,
	  endpoint: endpoint,
	  p256dh: p256dh,
	  auth: auth,
	  vapid: {
		public_key: ENV.fetch("VAPID_PUBLIC"),
		private_key: ENV.fetch("VAPID_PRIVATE")
	  }
	)
	
  rescue WebPush::ExpiredSubscription
	Rails.logger.info "Removing expired WebPush subscription"
	destroy
  rescue WebPush::Unauthorized
	Rails.logger.info "Removing unauthorized WebPush subscription"
	destroy
  end
end

This approach works well for simply pushing notifications. However, if we want to build a user-facing interface for managing notifications, we need to expose more details about delivery success or failure.

We can follow the pattern of ActiveRecord’s save method to update deliver_payload so it returns whether the delivery was successful and includes a delivery_error attribute if an error occurs.

# app/models/web_push_subscription.rb
class WebPushSubscription < ApplicationRecord
  attr_reader :delivery_error
  
  # …
  
  def deliver_payload(data)
	WebPush.payload_send(
	  message: data.to_json,
	  endpoint: endpoint,
	  p256dh: p256dh,
	  auth: auth,
	  vapid: {
		public_key: ENV.fetch("VAPID_PUBLIC"),
		private_key: ENV.fetch("VAPID_PRIVATE")
	  }
	)
	
	@delivery_error = nil
	
  rescue WebPush::ExpiredSubscription
	@delivery_error = "Removing expired WebPush subscription"
	destroy
	
  rescue WebPush::Unauthorized
	@delivery_error = "Removing unauthorized WebPush subscription"
	destroy
	
  ensure
	if @delivery_error
	  Rails.logger.warn @delivery_error
	  false
	else
	  true
	end
  end
end

With these updates, we can now determine whether a notification was successfully delivered or identify specific errors when something goes wrong.

Tomorrow, we’ll build on this to create a user-facing interface for testing notifications.