Tutorial: Building with an environment

Setting up your environment

To get started, create a directory somewhere and create a file with name index.html containing this:

<!DOCTYPE html> <html> <head> <title>My cool app</title> </head> <body> Hello, World </body> </html>

Open a Terminal or 'command prompt' and cd into the directory that contains index.html. Run ls (dir on Windows) to check that index.html is there and that you're in the right place.

Now let's create a local HTTP server.

Head to http://localhost:9000 in your browser. You should see 'Hello, World'.

Connecting index.html to a Sticky application

We need an application that routes to http://localhost:9000/index.html. You can create a second application for your live server later. index.html does not need to change between your test environment and your live server.

Go to the dashboard and choose the 'Applications' tab:

Dashboard showing applications list

Choose 'Create'. You'll now see a new application:

Dashboard showing a new application

Go into the new application. Change its Name and Public name to 'Test app'. Stay on the 'Logic' tab and paste this in the 'Routing logic' box. You may need to scroll down:

return "http://localhost:9000/index.html";

Don't write anything in the 'Client logic' box.

Choose 'Make live' at the top left:

Editing an application in the dashboard

Connecting your Sticky application to a sticker

Go to the dashboard homepage. You can do this by 'going back' in your browser or clicking your account name at the top left. Choose the 'Stickers' tab. Every Sticky account comes with a virtual 'test sticker'. Choose it:

Dashboard showing stickers list

Now choose your application in the 'Application' box on the left (you may need to scroll down). It will already be chosen if you have just signed up. Choose 'Make live' at the top left:

Editing a sticker in the dashboard

Choose 'Get test URL' and copy the URL in the popup box. It should start with https://sticky.to/. Paste this into a new tab. Your browser will redirect to a long URL that starts with http://localhost:9000/index.html?sticky=eyJ... and you should see 'Hello World'.

We'll call this URL the resolved URL. Bookmarking it will make development easier. Congratulations, you now have a working environment!

Loading the SDK

The SDK is a single 21kb, dependency-free JavaScript file. It's served from Google's CDN.

The easiest way to get started is to put the URL in a <script> tag in the <head> element of your app, index.html. The SDK will then be available globally as sticky.

Don't store a copy of the SDK. Load it from Google's CDN so you always have the latest version.

Next, give your public key to the SDK with the setPublicKey function.

Only give your private key to the SDK if you are building an internal tool like the Sticky dashboard and you trust every user. Your private key can do dangerous things and there is no undo.

Finally, call a function like users.get to test it out. Here's the full code for index.html. Remember to replace YOUR_PUBLIC_KEY_HERE with your public key:

<!DOCTYPE html> <html> <head> <title>My cool app</title> <script src='https://storage.googleapis.com/sticky-users/sdk/sticky-sdk-production-v1.js'></script> <script> sticky.setPublicKey('YOUR_PUBLIC_KEY_HERE') sticky.users.get() .then(user => { alert('You are authenticated as ' + user.name) }) </script> </head> <body> Hello, World </body> </html>

Refresh the resolved URL. You should see a popup box that says You are authenticated as (...).

The Sticky flow

What happens when someone taps a sticker? In short, Sticky redirects the user to your application's URL with a "sticky payload" attached to the end as a query parameter (query parameters are the parts of the URL that come after the ?). The query parameter is called sticky. You can see this in your resolved URL:

http://localhost:9000/index.html?sticky=eyJ...

The good news is that you don't need to parse or understand this query parameter. The SDK does it for you. Open the Inspector on your resolved URL and paste this into bottom of the 'Console' tab. Hit enter:

sticky.getPayload()

If you see a full object like this, everything is good:

{ userPublicKey: "..." userSlug: "store" consumerAppUrl: "..." id: "..." applicationId: "..." ...

Back in index.html, use object destructuring to extract applicationId and sessionId from the "sticky payload". Paste this code just after the setPublicKey call.

const { applicationId, sessionId } = sticky.getPayload() console.log('application ID: ' + applicationId) console.log('session ID: ' + sessionId)

Here's the full code for index.html. We have also changed the alert() call to console.log():

<!DOCTYPE html> <html> <head> <title>My cool app</title> <script src='https://storage.googleapis.com/sticky-users/sdk/sticky-sdk-production-v1.js'></script> <script> sticky.setPublicKey('YOUR_PUBLIC_KEY_HERE') sticky.users.get() .then(user => { console.log('You are authenticated as ' + user.name) }) const { applicationId, sessionId } = sticky.getPayload() console.log('application ID: ' + applicationId) console.log('session ID: ' + sessionId) </script> </head> <body> Hello, World </body> </html>

Events

Sticky is based upon a loose "event driven architecture", which means it cares more about events over time than fixed state, which can be out of date and cause bugs.

You can calculate state by "walking over" a set of events. For example, if the first event is "add 3 apples to my cart" and the second event is "remove 1 apple from my cart", then you can be sure there are 2 apples in the cart, without storing the cart in state somewhere.

This is very useful:

  • It gives you a complete log of the user's journey. If events were at a house party, they'd be the best storytellers.
  • It avoids storing state. State can be bad because if two functions try to change state at nearly exactly the same time, one function's state will be overwritten by the other's (there can be just one state at any given time).
  • You can go back in time. You can easily reproduce a bug by cutting a set of events at a specific time. On the other hand, reproducing different states manually is difficult.

Listing events

Listing events is easy. It's time to use the variables we have already got, applicationId and sessionId. Paste this code just after the getPayload call:

sticky.session.getEvents('application-session', { applicationId, sessionId }) .then(events => { console.log('events', events) })

You should get an array (list) of events. There should be just one event and it should look like this:

{ isYours: true id: "...", createdAt: 1586687879, type: "SESSION_READ", thingId: "...", sessionId: "...", applicationId: "...", location: "Halifax, ENG, GB", device: "API" }

This is the event that gets created when someone taps a sticker. You tapped the sticker by using the "Get test URL" button to be redirected to the resolved URL.

Most of the keys on event objects can be ignored. The important keys are type, thingId and createdAt:

  • type: You will soon get to know some of these. They are always formed of capital letters and underscores (e.g. SESSION_READ).
  • thingId: The ID of the sticker that was tapped. Sticky calls stickers 'things' internally.
  • createdAt: the time of the event in Unix time.

Events logic

Let's write some simple logic that shows whether the current user has used this Sticky application before. By caring about the application not the sticker, we can assign the application to multiple stickers in an environment and the user can tap any. If the specific sticker is important to you, try modifying this code to care about event thingId keys. Here's the full code for index.html:

<!DOCTYPE html> <html> <head> <title>My cool app</title> <script src='https://storage.googleapis.com/sticky-users/sdk/sticky-sdk-production-v1.js'></script> <script> const { applicationId, sessionId } = sticky.getPayload() ;(async () => { const events = await sticky.session.getEvents('application-session', { applicationId, sessionId }) const customEvents = events.filter(event => event.type === 'SESSION_CUSTOM_EVENT') const hasUsedApplicationBefore = customEvents.length > 0 if (!hasUsedApplicationBefore) { document.body.innerHTML = 'You have not used this application before' } if (hasUsedApplicationBefore) { document.body.innerHTML = 'You have used this application before' } })() </script> </head> <body> Hello, World </body> </html>

Give it a try. You should see 'You have not used this application before'.

Now let's walk through this bit by bit. This code uses JavaScript's 'async/await' syntax. Google can help you learn this.

First we get the events and filter by type SESSION_CUSTOM_EVENT. When we create an event later, it will have this type because it will be a custom event. Then we define a variable hasUsedApplicationBefore that checks how many of these events match the filter:

;(async () => { const events = await sticky.session.getEvents('application-session', { applicationId, sessionId }) const customEvents = events.filter(event => event.type === 'SESSION_CUSTOM_EVENT') const hasUsedApplicationBefore = customEvents.length > 0 })()

Because there are no SESSION_CUSTOM_EVENT type events for the current user, customEvents is an empty array with length 0. hasUsedApplicationBefore is false because customEvents.length > 0 is false. We show that in the page:

if (!hasUsedApplicationBefore) { document.body.innerHTML = 'You have not used this application before' } if (hasUsedApplicationBefore) { document.body.innerHTML = 'You have used this application before' }

We drop the users.get code that shows who you are authenticated as. We even drop the setPublicKey call because the SDK picks up your public key from the "sticky payload".

Creating an event

It's simple. Make sure you paste this inside the ;(async () => { block because it uses the await keyword.

await sticky.session.createEvent()

index.html now looks like this:

<!DOCTYPE html> <html> <head> <title>My cool app</title> <script src='https://storage.googleapis.com/sticky-users/sdk/sticky-sdk-production-v1.js'></script> <script> const { applicationId, sessionId } = sticky.getPayload() ;(async () => { const events = await sticky.session.getEvents('application-session', { applicationId, sessionId }) const customEvents = events.filter(event => event.type === 'SESSION_CUSTOM_EVENT') const hasUsedApplicationBefore = customEvents.length > 0 if (!hasUsedApplicationBefore) { document.body.innerHTML = 'You have not used this application before' } if (hasUsedApplicationBefore) { document.body.innerHTML = 'You have used this application before' } await sticky.session.createEvent() })() </script> </head> <body> Hello, World </body> </html>

What happens? On the first page load, you see 'You have not used this application before'. On the second (and later) page loads you see 'You have used this application before'.

Implementing a 'tap counter'

This only needs tiny modifications to our work so far. We remove hasUsedApplicationBefore because we care about how many events there are, not if there are any at all. We filter by type SESSION_READ. We then show the length of customEvents in the page. Remember, this logic doesn't care about event key thingId, so the number of taps will be 'shared' between all stickers that this application is connected to.

<!DOCTYPE html> <html> <head> <script src='https://storage.googleapis.com/sticky-users/sdk/sticky-sdk-production-v1.js'></script> <script> const { applicationId, sessionId } = sticky.getPayload() ;(async () => { const customEvents = (await sticky.session.getEvents('application-session', { applicationId, sessionId })) .filter(event => event.type === 'SESSION_READ') document.body.innerHTML = 'You have tapped ' + customEvents.length + ' times' await sticky.session.createEvent() })() </script> </head> <body></body> </html>

The counter doesn't seem to go up. That's because we use session.createEvent which creates events with type SESSION_CUSTOM_EVENT. Whilst it's possible to specify a SESSION_READ type in the session.createEvent body, it would be dishonest. Let Sticky create these events and remove the useless session.createEvent call. To increase the counter, go back to the dashboard, choose your sticker and choose 'Get test URL'. Paste this into a new tab to see the counter change from 1 to 2.