Bearer

The Bearer Documentation Hub

Welcome to the Bearer documentation hub. You'll find comprehensive guides and documentation to help you start working with Bearer as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started    Changelog

Getting Up and Running

Now that the Scenario structure is ready, let's code a real use case!

In this example, we are going to build a Scenario that queries GitHub API and let users retrieve and attach PullRequest. First, we are going to have to retrieve the user's repositories.

1. Create a GitHub OAuth Application

In order to use GitHub OAuth 2 API we first need to create an OAuth App on GitHub.
To do so, go to GitHub's developer setting page here and click on the Register a new application button:

Then fill the information as below and click Register application:

GitHub will provide a Client ID and a Client Secret in return that you will need for later.

2. Authentication Configuration

The create bearer command generated an auth.config.json file holding the API's OAuth parameters. We have to complete it using GitHub's OAuth definition as shown below:

{
  "authorizationURL": "https://github.com/login/oauth/authorize",
  "tokenURL": "https://github.com/login/oauth/access_token",
  "authType": "OAUTH2",
  "tokenParams": {},
  "authorizationParams": {},
  "config": {
    "accessType": "offline",
    "scope": ["repo"]
  },
  "setupViews": [
    {
      "type": "text",
      "label": "Client ID",
      "controlName": "clientID"
    },
    {
      "type": "password",
      "label": "Client Secret",
      "controlName": "clientSecret"
    }
  ]
}

We have added the authorizationURL, the tokenURL and the config like accessType and scope.

Setup Components

By default you don't need to change anything on the SetupViews part, this will help automatically generate Setup Root Components.

3. API Client

The client.ts file provides Intents with a Client object to easily query the API, handling by default all the necessary headers used by the chosen Authentication mechanism. We need to change the baseURL key with the Service provider's API base URL, in our case GitHub:

import axios from 'axios'

export default function(token: string) {
  const headers = {
    'Accept': 'application/json',
    'User-Agent': 'Bearer',
    'Authorization': `token ${token}`
  }

  return axios.create({
    // Adding GitHub API BaseURL
    baseURL: 'https://api.github.com',
    timeout: 5000,
    headers
  })
}

Once deployed, Bearer will automatically handle the OAuth flow, from dance to refresh tokens.

Access Token - OAuth in Local Development

In local development Bearer won't perform the OAuth flow, instead, you have to rely on a static test access token, learn more. You can also generate an access token directly on GitHub Settings page here.

Below, we added a GitHub OAuth test access token to the config.dev.js.example that we renamed config.dev.js:

module.exports = {
  global: { authAccess: { accessToken: 'CHANGE_ME' } },
}

We are now ready to perform API call on GitHub's API!

Reloading Server

Make sure to reload yarn start once you change the config.dev.json for the changes to take effect.

4. Intents

The intents/ folder is where the Scenario backend logic belongs - retrieving data from the GitHub API and exposing it to the UI through components located in the views/ folder.

In this Scenario we are going to start by retrieving the list of repositories belonging to the authenticated user.

Bearer comes with a set of Generators to help you bootstrap some code!
Run the yarn generate command and choose Intent:

$ yarn generate
? What do you want to generate (Use arrow keys)
❯ Intent
  Component

Choose the Intent type, Fetch:

$ yarn generate
? What do you want to generate Intent
? What type of intent do you want to generate
❯ Fetch
  ------
  Save State

Choose a name, listRepositories:

$ yarn generate
? What would you like to generate: Intent
? Type: Fetch
? Name: listRepositories
    create: intents/listRepositories.ts

Intent generated

Skip the prompt

You can achieve the same result without any prompt by running

yarn bearer generate:intent -tfetch listRepositories
or again shorter
yarn bearer g:i -tfetch listRepositories

Intent Types

Bearer provides multiple Intent types through the generator, learn more.

Let's take a look at the generated Intent:

import { TOAUTH2AuthContext, FetchData, TFetchActionEvent, TFetchPromise } from '@bearer/intents'
// Uncomment this line if you need to use Client
// import Client from './client'

export default class ListRepositoriesIntent extends FetchData implements FetchData<ReturnedData, any, TOAUTH2AuthContext> {
  async action(event: TFetchActionEvent<Params, TOAUTH2AuthContext>): TFetchPromise<ReturnedData> {
    // const token = event.context.authAccess.accessToken
    // Put your logic here
    return { data: [] }
  }
}

/**
 * Typing
 */
export type Params = {
  // name: string
}

export type ReturnedData = {
  // foo: string[]
}

The generator pre-built the structure to query a collection type API.

Let's modify the Intent in order to query GitHub Repository endpoint:

import { TOAUTH2AuthContext, FetchData, TFetchActionEvent, TFetchPromise } from '@bearer/intents'
import Client from './client'

export default class ListRepositoriesIntent extends FetchData
  implements FetchData<ReturnedData, any, TOAUTH2AuthContext> {
  async action(event: TFetchActionEvent<Params, TOAUTH2AuthContext>): TFetchPromise<ReturnedData> {
    try {
      const { data } = await Client(event.context.authAccess.accessToken).get<Repository[]>('user/repos')
      return { data }
    } catch (error) {
      return { error: error.toString() }
    }
  }
}

/**
 * Typing
 */
export type Params = {
  // name: string
}

type Repository = {
  id: number
}

export type ReturnedData = Repository[]

The Client object uses Axios under the hood. If you are not familiar with it, make sure to check it out.

We can test our Intent through the command line using yarn invoke followed by the Intent file name:

$ yarn invoke listRepositories

Bearer: [local:intentServer] Reloading intents
Bearer: [local:intentServer] Intents reloaded
Bearer: [local:intentServer] Serving: http://127.0.0.1:3000
Bearer: [local:intentServer] paths: /api/v1/items/:referenceId, /api/v1/items/:referenceId, /api/v1/items/:referenceId, /api/v1/items, /v1/user/initialize, /v1/auth/:integration_uuid, /api/v1/unset-scenari0-uuid/:intentName
  <-- POST /api/v1/unset-scenari0-uuid/listRepositories
  --> POST /api/v1/unset-scenari0-uuid/listRepositories 200 1,084ms 146.05kb
{
  "data": [
    {
      "id": 21244246,
			....

You can also specify a file path, relative to the scenario root, to pass parameters:

$ yarn invoke listRepositories -p params.json
{
  "params": {
    "per_page": 5
  },
  "body": {},
  "httpMethod": "GET"
}

We've successfully implemented our first Intent! Now we are going to look at how to display those repositories on the UI.

5. Views

The views/ folder is where Scenario UI belongs - accessing Intents data and displaying it with components.

Our previous Intent is in charge of fetching GitHub's repositories. This time we will create a component in charge of displaying it to the user.

Run the yarn generate command and choose Component:

$ yarn generate
? What do you want to generate
  Intent
❯ Component

Choose the Intent type, Collection:

$ yarn generate
? What do you want to generate Component
? What type of component do you want to generate
  Blank
❯ Collection
  Root Component

Name it listRepositories:

$ yarn generate
? What would you like to generate: Component
? What kind of component would you like to generate: Collection
? Name: listRepositories
    create: views/components/listRepositories.tsx

Component generated

The generator bootstrapped the file views/components/listRepositories.tsx.

Component naming

Bearer components are transformed into WebComponents. In order to be compliant with HTML components need to be written in camelCase style (ex: myComponent).

Let's take a look at listRepositories.tsx:

import { BearerFetch, Component, Intent } from '@bearer/core'

@Component({
  tag: 'list-repositories',
  shadow: true
})
export class ListRepositories {
  @Intent('listRepositories') fetcher: BearerFetch
  render() {
    return <bearer-scrollable fetcher={this.fetcher} />
  }
}

The generator created a component with a default structure to display data from a collection.

Notice the property:

  • tag: 'list-repositories': This will be the HTML tag referencing the component <list-repositories />

Components vs WebComponents

Every component built with Bearer is transformed into a WebComponent.

The generator used:

  • @Intent('listRepositories') fetcher: BearerFetch: Intent decorator that fetches the data from the Intent we previously created.
  • <bearer-scrollable />: Component in charge of building a scrollable paginated list with the data from the Intent.

Bearer comes with a Component library that can be used to build custom components. Behind the scenes here is the <bearer-scrollable /> with its default properties:

<bearer-scrollable
    fetcher={this.fetcher}
    perPage={10}
    renderCollection={collection => (
      <bearer-navigator-collection
        data={collection}
        renderFunc={repository => repository.name}
      />
    )}
/>

6. Linking Scenarios and Components

A Scenario acts as a gate between a host app and a 3rd party Service API. To make Integration of a Scenario easy, Bearer relies on components that can be integrated as custom HTML tags, known as WebComponents. These components are created using the @RootComponent() decorator.

Root Components can be thought as tiny encapsulated micro applications. They can stack components to provide a richer experience, and Navigation between them is handled by a special component, <bearer-navigator/>.

Here is how everything is linked:

Let's open the Scenario feature action Root Component:

import { RootComponent } from '@bearer/core'
import '@bearer/ui'

@RootComponent({
  role: 'action',
  group: 'feature'
})
export class FeatureAction {
  render() {
    return (
      <bearer-navigator direction="right">
        <span slot="navigator-btn-content">Feature Action</span>
        <bearer-navigator-auth-screen />
        <bearer-navigator-screen navigationTitle="My first screen">
          <div style={ { textAlign: 'center' } }>My first screen</div>
        </bearer-navigator-screen>
      </bearer-navigator>
    )
  }
}

By default <bearer-navigator/> is included alongside the <bearer-navigator-auth-screen /> as its first child element . It uses a Popover style. (Those components are part of the Advanced Components UI library and offer a set of customizable properties)

To include the listRepositories component previously created in the stack of our Root Component, we use a <bearer-navigator-screen/> component and replace the sample one generated:

import { RootComponent } from '@bearer/core'
import '@bearer/ui'

@RootComponent({
  role: 'action',
  group: 'feature'
})
export class FeatureAction {
  render() {
    return (
      <div>
        <bearer-navigator direction="right">
          <span slot="navigator-btn-content">Feature Action</span>      
          <bearer-navigator-auth-screen />
          <bearer-navigator-screen navigationTitle="Repositories" name="repository">
            <list-repositories />
          </bearer-navigator-screen>
        </bearer-navigator>
      </div>
    )
  }
}

This component is added after the authentication since it is the first step required to run our Scenario. We included our <list-repositories /> component inside the <bearer-navigator-screen/>.

That's how navigation & routing is done!

Scenario Anatomy

Learn more about the Anatomy of a Scenario and the underlying component structure.

7. Testing

It's about time we test our Intent and Screen that we previously implemented.

$ yarn start

Open your browser and you should see this result:

By now you should have a better understanding of how Bearer works. It's time to go deeper in the next section!

What have we accomplished?

We fetched data from GitHub API with an Intent, built a UI Component that retrieves the Intent data and displays it on a paginated infinite scrollable list.


What's Next

Going deeper..