Skip to content

Commit

Permalink
Add node documentation (#389)
Browse files Browse the repository at this point in the history
  • Loading branch information
njooma authored Oct 14, 2024
1 parent 1f8e7f4 commit 59b2ba3
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 55 deletions.
102 changes: 102 additions & 0 deletions Node.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Node & Viam's TypeScript SDK

This document contains detailed instructions on including Viam in your Node project. For a runnable example, see the [examples directory](/examples/node/).

## Requirements

This document assumes you already have Node installed. If not, follow the [instructions](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs) provided by Node.

### Dependencies

In addition to the Viam SDK, the following direct dependencies are requires:

- `@connectrpc/connect-node`
- `node-datachannel`

You can use either Yarn or NPM to install the dependencies. This document will use NPM, but either will work.

`npm install @viamrobotics/sdk @connectrpc/connect-node node-datachannel`

### Polyfills

Using the SDK with Node also requires the use of some polyfills. In your application's entrypoint (`main.ts`, `index.ts`, or something similar), you will need to register those polyfills:

```ts
// main.ts

import wrtc = require('node-datachannel/polyfill');
for (const key in wrtc) {
(global as any)[key] = (wrtc as any)[key];
}
```

### Transport

Communicating with your Viam machine in Node requires the use of a custom transport. In your app's entrypoint, you will also need to register the custom transport:

```ts
// main.ts

import connectNode = require('@connectrpc/connect-node');
globalThis.VIAM = {
GRPC_TRANSPORT_FACTORY: (opts: any) =>
connectNode.createGrpcTransport({ httpVersion: '2', ...opts }),
};
```

## Using the SDK

To use the SDK, you can use similar instructions to those found on the [documentation site](https://docs.viam.com/build/program/). Below is an example of how you could use the SDK to display a list of resources on the connected device:

```ts
// main.ts

import VIAM = require('@viamrobotics/sdk');
import wrtc = require('node-datachannel/polyfill');
import connectNode = require('@connectrpc/connect-node');
globalThis.VIAM = {
GRPC_TRANSPORT_FACTORY: (opts: any) =>
connectNode.createGrpcTransport({ httpVersion: '2', ...opts }),
};
for (const key in wrtc) {
(global as any)[key] = (wrtc as any)[key];
}

async function connect() {
const host = process.env.HOST;
const apiKeyId = process.env.API_KEY_ID;
const apiKeySecret = process.env.API_KEY_SECRET;
if (!host) {
throw new Error('must set HOST env var');
}
if (!apiKeyId) {
throw new Error('must set API_KEY_ID env var');
}
if (!apiKeySecret) {
throw new Error('must set API_KEY_SECRET env var');
}

const client = await VIAM.createRobotClient({
host,
credentials: {
type: 'api-key',
authEntity: apiKeyId,
payload: apiKeySecret,
},
signalingAddress: 'https://app.viam.com:443',
iceServers: [{ urls: 'stun:global.stun.twilio.com:3478' }],
});

console.log(await client.resourceNames());
}

connect().catch((e) => {
console.error('error connecting to machine', e);
});
```

In the above example, it is assumed that certain environment variables are set (`HOST`, `API_KEY_ID`, and `API_KEY_SECRET`). You can set those in the process or have a `.env` file set them automatically. If you use a `.env.` file, be sure to exclude it from version control.

In your terminal, you can run:

`npx tsc && node --env-file=.env main.js`
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Build and connect to robots with TypeScript

> **Warning**
> This is a beta release of the Viam TypeScript SDK. Stability is not guaranteed. Breaking changes are likely to occur, and occur often.
> TS SDK only works in a browser environment.
## Usage

Expand All @@ -28,12 +27,16 @@ npm install @viamrobotics/sdk

[https://ts.viam.dev](https://ts.viam.dev/)

## Node

To use this SDK with node, read the [documentation](/Node.md) or view the [example](/examples/node/)

## React Native

To use this SDK with React Native, read the [documentation](/ReactNative.md) or view the [example](/examples/react-native/)

## License

Copyright 2022-2023 Viam Inc.
Copyright 2022-2024 Viam Inc.

Apache 2.0 - See [LICENSE](https://github.com/viamrobotics/viam-typescript-sdk/blob/main/LICENSE) file
153 changes: 103 additions & 50 deletions ReactNative.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# React Native & Viam's TypeScript SDK

Viam's React Native support is still experimental. Therefore, we've provided this document for with detailed instructions on including Viam in your React Native project.
This document contains detailed instructions on including Viam in your React Native project. For an runnable example, see the [examples directory](/examples/react-native/).

## Requirements

Expand All @@ -11,63 +11,102 @@ This document assumes you already have a React Native project. If not, follow th
### Dependencies

You must use the latest version of Viam's TypeScript SDK, `>=0.11.0`, alongside a few other direct dependencies: `react-native-webrtc`, `react-native-url-polyfill`, and `@improbable-eng/grpc-web-react-native-transport`.
You must use the latest version of Viam's TypeScript SDK, `>=0.26.1`, alongside a few other direct dependencies:

You can use either Yarn or NPM to install the dependencies. This document will use NPM, but either will work.
- `fast-text-encoding`
- `react-native-fast-encoder`
- `react-native-fetch-api`
- `react-native-url-polyfill`
- `react-native-webrtc`
- `web-streams-polyfill`

`npm install @viamrobotics/sdk react-native-webrtc react-native-url-polyfill @improbable-eng/grpc-web-react-native-transport`
You can use either Yarn or NPM to install the dependencies. This document will use NPM, but either will work.

### Configuration
`npm install @viamrobotics/sdk fast-text-encoding react-native-fast-encoder react-native-fetch-api react-native-url-polyfill react-native-webrtc web-streams-polyfill`

#### `index.js`
### Polyfills

You will also have to update your `index.js`. These updates should be placed above all other imports or customizations.
Using the SDK with React Native also requires a number of polyfills. You can find these at [polyfills.native.ts](/examples/react-native/polyfills.native.ts) and [polyfills.ts](/examples/react-native/polyfills.ts). They are also pasted in their entirety below. You can copy these directly into your application.

Firstly, you will have to import the URL polyfill:
```ts
// polyfills.native.ts

```js
// Dervied from https://raw.githubusercontent.com/connectrpc/examples-es/refs/heads/main/react-native
import TextEncoder from 'react-native-fast-encoder';
// @ts-expect-error -- missing type declarations
import { polyfillGlobal } from 'react-native/Libraries/Utilities/PolyfillFunctions';
// @ts-expect-error -- missing type declarations
import { fetch, Headers, Request, Response } from 'react-native-fetch-api';
import 'react-native-url-polyfill/auto';
import { registerGlobals } from 'react-native-webrtc';
import { ReadableStream } from 'web-streams-polyfill';

export function polyfills() {
polyfillGlobal('TextDecoder', () => TextEncoder);
polyfillGlobal('TextEncoder', () => TextEncoder);
registerGlobals();
polyfillGlobal('ReadableStream', () => ReadableStream);
polyfillGlobal(
'fetch',
() =>
(...args: Parameters<typeof window.fetch>) =>
fetch(args[0], {
...args[1],
// Inject textStreaming: https://github.com/react-native-community/fetch/issues/15
reactNative: { textStreaming: true },
})
);
polyfillGlobal('Headers', () => Headers);
polyfillGlobal('Request', () => Request);
polyfillGlobal('Response', () => Response);
// Polyfill async.Iterator. For some reason, the Babel presets and plugins are not doing the trick.
// Code from here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html#caveats
(Symbol as any).asyncIterator =
Symbol.asyncIterator || Symbol.for('Symbol.asyncIterator');
}
```

Then, you have to register the React Native WebRTC globals:
```ts
// polyfills.ts

```js
import { registerGlobals } from 'react-native-webrtc';
registerGlobals();
// From https://raw.githubusercontent.com/connectrpc/examples-es/refs/heads/main/react-native
// No polyfills needed for web
export function polyfills() {}
```

Finally, you will have to update add the GRPC connection configuration:
### Transport

```js
import { ReactNativeTransport } from '@improbable-eng/grpc-web-react-native-transport';
global.VIAM = {
GRPC_TRANSPORT_FACTORY: ReactNativeTransport,
};
```
Communicating with your Viam machine in React Native requires the use of a custom transport. You can find it in the examples directory at [transport.ts](/examples/react-native/transport.ts). You can copy that file as is and put it in your project's root directory (sibling to the `polyfill` files).

Your final `index.js` might look something like this:
## Configuration

```js
/** @format */
### `App.tsx`

import 'react-native-url-polyfill/auto';
You will also have to update your `App.tsx` to import and install the `polyfills` and update the Viam transport factory.

import { registerGlobals } from 'react-native-webrtc';
registerGlobals();
```tsx
// App.tsx

import { ReactNativeTransport } from '@improbable-eng/grpc-web-react-native-transport';
global.VIAM = {
GRPC_TRANSPORT_FACTORY: ReactNativeTransport,
};
// React imports here
// e.g.
// import React, { useState } from 'react';

// ADD THE FOLLOWING LINES
import * as VIAM from '@viamrobotics/sdk';
import { polyfills } from './polyfills';
polyfills();

import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';
import { GrpcWebTransportOptions } from '@connectrpc/connect-web';
import { createXHRGrpcWebTransport } from './transport';

AppRegistry.registerComponent(appName, () => App);
globalThis.VIAM = {
GRPC_TRANSPORT_FACTORY: (opts: GrpcWebTransportOptions) => {
return createXHRGrpcWebTransport(opts);
},
};
```

#### `metro.config.js`
### `metro.config.js`

In addition, your `metro.config.js` file needs to be updated as well. `react-native` and `react-native-webrtc` require conflicting versions of the library `event-target-shim`. Because of that, we need to tell the Metro Bundler to package this library properly. The following is a full example of what the `metro.config.js` file could look like. If you have made any changes yourself to the bundler, yours will look different.

Expand Down Expand Up @@ -127,8 +166,7 @@ To use the SDK, you can use similar instructions to those found on the [document
```tsx
// App.tsx

import React, { PropsWithoutRef } from 'react';
import { useState } from 'react';
import React, { PropsWithoutRef, useState } from 'react';
import {
Button,
FlatList,
Expand All @@ -140,6 +178,17 @@ import {
} from 'react-native';

import * as VIAM from '@viamrobotics/sdk';
import { polyfills } from './polyfills';
polyfills();

import { GrpcWebTransportOptions } from '@connectrpc/connect-web';
import { createXHRGrpcWebTransport } from './transport';

globalThis.VIAM = {
GRPC_TRANSPORT_FACTORY: (opts: GrpcWebTransportOptions) => {
return createXHRGrpcWebTransport(opts);
},
};

type ResourceNameViewProps = PropsWithoutRef<{
resourceName: VIAM.ResourceName;
Expand All @@ -163,19 +212,23 @@ function App(): React.JSX.Element {
const [resourceNames, setResourceNames] = useState<VIAM.ResourceName[]>([]);

async function connect() {
const host = 'YOUR_HOST';
const client = await VIAM.createRobotClient({
host,
credential: {
type: 'api-key',
payload: 'YOUR_API_KEY',
},
authEntity: 'YOUR_API_KEY_ID',
signalingAddress: 'https://app.viam.com:443',
});
setConnected(true);
const rns = await client.resourceNames();
setResourceNames(rns.sort((a, b) => (a.name < b.name ? -1 : 1)));
const host = 'test4-main.hrsdzs2gp3.viam.cloud';
try {
const client = await VIAM.createRobotClient({
host,
credentials: {
type: 'api-key',
authEntity: '2f862d8c-7824-4f1f-aca1-0a9fab38506a',
payload: '4ft2ch1zdxsjyj5trn4ppq4rk7crj2jc',
},
signalingAddress: 'https://app.viam.com:443',
});
setConnected(true);
const rns = await client.resourceNames();
setResourceNames(rns.sort((a, b) => (a.name < b.name ? -1 : 1)));
} catch (error) {
console.error(error);
}
}

return (
Expand Down
6 changes: 3 additions & 3 deletions examples/react-native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The following direct dependencies are required:
- react-native-webrtc
- web-streams-polyfill
In addition, polyfills and a custom gRPC Transport are provided at `polyfills.[native].ts` and `transport.ts` respectively.
In addition, polyfills and a custom gRPC Transport are provided at `polyfills.native.ts` and `transport.ts` respectively.
#### `App.tsx`
Expand All @@ -44,12 +44,12 @@ The `App.tsx` file was updated to include the following polyfills and updates:
- Polyfills:
```js
import { polyfills } from "./polyfills";
import {polyfills} from './polyfills';
polyfills();
```
- GRPC connection configuration
```js
import { GrpcWebTransportOptions } from "@connectrpc/connect-web";
import { createXHRGrpcWebTransport } from './transport';
Expand Down

0 comments on commit 59b2ba3

Please sign in to comment.