content/shared/influxdb-v2/api-guide/tutorials/nodejs.md
{{% api/iot-starter-intro %}}
If you haven't already, create an InfluxDB Cloud account or install InfluxDB OSS.
The IoT Starter example app assumes the following prerequisites:
iot_center for storing time series data from devicesiot_center_devices for storing device metadata and API token IDs{{% note %}}
For a production application, create and use a {{% show-in "cloud,cloud-serverless" %}}custom{{% /show-in %}}{{% show-in "v2" %}}read-write{{% /show-in %}} token with minimal permissions and only use it with a single client or application.
{{% /note %}}
The application architecture has four layers:
{{% note %}} For the complete code referenced in this tutorial, see the influxdata/iot-api-js repository. {{% /note %}}
If you haven't already installed yarn, follow the Yarn package manager installation instructions for your version of Node.js.
To check the installed yarn version, enter the following code into your terminal:
yarn --version
Create a directory that will contain your iot-api projects.
The following example code creates an iot-api directory in your home directory
and changes to the new directory:
mkdir ~/iot-api-apps
cd ~/iot-api-apps
Follow these steps to create a JavaScript application with Next.js:
In your ~/iot-api-apps directory, open a terminal and enter the following commands to create the iot-api-js app from the NextJS learn-starter template:
yarn create-next-app iot-api-js --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
After the installation completes, enter the following commands in your terminal to go into your ./iot-api-js directory and start the development server:
cd iot-api-js
yarn dev -p 3001
To view the application, visit http://localhost:3001 in your browser.
The InfluxDB client library provides the following InfluxDB API interactions:
Enter the following command into your terminal to install the client library:
yarn add @influxdata/influxdb-client
Enter the following command into your terminal to install @influxdata/influxdb-client-apis, the management APIs that create, modify, and delete authorizations, buckets, tasks, and other InfluxDB resources:
yarn add @influxdata/influxdb-client-apis
For more information about the client library, see the influxdata/influxdb-client-js repo.
InfluxDB client libraries require configuration properties from your InfluxDB environment. Typically, you'll provide the following properties as environment variables for your application:
INFLUX_URLINFLUX_TOKENINFLUX_ORGINFLUX_BUCKETINFLUX_BUCKET_AUTHNext.js uses the env module to provide environment variables to your application.
The ./.env.development file is versioned and contains non-secret default settings for your development environment.
# .env.development
INFLUX_URL=http://localhost:8086
INFLUX_BUCKET=iot_center
INFLUX_BUCKET_AUTH=iot_center_devices
To configure secrets and settings that aren't added to version control,
create a ./.env.local file and set the variables--for example, set your InfluxDB token and organization:
# .env.local
# INFLUX_TOKEN
# InfluxDB API token used by the application server to send requests to InfluxDB.
# For convenience in development, use an **All Access** token.
INFLUX_TOKEN=29Xx1KH9VkASPR2DSfRfFd82OwGD...
# INFLUX_ORG
# InfluxDB organization ID you want to use in development.
INFLUX_ORG=48c88459ee424a04
Enter the following commands into your terminal to restart and load the .env files:
CONTROL+C to stop the application.yarn dev to start the application.Next.js sets variables that you can access in the process.env object--for example:
console.log(process.env.INFLUX_ORG)
Your application API provides server-side HTTP endpoints that process requests from the UI. Each API endpoint is responsible for the following:
Add the /api/devices API endpoint that retrieves, processes, and lists devices.
/api/devices uses the /api/v2/query InfluxDB API endpoint to query INFLUX_BUCKET_AUTH for a registered device.
Create a ./pages/api/devices/[[...deviceParams]].js file to handle requests for /api/devices and /api/devices/<deviceId>/measurements/.
In the file, export a Next.js request handler function.
See the example.
{{% note %}}
In Next.js, the filename pattern [[...param]].js creates a catch-all API route.
To learn more, see Next.js dynamic API routes.
{{% /note %}}
Retrieve registered devices in INFLUX_BUCKET_AUTH and process the query results.
Create a Flux query that gets the last row of each series that contains a deviceauth measurement.
The example query below returns rows that contain the key field (authorization ID) and excludes rows that contain a token field (to avoid exposing tokens to the UI).
// Flux query finds devices
from(bucket:`${INFLUX_BUCKET_AUTH}`)
|> range(start: 0)
|> filter(fn: (r) => r._measurement == "deviceauth" and r._field != "token")
|> last()
Use the QueryApi client to send the Flux query to the POST /api/v2/query InfluxDB API endpoint.
Create a ./pages/api/devices/_devices.js file that contains the following:
{{< code-tabs-wrapper >}} {{% code-tabs %}} Node.js {{% /code-tabs %}} {{% code-tab-content %}}
{{% truncate %}}
import { InfluxDB } from '@influxdata/influxdb-client'
import { flux } from '@influxdata/influxdb-client'
const INFLUX_ORG = process.env.INFLUX_ORG
const INFLUX_BUCKET_AUTH = process.env.INFLUX_BUCKET_AUTH
const influxdb = new InfluxDB({url: process.env.INFLUX_URL, token: process.env.INFLUX_TOKEN})
/**
* Gets devices or a particular device when deviceId is specified. Tokens
* are not returned unless deviceId is specified. It can also return devices
* with empty/unknown key, such devices can be ignored (InfluxDB authorization is not associated).
* @param deviceId optional deviceId
* @returns promise with an Record<deviceId, {deviceId, createdAt, updatedAt, key, token}>.
*/
export async function getDevices(deviceId) {
const queryApi = influxdb.getQueryApi(INFLUX_ORG)
const deviceFilter =
deviceId !== undefined
? flux` and r.deviceId == "${deviceId}"`
: flux` and r._field != "token"`
const fluxQuery = flux`from(bucket:${INFLUX_BUCKET_AUTH})
|> range(start: 0)
|> filter(fn: (r) => r._measurement == "deviceauth"${deviceFilter})
|> last()`
const devices = {}
return await new Promise((resolve, reject) => {
queryApi.queryRows(fluxQuery, {
next(row, tableMeta) {
const o = tableMeta.toObject(row)
const deviceId = o.deviceId
if (!deviceId) {
return
}
const device = devices[deviceId] || (devices[deviceId] = {deviceId})
device[o._field] = o._value
if (!device.updatedAt || device.updatedAt < o._time) {
device.updatedAt = o._time
}
},
error: reject,
complete() {
resolve(devices)
},
})
})
}
{{% /truncate %}}
{{% caption %}}iot-api-js/pages/api/devices/_devices.js getDevices(deviceId){{% /caption %}}
{{% /code-tab-content %}} {{< /code-tabs-wrapper >}}
The _devices module exports a getDevices(deviceId) function that queries
for registered devices, processes the data, and returns a Promise with the result.
If you invoke the function as getDevices() (without a deviceId),
it retrieves all deviceauth points and returns a Promise with { DEVICE_ID: ROW_DATA }.
To send the query and process results, the getDevices(deviceId) function uses the QueryAPI queryRows(query, consumer) method.
queryRows executes the query and provides the Annotated CSV result as an Observable to the consumer.
queryRows has the following TypeScript signature:
queryRows(
query: string | ParameterizedQuery,
consumer: FluxResultObserver<string[]>
): void
{{% caption %}}@influxdata/influxdb-client-js QueryAPI{{% /caption %}}
The consumer that you provide must implement the FluxResultObserver interface and provide the following callback functions:
next(row, tableMeta): processes the next row and table metadata--for example, to prepare the response.error(error): receives and handles errors--for example, by rejecting the Promise.complete(): signals when all rows have been consumed--for example, by resolving the Promise.To learn more about Observers, see the RxJS Guide.
In this application, a registered device is a point that contains your device ID, authorization ID, and API token.
The API token and authorization permissions allow the device to query and write to INFLUX_BUCKET.
In this section, you add the API endpoint that handles requests from the UI, creates an authorization in InfluxDB,
and writes the registered device to the INFLUX_BUCKET_AUTH bucket.
To learn more about API tokens and authorizations, see Manage API tokens
The application API uses the following /api/v2 InfluxDB API endpoints:
POST /api/v2/query: to query INFLUX_BUCKET_AUTH for a registered device.GET /api/v2/buckets: to get the bucket ID for INFLUX_BUCKET.POST /api/v2/authorizations: to create an authorization for the device.POST /api/v2/write: to write the device authorization to INFLUX_BUCKET_AUTH.Add a ./pages/api/devices/create.js file to handle requests for /api/devices/create.
In the file, export a Next.js request handler function that does the following:
INFLUX_BUCKET_AUTH and respond with an error if an authorization exists for the device.INFLUX_BUCKET_AUTH.HTTP 200 when the write request completes.In this section, you create an authorization with read-write permission to INFLUX_BUCKET and receive an API token for the device.
The example below uses the following steps to create the authorization:
AuthorizationsAPI client and BucketsAPI client with the configuration.POST request to the /api/v2/authorizations InfluxDB API endpoint.In ./api/devices/create.js, add the following createAuthorization(deviceId) function:
{{< code-tabs-wrapper >}} {{% code-tabs %}} Node.js {{% /code-tabs %}} {{% code-tab-content %}}
{{% truncate %}}
import { InfluxDB } from '@influxdata/influxdb-client'
import { getDevices } from './_devices'
import { AuthorizationsAPI, BucketsAPI } from '@influxdata/influxdb-client-apis'
import { Point } from '@influxdata/influxdb-client'
const INFLUX_ORG = process.env.INFLUX_ORG
const INFLUX_BUCKET_AUTH = process.env.INFLUX_BUCKET_AUTH
const INFLUX_BUCKET = process.env.INFLUX_BUCKET
const influxdb = new InfluxDB({url: process.env.INFLUX_URL, token: process.env.INFLUX_TOKEN})
/**
* Creates an authorization for a supplied deviceId
* @param {string} deviceId client identifier
* @returns {import('@influxdata/influxdb-client-apis').Authorization} promise with authorization or an error
*/
async function createAuthorization(deviceId) {
const authorizationsAPI = new AuthorizationsAPI(influxdb)
const bucketsAPI = new BucketsAPI(influxdb)
const DESC_PREFIX = 'IoTCenterDevice: '
const buckets = await bucketsAPI.getBuckets({name: INFLUX_BUCKET, orgID: INFLUX_ORG})
const bucketId = buckets.buckets[0]?.id
return await authorizationsAPI.postAuthorizations(
{
body: {
orgID: INFLUX_ORG,
description: DESC_PREFIX + deviceId,
permissions: [
{
action: 'read',
resource: {type: 'buckets', id: bucketId, orgID: INFLUX_ORG},
},
{
action: 'write',
resource: {type: 'buckets', id: bucketId, orgID: INFLUX_ORG},
},
],
},
}
)
}
{{% /truncate %}} {{% caption %}}iot-api-js/pages/api/devices/create.js{{% /caption %}}
{{% /code-tab-content %}} {{< /code-tabs-wrapper >}}
To create an authorization that has read-write permission to INFLUX_BUCKET, you need the bucket ID.
To retrieve the bucket ID,
createAuthorization(deviceId) calls the BucketsAPI getBuckets function that sends a GET request to
the /api/v2/buckets InfluxDB API endpoint.
createAuthorization(deviceId) then passes a new authorization in the request body with the following:
IoTCenterDevice: DEVICE_ID.To learn more about API tokens and authorizations, see Manage API tokens.
Next, write the device authorization to a bucket.
With a device authorization in InfluxDB, write a point for the device and authorization details to INFLUX_BUCKET_AUTH.
Storing the device authorization in a bucket allows you to do the following:
To write a point to InfluxDB, use the InfluxDB client library to send a POST request to the /api/v2/write InfluxDB API endpoint.
In ./pages/api/devices/create.js, add the following createDevice(deviceId) function:
{{< code-tabs-wrapper >}} {{% code-tabs %}} Node.js {{% /code-tabs %}} {{% code-tab-content %}}
/** Creates an authorization for a deviceId and writes it to a bucket */
async function createDevice(deviceId) {
let device = (await getDevices(deviceId)) || {}
let authorizationValid = !!Object.values(device)[0]?.key
if(authorizationValid) {
console.log(JSON.stringify(device))
return Promise.reject('This device ID is already registered and has an authorization.')
} else {
console.log(`createDeviceAuthorization: deviceId=${deviceId}`)
const authorization = await createAuthorization(deviceId)
const writeApi = influxdb.getWriteApi(INFLUX_ORG, INFLUX_BUCKET_AUTH, 'ms', {
batchSize: 2,
})
const point = new Point('deviceauth')
.tag('deviceId', deviceId)
.stringField('key', authorization.id)
.stringField('token', authorization.token)
writeApi.writePoint(point)
await writeApi.close()
return
}
}
{{% caption %}}iot-api-js/pages/api/devices/create.js{{% /caption %}}
{{% /code-tab-content %}} {{< /code-tabs-wrapper >}}
createDevice(device_id) takes a device_id and writes data to INFLUX_BUCKET_AUTH in the following steps:
InfluxDBClient() with url, token, and org values from the configuration.WriteAPI client for writing data to an InfluxDB bucket.Point.writeApi.writePoint(point) to write the Point to the bucket.The function writes a point with the following elements:
| Element | Name | Value |
|---|---|---|
| measurement | deviceauth | |
| tag | deviceId | device ID |
| field | key | authorization ID |
| field | token | authorization (API) token |
influxdata/iot-api-ui is a standalone Next.js React UI that uses your application API to write and query data in InfluxDB.
iot-api-ui uses Next.js rewrites to route all requests in the /api/ path to your API.
To install and run the UI, do the following:
In your ~/iot-api-apps directory, clone the influxdata/iot-api-ui repo and go into the iot-api-ui directory--for example:
cd ~/iot-api-apps
git clone [email protected]:influxdata/iot-api-ui.git
cd ./iot-app-ui
The ./.env.development file contains default configuration settings that you can
edit or override (with a ./.env.local file).
To start the UI, enter the following command into your terminal:
yarn dev
To view the list and register devices, visit http://localhost:3000/devices in your browser.
To learn more about the UI components, see influxdata/iot-api-ui.