Quick Start

To build our first custom function, we are going to use Github's OAuth API.

This integration will query user's repositories by first letting them connect with their Github account using the OAuth flow.

Create an Integration

Open the console and use the create bearer command:

NPM
Yarn
npm create bearer github-integration
yarn create bearer github-integration

As part of the initialization process, you will be prompted to choose the authentication mechanism of the chosen API.

In this case, start this integration using Github's OAuth API, so we select OAuth2:

? Select an authentication method for the API you want to consume:
OAuth1
❯ OAuth2
Basic Auth
API Key
NoAuth

API Authentication Support

Bearer supports out-of-the-box for most API authentication methods, __learn more.

The initialization process should go something like this:

________________________________
_
| |__ ___ __ _ _ __ ___ _ __
| '_ \ / _ \/ _` | '__/ _ \ '__|
| |_) | __/ (_| | | | __/ |
|_.__/ \___|\__,_|_| \___|_|
________________________________
bearer cli runner.
Links:
* documentation: https://docs.bearer.sh
* portal: https://app.bearer.sh
________________________________
? Select an authentication method for the API you want to consume: OAuth2
✔ Generating integration structure
✔ Create auth files
✔ Install dependencies
create: github-integration/LICENSE
create: github-integration/.gitignore
create: github-integration/.integrationrc
create: github-integration/package.json
create: github-integration/tsconfig.json
create: github-integration/functions/tsconfig.json
create: github-integration/auth.config.json
create: github-integration/functions/client.ts
Integration initialized, name: github-integration, authentication type: OAuth2
What's next?
* read the bearer documentation at https://docs.bearer.sh
* start your local development environment by running:
cd github-integration

From this output, the command should have generated a github-integration/ folder and bootstrapped many files.

Structure

The structure in the integration folder should look like this:

cd github-integration
tree -L 2 -I node_modules
.
├── LICENSE
├── auth.config.json
├── functions
│ ├── client.ts
│ └── tsconfig.json
├── package.json
├── tsconfig.json
└── yarn.lock
1 directory, 7 files

Few things to understand:

  • The auth.config.json manages the API authentication configuration

  • the functions/ folder hold our functions in charge of consuming, transforming, and remapping the API.

You should see that it looks like a standard Node.js project, with a package.json manifest where you can add custom libraries if needed.

Working with OAuth

Since we are using an OAuth type integration, there are three things we need to do.

First, create an OAuth App on the Github developer portal, next configure the authentication of the integration and finally generate an access token to use in local development.

Github OAuth App

In order to use Github's OAuth API, you will have to create an OAuth App on Github. This is required by Github and any OAuth provider.

If you don't know how to create a GitHub OAuth App, GitHub’s own guide will walk you through the process.

As part of the setup of the OAuth App on Github, you need to provide Bearer's authorization callback URL: https://int.bearer.sh/v2/auth/callback

Once the OAuth App is configured, Github will give you the necessary Client ID and Client Secret credentials.

Configuration

An auth.config.json config file should have been generate at the integration's root folder:

auth.config.json
{
"authorizationURL": "https://example.com/authorizationUrl",
"tokenURL": "https://example.com/tokenUrl",
"authType": "OAUTH2",
"tokenParams": {},
"authorizationParams": {},
"config": {
"scope": []
}
}

This holds the integration authentication configuration.

For Github's OAuth API, we need to update and change the authorizationURL, tokenURL , and config as shown below:

..
"authorizationURL": "https://github.com/login/oauth/authorize",
"tokenURL": "https://github.com/login/oauth/access_token",
..
"config": {
"accessType": "offline",
"scope": ["repo"]
}
..

Github's OAuth documentation can be found here if you necessary.

Access Token

To make working with the API simpler, Bearer's OAuth integrations rely on a static access token in local development environment.

Generating an access token can be very tricky, but Bearer's CLI makes it super simple..

First, use the setup command:

NPM
Yarn
npm run bearer setup:auth
yarn bearer setup:auth

Next, provide our OAuth credentials previously supplied by Github:

? Client ID: [hidden]
? Client secret: [hidden]

This should automatically open up our browser, trigger an OAuth dance and return a static access token:

The token will be automatically forwarded to your console and stored in a new local.config.jsoncfile:

$ cat local.config.jsonc
{
"setup": {
"auth": {
"accessToken": "a0facb69f0a047c9b9475xxxxxxxxxxxxxx"
}
}
}%

This access token will later be used by the event.context.auth.accessToken object in development, so you can easily build and test functions.

You are now ready to query the API.

Querying the API

Configure Client

In the functions/ folder, there is a client.ts file that provides a Client object to help you query the API. By default, it handles all the ~~~~necessary headers according to the chosen authentication mechanism:

import axios from 'axios'
export default function() {
const headers = {
'Accept': 'application/json',
'User-Agent': 'Bearer'
}
return axios.create({
baseURL: 'https://api.example.com/v1',
timeout: 5000,
headers
})
}

Replace the default baseURL value with Github's API base URL, as shown below:

baseURL: 'https://api.github.com/'

Functions

Now, it's time to query the API using the Client object. This is the role of functions.

To help you get started, use the built-in generator, in the console:

NPM
Yarn
npm run bearer generate:function listRepositories
yarn bearer generate:function listRepositories

Now, the function has been generated, and you can find it in the functions repositories:

functions/listRepositories.ts
import { TOAUTH2AuthContext, FetchData, TFetchActionEvent, TFetchPromise } from '@bearer/functions'
// Uncomment the line below to use the API Client
// import Client from './client'
export default class ListRepositoriesFunction extends FetchData implements FetchData<ReturnedData, any, TOAUTH2AuthContext> {
async action(event: TFetchActionEvent<Params, TOAUTH2AuthContext>): TFetchPromise<ReturnedData> {
// const token = event.context.auth.accessToken
// Put your logic here
return { data: [] }
}
// Uncomment the line below to restrict the function to be called only from a server-side context
// static serverSideRestricted = true
}
/**
* Typing
*/
export type Params = {
// name: string
}
export type ReturnedData = {
// foo: string[]
}

What is a Function?

A function represents one or multiple API calls, and helps you consume & transform APIs, which will in return be exposed as a standardized OpenAPI 3 compliant endpoint.

To start calling Github's API and fetch the list of public repositories, use the API Client provided by default, uncomment the import Client at line 3:

import Client from './client'

Now, you can start calling the API. Use the Github's public repositories endpoint and fetch the first 10 items while also filtering and remapping the data:

export default class ListRepositoriesFunction extends FetchData implements FetchData<ReturnedData, any, TOAUTH2AuthContext> {
async action(event: TFetchActionEvent<Params, TOAUTH2AuthContext>): TFetchPromise<ReturnedData> {
try {
const token = event.context.auth.accessToken
const { data } = await Client(token).get('user/repos')
const pullRequests = data.slice(0, 10).map(({ name, description, html_url }) => (
{
name,
description,
url: html_url
})
)
return {
data: pullRequests
}
} catch (error) {
return { error: error.toString() }
}
}
}

Types

Since we are coding in TypeScript, it's important to use Typing, making code more robust and secure. Additionally, Bearer use the Typing to automatically build the integration specification.

At the end of the generated function, we can find some empty Type definitions:

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

Since the function doesn't take any Params (entry arguments) we can leave the associated type empty. On the other hand, we define the Type of ReturnedData to match the output of the function.

Since our function returns Github's repositories name, description, and url, we define Types as:

export type ReturnedData = {
name: string
description: string
url: string
}

Here is a list of all the basic Types available in TypeScript.

Local Testing

To check if the function is working properly, we can use the invoke command:

NPM
Yarn
npm run bearer invoke listRepositories
yarn bearer invoke listRepositories
{
"data": [
{
"name": "Awesome Private Repo",
"description": "Coming soon...",
"url": "https://github.com/Bearer/PrivateRepo1"
},
{
"name": "Even More Awesome Repo",
"description": "Coming soon...",
"url": "https://github.com/Bearer/PrivateRepo2"
},
....

Et voilà! You should see the list of your Github repositories 🙌

Deploying

Now that the integration is ready (yes, already!), you can deploy it on Bearer's platform.

Push the code

First, log into your Bearer account (sign up for free here if you haven't already) from the console using the login command:

NPM
Yarn
npm run bearer login
yarn bearer login

Your browser will automatically open and ask you to login with your Bearer account, once it's done, you're magically logged-in to your account in the console:

Then, you a are ready to push your code:

NPM
Yarn
npm run bearer push
yarn bearer push
? Your integration isn't linked, what would you like to do? (Use arrow keys)
❯ Create a new integration
Select an integration from my list

Since it's the first time we deploy this integration, Bearer will offer to create a new one or link it to one already present on the dashboard.

Select Create a new integration and follow the process:

? Integration name: Github
? Description (optional): My first Integration on Github's API
Integration successfully created
name: Github
uuid: 1d1faf
Url: https://app.bearer.sh/integrations/1d1faf
Integration successfully linked! 🎉
✔ Generate bundle
✔ Transfer bundle
🐻 Integration successfully pushed.
Your Integration will be available shortly here: https://app.bearer.sh/integrations/1d1faf
In the meantime you can follow the deployment here: https://app.bearer.sh/integrations/1d1faf/logs

Your integration will now be deployed. You can see it on your dashboard, and follow the deployment process in its logs:

Integration Specification

Once the deployment is done, you can access the integration's specification using the Specifications tab:

As you can see, our functions have been automatically exposed and documented using the Open API 3 specification.

Integration UUID

Your integration will be referenced by its UUID. You can find this information in the Settings tab:

Here, the integration UUID is: 1d1faf

Setup API Credentials

In order to use the integration in production and let users connect with their Github account (no more static access token), you need to setup your OAuth credentials.

In the Settings tab of the integration on the dashboard, provide your Github OAuth Credentials, Client Id and Client Secret:

Once saved, a unique identifier is provided: setupId.

Bearer securely store your API credentials and hide them under an immutable identifier, setupId; this will be used by the Connect Component embedded on your app.

Your integration is now ready to be integrated in any App 🚀

Integrating into your App

Now open your app code.

Bearer provides clients to easily integrate into your app, from either a client-side (JavaScript, React) or server-side (Node.js, Ruby etc.) context - depending on your needs.

Connect Component

Since our integration relies on OAuth, we need our users to connect with their Github account. In exchange, we retrieve their access token which will be used to query the API and by extension call the functions.

First, install the integration client - choose your preferred technology:

JavaScript
React
// With a NPM
npm install @bearer/js
// Without NPM
<script src="https://cdn.jsdelivr.net/npm/@bearer/js/lib/bearer.production.min.js"></script>
npm install @bearer/react

Next, initialize it:

JavaScript
React
// With NPM
import bearer from '@bearer/js'
// Without NPM
const bearerClient = bearer('BEARER_CLIENT_ID')
import * as React from 'react'
import { Bearer } from "@bearer/react"
const App = () =>
<Bearer clientId="BEARER_CLIENT_ID">
<MyComponent />
</Bearer>

Replace the BEARER_CLIENT_ID with your Client ID, which can be found on your Developer Keys page.

Finally, create a "Connect" Component that will trigger the OAuth flow:

JavaScript
React.js
bearerClient.connect(
'INTEGRATION_UUID',
'YOUR_SETUP_ID',
{ authId: 'YOUR_USER_IDENTIFIER' }
)
import * as React from "react"
import { factory } from "@bearer/react"
import { BearerContext } from "@bearer/react/lib/bearer-provider"
const { Connect } = factory(INTEGRATION_UUID);
const MyComponent = () => {
const [authId, setAuthId] = React.useState();
return (
<Connect authId={YOUR_USER_IDENTIFIER} setupId={YOUR_SETUP_ID}
onSuccess={data => {
setAuthId(data.authId);
}}
render={({ connect }) => {
return (
<button onClick={connect}>Connect</button>
)
}}
/>
)
}

Replace YOUR_SETUP_ID with the Setup Identifier from earlier.

Replace YOUR_USER_IDENTIFIER with your app current user identifier. Bearer securely stores your user access token by referencing it to your app identifier. This way your app doesn't need to store anything new.

Security first! Make sure to use an unguessable user identifier, such as a UUID. Don’t let malicious users call your function using someone else’s identity.

If you don't provide an authId value, Bearer will automatically generate one for you and will return it as a JavaScript event. You will have to store it somewhere.

Calling the Function

Now that we can retrieve the user's access token, we are ready to call the functions.

First, install the integration client - if different than previously:

JavaScript
React
Node.js
Ruby
PHP
// With a NPM
npm install @bearer/js
// Without NPM
<script src="https://cdn.jsdelivr.net/npm/@bearer/js/lib/bearer.production.min.js"></script>
npm install @bearer/react
npm install @bearer/node
gem install bearer
composer require bearer/bearer-php

Next, initialize it:

JavaScript
React
Node.js
Ruby
PHP
// With NPM
import bearer from '@bearer/js'
// Without NPM
const bearerClient = bearer('BEARER_CLIENT_ID')
import * as React from 'react'
import { Bearer } from "@bearer/react"
const App = () =>
<Bearer clientId="BEARER_CLIENT_ID">
<MyComponent />
</Bearer>
import bearer from '@bearer/node'
const bearerClient = bearer(process.env.BEARER_API_KEY)
Bearer::Configuration.setup do |config|
config.api_key = BEARER_API_KEY
end
$bearer = new Bearer\Client(BEARER_API_KEY);

Replace the BEARER_CLIENT_ID or BEARER_API_KEY with your API Key and Client ID that can be found on your Developer Keys page.

Finally, call the defaultFunction of the integration to fetch the Github Data:

JavaScript
React
Node.js
Ruby
PHP
bearerClient.invoke('INTEGRATION_UUID', 'listRepositories', {
query: { authId: 'YOUR_USER_IDENTIFIER' }
})
.then(() => {
console.log('Successfully invoked function')
})
.catch(() => {
console.log('Something went wrong')
})
import * as React from "react"
import { BearerContext } from "@bearer/react/lib/bearer-provider"
const MyComponent = () => {
const context = useContext(BearerContext);
const handleClick = (e: any) => {
context.bearer
.invoke("INTEGRATION_UUID", "listRepositories", { query: { authId: "YOUR_USER_IDENTIFIER" } })
.then(data => { console.log(data) })
.catch(console.error);
}
return (
<button type="button" onClick={this.handleClick}>Click Me</button>
)
}
bearerClient.invoke('INTEGRATION_UUID', 'listRepositories', {
query: { authId: 'YOUR_USER_IDENTIFIER' }
})
.then(() => {
console.log('Successfully invoked function')
})
.catch(() => {
console.log('Something went wrong')
})
Bearer.invoke("INTEGRATION_UUID", "listRepositories", params: { authId: "YOUR_USER_IDENTIFIER" }
$bearer
->integration(INTEGRATION_UUID)
->invoke('listRepositories', ["query" => ["since" => "364"]]);

Replace the INTEGRATION_UUID with the integration UUID available in the integration Settings tab.

Replace the YOUR_USER_IDENTIFIER with your app current user identifier.

Your users can now log in with their GitHub Account. And you can retrieve repositories and filter them! Last but not least, the app is totally shielded from the integration glue code 🎉

Sandbox vs Production

As you may have noticed, our integration is in sandbox mode by default. This means it is limited to 2000 API calls per month - so it's mainly for testing purposes.

Once the integration is ready to be used in a production environment, we just need to toggle the production mode on, and the limits will be removed.