Startup Clix: Pusher Presence Channels with AWS SAM

K - May 6 '18 - - Dev Community

After I lined out some basic ideas about the game I want to make, I finally had time to start some implementation.

What happened

Not much actually... and I'm already sitting 8h on this thing.

Well, Pusher is easy going, but AWS SAM & Lambda & API-Gateway aren't that simple.

Project Setup

First I created a repo on GitHub in which I'm gonna version my game.

I split it up in back-end, which is a AWS SAM application, and front-end which is, at the moment a pile of garbage.

You need a server to handle authentication for Pusher presence channels, and I need them to keep track of players and distribute events to all players in a game.

Back-End

Currently it consist of 3 Lambda functions. The JavaScript in the back-ed/functions directory gets wired up via the template.yaml. AWS SAM Local is used for deployment.

  • hello gets called by CloudWatch every minute and publishes a test message to the only channel

This is how a Lambda Function looks with a 1 minute rate schedule event:

  HelloFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./functions/hello
      Events:
        ScheduleOneMinuteEvent:
          Type: Schedule
          Properties:
            Schedule: rate(1 minute)
Enter fullscreen mode Exit fullscreen mode
  • pusherAuth gets called by the Pusher client to allow a client to joint a presence channel

This is how a Lambda Function looks with an API-Gateway POST event:

  PusherAuthFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./functions/pusherAuth
      Events:
        pusherAuthEvent:
          Type: Api
          Properties:
            Path: /pusherauth
            Method: post
Enter fullscreen mode Exit fullscreen mode

This is how my simple authentication code looks like at the moment:

// Loading and initializing Pusher server API
// only on a cold start
const Pusher = require("pusher");
const pusher = new Pusher({
  appId: process.env.APP_ID,
  key: process.env.APP_KEY,
  secret: process.env.SECRET_KEY
});

exports.handler = (event, context, callback) => {

  // the Pusher client doesn't send JSON, so I just crack it open manually
  const body = event.body.split("&");
  const socket_id = body[0].split("=")[1];
  const channel_name = body[1].split("=")[1];

  // some data Pusher will distribute to the clients on channel subscription
  const presenceData = {
    user_id: socket_id,
    user_info: {
      name: "Player " + Math.random()
    }
  };

  // checking if this is all good and well, hurr
  const auth = pusher.authenticate(socket_id, channel_name, presenceData);

  // API-Gateway wants its response it that format
  const response = {
    statusCode: 200,
    body: JSON.stringify(auth)
  };

  callback(null, response);
};
Enter fullscreen mode Exit fullscreen mode
  • getGameChannel gives the client a channel to join, later this gets more logic to check which games are open, not full, waiting for players etc.

Front-End

Not much happening here. Got a Bootstrap theme, loaded the Pusher client and connected.

The whole thing is hosted on Github Pages.

Problems

  • CORS setup seems to be a huge pain with AWS SAM, so I'm using CORS Everywhere until I get this fixed.

  • Packaging of Lambda functions is needed. Currently I have to copy the node_modules directory into every function directory before packaging. Would be cool if I could simply install it in the back-end directory and it gets copied automatically.

  • Some helper functions are needed too. The whole Lambda experience is very basic at the moment.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .