How to log user activities using the Beacon Web API?

Featured on Hashnode

Subscribe to my newsletter and never miss my upcoming articles

The Beacon API is a relatively unknown, lightweight, and efficient way to log web page activities to a server. It is a JavaScript API that helps the developer to send a small amount of data such as, analytics or tracking information, debugging, or diagnostic data from the browser to the server.

In this article, we will learn about the Beacon API and use it to log some user activities to the server. Hope you enjoy knowing it.

The Beacon Web API

The Beacon API schedules an asynchronous and non-blocking request to a web server. There are a few specialties of the beacon request,

  • Beacon requests do not require a response. This is a huge difference from the regular XHR or fetch requests where the client(browser) expects a response from the server.
  • Beacon requests are guaranteed to be initiated before a page is unloaded, even when you close the browser.
  • Beacon requests complete without requiring a blocking request (XHR, for example).
  • Beacon requests use the HTTP POST method.

Some of the characteristics like, non-blocking, no expectations on the response make the beacon requests extremely useful to send data to the server when a page unloads(example, closing the browser, page navigations, etc.).

How to use the Beacon API?

The Beacon API has wide browser support. It works on most of the browsers except the older internet explorer versions.

Just to be on the safer side, we can test the browser support using this simple check,

if (navigator.sendBeacon) {
  // Initiate a beacon request
} else {
  // May be, fallback to XHR or fetch?
}

The Navigator.sendBeacon() method sends a beacon request to the server. The method takes two arguments, the URL to the server and the data. The sendBeacon() method returns a boolean. It returns true when the request is placed correctly in the queue and a false otherwise.

if (navigator.sendBeacon) {
  navigator.sendBeacon('/log-tracking', data);
} else {
  // May be, fallback to XHR or fetch?
}

The data argument of the sendBeacon() method is optional and it is of type of an ArrayBufferView, Blob, DOMString, or FormData. Let us use the FormData to create the data for our example.

function sendAnalytics(msg) {
  if (navigator.sendBeacon) {
    let data = new FormData();
    data.append('start', startTime);
    data.append('end', performance.now());
    data.append('msg', msg);

    navigator.sendBeacon('/log-tracking', data);
  } else {
    // May be, fallback to XHR or fetch?
  }
}

In the above example, we are sending the start and end time that a user would have spent on the application. we are also sending a tracking msg that captures what are the activities user would have performed(like, button clicked, scrolled to a page section, etc.).

We can specify a handler for the unload and/or beforeunload events and call the sendAnalytics() method.

window.addEventListener('unload', function() {
  sendAnalytics(msg);
});

As we are sending the data using the URL /log-tracking, the server-side code can log this information anywhere and use it for analytics purposes.

Here is a sample express server code that logs this information in the console. We can be creative enough to log it in the file system, database, etc. Please note, the server is not sending back any response here.

app.post('/log-tracking', function(req, res) {
  console.log('**** Tracked. Now logging ****');

  let startTime = req.body.start;
  let endTime = req.body.end;
  let trackInfo = req.body.msg;

  let logMsg = '';
  let time = (endTime - startTime) / 1000;
  logMsg = `${time.toFixed(2)} seconds`;

  if (time > 60) {
      time = time / 60;
      logMsg = `${time.toFixed(2)} minutes`;
  }
  console.log('In Session for: ', logMsg);
  console.log('Tracking info: ', trackInfo);
});

Demo

Here is a quick demo(12 seconds) to showcase how Beacon requests work. We have two buttons here. One button is to send an ad-hoc beacon request to the server and another one simulates the browser unload event.

As you see below, the server console logs the time spent information with a sample message. Also note, the information logs when the user closes the browser.

If we see the beacon request in the developer tool(Network tab), we will see the request as pending because the server doesn't send a response. That's why it is a better use when we send a beacon request on browser unload event.

image.png

Another note is, the beacon request is not an XHR request. You can see the All option is selected above to trace this request.

Source Code

All the source code used in this article and for the demo is in the GitHub repo mentioned below. Please feel free to fork, modify, and use. Show your support with a star(⭐) if you liked the project. You are welcome to follow 🤝 me in GitHub to stay connected.

Primary Use-cases

There are two primary use-cases where the Beacon API can be useful.

User activity tracking and analytics

You may want to capture and send an analytics report of your user activities and behavior. These activities may include,

  • How long a user was in the session?
  • What are the user interface controls users used?
  • Any other kind of telemetry information to capture.

We may want to use any of the analytics tools and services like Google analytics for this but, it is hard to convenience our customers especially with an enterprise application.

One more point about the analytics and user activity tracking is, you need to take the end user's consent before you enable a feature like this.

Debugging and diagnostics

Have you ever faced situations like a feature works locally(in the dev mode) but doesn't work as expected in the customer environment(production mode)? This is a typical situation where the Beacon API can be a day(or night) saver.

You can logically send these lightweight beacon requests to log useful trace path information and debug them as needed.

Data limit with Beacon API

There is a limit on the data that can be sent to the server using Beacon API. However, this limit is not uniform across all the browsers and user-agents.

As per the spec,

The user agent must restrict the maximum data size to ensure that beacon requests are able to complete quickly and in a timely manner.

Please read through the beacon API specifications from w3c.org for more information.

Summary

In Summary,

  • The Beacon API is a lightweight API to send a small amount of data from the browser to the server.
  • The beacon requests are non-blocking asynchronous requests. There is no expectation from the server to send a response for a beacon request.
  • The beacon requests are guaranteed to be initiated before a page is unloaded.
  • We can use it for user activities, behavior analysis, and production time debugging.
  • There are plenty of tools out that are doing the user activity, behavior analysis, and create logs. However, they are not viable many times due to the cost and our enterprise user's unfriendliness to these apps.
  • It is also important to know what is available natively with JavaScript to understand the basics better.

More read on the Beacon API is from here,

Hope you learned something new today and all set to give the Beacon API a try sooner. You may also like,


If it was useful to you, please Like/Share so that, it reaches others as well. Please hit the Subscribe button at the top of the page to get an email notification on my latest posts.

You can @ me on Twitter (@tapasadhikary) with comments, or feel free to follow.

Bolaji Ayodeji's photo

Love this Tapas Adhikary, would try this out!

Tapas Adhikary's photo

Sure thing. Thanks Bolaji.

Sébastien Portebois's photo

I forgot about this, thanks for sharing 🙏

It’s also worth noting that Beacon API isn’þ restricted by CORS policies as much as regular XHRs requests.

The specs: w3.org/TR/beacon

And some interesting story about CORS, Beacon, Chrome (and Firefox) : medium.com/@longtermsec/chrome-just-hardene..

Tapas Adhikary's photo

Sébastien Portebois, Thanks!

Great point about CORS and thanks for the links. Must go through.

Chris Bongers's photo

Nice I remember Beacons being quite big from the apple days, not sure if this API can also be used for those?

Tapas Adhikary's photo

Good point Chris Bongers, certainly something to check out. Thanks for reading and commenting!

Edidiong Asikpo's photo

Very insightful article. I love it Tapas Adhikary.

Shamaayil Ahmed's photo

Really informative post.

Tapas Adhikary's photo

Thank you very much!

Vamsi Rao's photo

Great article, will try this soon!

Tapas Adhikary's photo

Great! Thanks.

Bhanu Teja Pachipulusu's photo

Exactly what I needed this week. Thanks 🙏 Bookmarked.