What is REST?

If your friend was going to the store and you needed something, you might ask them to "get" something for you. After they get back, they might "put" that item away for you. These are the constructs we use in everyday life, and REST APIs attempt to build on those, but, rather than operating on physical items, you're dealing with objects on the computer.

When you send HTTP requests to webpages, each request contains a verb. Most browsing you do involves your web browser making GET requests—asking for this page, for instance! But there are several other verbs your browser uses:

  • GET looks something up, as we've said
  • POST creates something
  • PUT updates something
  • DELETE destroys something

On our RESTful API, you make requests by calling VERB https://beam.pro/api/v1/resources. We'll abbreviate this as VERB /resources from now on. For instance, to create a new user, you would call POST /users (the endpoints are always plural). If you want to operate on a particular user object, you would append their ID to that URL, such as GET /users/344 to return information about user ID 344.

You can also run actions on a particular user by chaining on "actions", such as PUT /users/344/confirm, which is used to verify an account. Here's a full blueprint with some examples:

Endpoint Description Example
GET /resources Returns a list of 'resource' objects. GET /users
POST /resources Creates a new 'resource' object. POST /users
GET /resources/{id} Returns information about the 'resource' with the provided ID. GET /users/314
PUT /resources/{id} Updates a resource with the provided ID. PUT /users/314
POST /resources/{id}/action Runs some action on a 'resource'. PUT /users/314/confirm
GET /resources/{id}/data Gets some nested information about a 'resource'. GET /users/314/avatar
DELETE /resources/{id} Delete a resource from the server. DELETE /channels/314/streamKey

Writing the Code

We’re going to be using our Node and Java clients to build this app. The user will run it and it'll tell them how far they are from being the top streamer on Beam!

Prerequisites

  1. Get NodeJS and NPM for your platform.
  2. Create a new project with npm.
  3. Run npm i -S beam-client-node

Usage

Let's start by importing all of the modules which we'll need.

'use strict';

const Beam = require('beam-client-node');

const beam = new Beam();

To get the user's channel name we'll ask for it from the command line and use this to make a REST request that will provide a response with their channel details. We'll then print out how many viewers the have on their channel.

'use strict';

const Beam = require('beam-client-node');

const beam = new Beam();

const channelName = process.argv[2];

beam.request('GET', `channels/${channelName}`)
.then(res => {
    const viewers = res.body.viewersTotal;
    console.log(`You have ${viewers} total viewers...`);
});

You've already got working code that connects and talks to our API. Go ahead, try it! Run node rank.js <your_username> in your terminal.

$ node rank.js connor4312
You have 595 total viewers...

Now it's time to dig up all that stuff you learned in your computer science course. We want to sort the channels on Beam by the number of viewers they have, and loop through until we find the first channel that has less viewers than we do. To do this we'll have to make several calls to the API, and in Node this kind of chaining is often done through recursion.

We define a function simply called run that we initially call with page 0. Each time it makes a request, it'll count up the channels it gets back and stop when it gets a channel less or as popular as we are.

// ...
let rank = 1;
const run = (page) => {
    return getChannelsDescending(page).then(res => {
        for (let i = 0; i < res.body.length; i++) {
            const channel = res.body[i];
            if (channel.viewersTotal <= viewers) {
                console.log(`Your rank on Beam is ${rank}!`);
                return;
            }

            rank++;
        }

        console.log(`Your rank is at least ${rank}...`);
        return run(page + 1);
    });
};

return run(0);
// ...

Now we just need to fill in that mysterious getChannelsDescending(page) function with a call on the Beam client. The client has several helpers here that are essentially light wrappers around the request package. So, we'll pass options in that we read from the channel endpoint docs to do what we need:

// ...
const run = (page) => {
    return beam.request('GET', '/channels', {
        qs: {
            page,
            fields: 'viewersTotal',
            order: 'viewersTotal:DESC',
        },
    }).then(res => {
// ...

All together now, you can put this together into a single script...

'use strict';

const Beam = require('beam-client-node');

const beam = new Beam();

const channelName = process.argv[2];

beam.request('GET', `channels/${channelName}`)
.then(res => {
    const viewers = res.body.viewersTotal;
    console.log(`You have ${viewers} total viewers...`);

    let rank = 1;
    const run = (page) => {
        return beam.request('GET', '/channels', {
            qs: {
                page,
                fields: 'viewersTotal',
                order: 'viewersTotal:DESC',
            },
        }).then(res => {
            for (let i = 0; i < res.body.length; i++) {
                const channel = res.body[i];
                if (channel.viewersTotal <= viewers) {
                    console.log(`Your rank on Beam is ${rank}!`);
                    return;
                }

                rank++;
            }

            console.log(`Your rank is at least ${rank}...`);
            return run(page + 1);
        });
    };

    return run(0);
});

...and run it to get your rank on Beam!

$ node rank.js connor4312 l337hax0r
You have 595 total viewers...
Your rank is at least 51...
Your rank is at least 101...
Your rank is at least 151...
Your rank is at least 201...
Your rank is at least 251...
Your rank is at least 301...
Your rank is at least 351...
Your rank is at least 401...
Your rank is at least 451...
Your rank is at least 501...
Your rank is at least 551...
Your rank is at least 601...
Your rank is at least 651...
Your rank is at least 701...
Your rank is at least 751...
Your rank on Beam is 761!

Prerequisites

  1. Java 1.8 or above
  2. A Java IDE such as:
    1. Eclipse
    2. IntelliJ
    3. NetBeans
  3. A Java Project Manager such as:
    1. Maven
    2. Gradle

Project Setup

Setup a standard project for your environment and include beam-client-java as a dependancy.

To setup beam-client-java, first add the Beam repo to your pom.xml as a repository as follows:

<repositories>
  <repository>
    <id>beam-snapshots</id>
    <url>https://maven.beam.pro/content/repositories/snapshots/</url>
  </repository>
</repositories>

And secondly, add this project as a dependency in your pom.xml:

<dependencies>
  <dependency>
    <groupId>pro.beam</groupId>
    <artifactId>api</artifactId>
    <version>3.0.3-SNAPSHOT</version>
  </dependency>
</dependencies>

To setup beam-client-java, first add the Beam repo to your build.gradle as a repository as follows:

repositories {
    maven {
        name = "beam"
        url = "https://maven.beam.pro/content/repositories/snapshots"
    }
}

And secondly, add this project as a dependency in your build.gradle:

dependencies {
    compile "pro.beam:api:3.0.3-SNAPSHOT"
}

Usage

Let's start by creating a Main class for the Java application and importing all of the required packages. We'll also initialize an instance of the BeamAPI.

import pro.beam.api.BeamAPI;
import pro.beam.api.http.SortOrderMap;
import pro.beam.api.resource.BeamUser;
import pro.beam.api.resource.channel.BeamChannel;
import pro.beam.api.response.channels.ShowChannelsResponse;
import pro.beam.api.services.impl.ChannelsService;
import pro.beam.api.services.impl.UsersService;

import java.util.concurrent.ExecutionException;

public class Tutorial {
    public static BeamAPI beam;

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        beam = new BeamAPI();
    }
}

Next let's fetch the user's channel we'll grab their username off the command line. The Java Client uses a service orientated approach which means all channel related functions are on the ChannelService. So we need to use that service to get the channel data. Most methods that talk to the API return a Java Future. These are a form of asyncronous operation in Java. For this tutorial however we'll use the get method of a future to wait for the Future to complete. It returns a BeamChannel object. On the channel we can find the viewersTotal property which we can then print.

//...
BeamChannel channel = beam.use(ChannelsService.class).findOneByToken(args[0]).get();

int viewers = user.channel.viewersTotal;
System.out.format("You have %d total viewers...\n", viewers);

run(0,viewers,1);
//...

Now you've already got some working code!

To run your project you can use your Java IDE to create a run configuration that supplies the username as Program Arguments. If you're using IntelliJ it should look like this:

IntelliJ Run Configuration

Next let's make a function which gets a page of channels from the API sorted in descending order based on total views.

//...
public static ShowChannelsResponse getChannelsPage(int page) throws ExecutionException,InterruptedException  {
    SortOrderMap<ShowChannelsResponse.Attributes, ShowChannelsResponse.Ordering> map = new SortOrderMap<>();
    map.put(ShowChannelsResponse.Attributes.VIEWERS_TOTAL, ShowChannelsResponse.Ordering.DESCENDING);
    return beam.use(ChannelsService.class).show(map,page,100).get();
}
//...

Finally, we can make a function that loops through the BeamChannel objects on the ShowChannelsResponse, until it gets to a channel that equal to or lower than you're rank. If this is not found we request the next page recursively.

When it finds such a channel, it'll log it to the console.

public static int run(int page, int viewers, int rank) throws ExecutionException,InterruptedException {
    ShowChannelsResponse channels = getChannelsPage(page);
    for (int i = 0; i < channels.size(); i++) {
        BeamChannel channel = channels.get(i);
        if (channel.viewersTotal <= viewers) {
            System.out.format("Your rank on beam is %d!\n", rank);
            return rank;
        }
        System.out.format("Your rank is at least %d...\n", rank);
        rank++;
    }
    return run(page + 1, viewers, rank);
}

To start the ranking process we just need to call run(0, viewers, 1) in the main method. All together you can put the code together into one Java Class...

import pro.beam.api.BeamAPI;
import pro.beam.api.http.SortOrderMap;
import pro.beam.api.resource.channel.BeamChannel;
import pro.beam.api.response.channels.ShowChannelsResponse;
import pro.beam.api.services.impl.ChannelsService;

import java.util.concurrent.ExecutionException;

public class Tutorial {
    public static BeamAPI beam;
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        beam = new BeamAPI();

        BeamChannel channel = beam.use(ChannelsService.class).findOneByToken(args[0]).get();

        int viewers = channel.viewersTotal;
        System.out.format("You have %d total viewers...\n", viewers);

        run(0,viewers,1);


    }
    public static int run(int page, int viewers, int rank) throws ExecutionException,InterruptedException {
        ShowChannelsResponse channels = getChannelsPage(page);
        for (int i = 0; i < channels.size(); i++) {
            BeamChannel channel = channels.get(i);
            if (channel.viewersTotal <= viewers) {
                System.out.format("Your rank on beam is %d!\n", rank);
                return rank;
            }
            System.out.format("Your rank is at least %d...\n", rank);
            rank++;
        }
        return run(page + 1, viewers, rank);
    }
    public static ShowChannelsResponse getChannelsPage(int page) throws ExecutionException,InterruptedException  {
        SortOrderMap<ShowChannelsResponse.Attributes, ShowChannelsResponse.Ordering> map = new SortOrderMap<>();
        map.put(ShowChannelsResponse.Attributes.VIEWERS_TOTAL, ShowChannelsResponse.Ordering.DESCENDING);
        return beam.use(ChannelsService.class).show(map,page,100).get();
    }
}

... and run it to get your Rank on Beam!

You have 2279 total viewers...
Your rank is at least 1...
Your rank is at least 2...
...
Your rank is at least 281...
Your rank is at least 282...
Your rank is at least 283...
Your rank is at least 284...
Your rank on beam is 285!

Prerequisites

  1. Python 3
  2. pip (Python Package Manager)

Unfortunately we don't have a client library built for Python but, don't worry, the API is still super simple to use with the wonderful requests package. Go ahead and fire of pip install requests if you don't already have it installed.

First off, lets import what we need and make a login request to the API. We'll use requests.Session since we may want to expand this app later and the session cookie created when we log in.

import requests
import sys

s = requests.Session()

channel_response = s.get('https://beam.pro/api/v1/channels/{}'.format(sys.argv[1]))

After authenticating we'll get back a HTTP response that includes a bunch of details about the user. We'll print out how many viewers the have on their channel.

import requests
import sys

s = requests.Session()

channel_response = s.get('https://beam.pro/api/v1/channels/{}'.format(sys.argv[1]))

viewers = channel_response.json()['viewersTotal']
print("You have {} viewers...".format(viewers))

You've already got working code that connects and talks to our API. Go ahead, try it! Run python rank.py <your_username> in your terminal.

$ python rank.py connor4312
You have 595 total viewers...

Next, let's make a function that loops the channel endpoint in descending order by total viewers, until it gets to a channel that equal to or lower than you're rank. When it finds such a channel, it'll return it.

def channels_with_more_viewers(viewers):
    rank = 0
    page = 0
    while True:
        channels_response = s.get('https://beam.pro/api/v1/channels', params={
            'fields': 'viewersTotal',
            'order': 'viewersTotal:DESC',
            'page': page
        })

        for channel in channels_response.json():
            if channel['viewersTotal'] <= viewers:
                return rank
            else:
                rank += 1

        print("Your rank is at least {}...".format(rank))
        page += 1

All together now, you can put this together into a single script...

import requests
import sys

s = requests.Session()

def channels_with_more_viewers(viewers):
    """Returns the number of channels that have more than `viewers` viewers.
    """

    rank = 0
    page = 0
    while True:
        channels_response = s.get('https://beam.pro/api/v1/channels', params={
            'fields': 'viewersTotal',
            'order': 'viewersTotal:DESC',
            'page': page
        })

        for channel in channels_response.json():
            if channel['viewersTotal'] <= viewers:
                return rank
            else:
                rank += 1

        print("Your rank is at least {}...".format(rank))
        page += 1


channel_response = s.get('https://beam.pro/api/v1/channels/{}'.format(sys.argv[1]))

viewers = channel_response.json()['viewersTotal']
print("You have {} viewers...".format(viewers))

rank = channels_with_more_viewers(viewers)
print("Your rank on Beam is {}!".format(rank))

...and run it to get your rank on Beam!

$ python rank.py connor4312 l337hax0r
You have 595 total viewers...
Your rank is at least 51...
Your rank is at least 101...
Your rank is at least 151...
Your rank is at least 201...
Your rank is at least 251...
Your rank is at least 301...
Your rank is at least 351...
Your rank is at least 401...
Your rank is at least 451...
Your rank is at least 501...
Your rank is at least 551...
Your rank is at least 601...
Your rank is at least 651...
Your rank is at least 701...
Your rank is at least 751...
Your rank on Beam is 761!

Want More Info?

If you'd like more information on our Rest API, checkout the rest reference docs.

Need more help?

If you're still not sure, or would like some help, hit us up on Gitter!