diff --git a/.gitignore b/.gitignore
index 550b62f43..30ad3184b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ node_modules
/content/influxdb/*/api/*.html
/api-docs/redoc-static.html*
.vscode/*
+.idea
+package-lock.json
\ No newline at end of file
diff --git a/content/influxdb/cloud/api-guide/tutorials/_index.md b/content/influxdb/cloud/api-guide/tutorials/_index.md
new file mode 100644
index 000000000..5e09b82ad
--- /dev/null
+++ b/content/influxdb/cloud/api-guide/tutorials/_index.md
@@ -0,0 +1,13 @@
+---
+title: InfluxDB API client library tutorials
+seotitle: Get started with InfluxDB API client libraries
+description: Follow step-by-step tutorials to for InfluxDB API client libraries in your favorite framework or language.
+weight: 4
+menu:
+ influxdb_cloud:
+ name: Client library tutorials
+ parent: Develop with the API
+influxdb/cloud/tags: [api]
+---
+
+{{< children >}}
diff --git a/content/influxdb/cloud/api-guide/tutorials/client-library-starter/_index.md b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/_index.md
new file mode 100644
index 000000000..ff3308178
--- /dev/null
+++ b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/_index.md
@@ -0,0 +1,13 @@
+---
+title: InfluxDB API client library starter
+seotitle: Starter tutorial for InfluxDB API client libraries
+description: Follow step-by-step tutorials to build an IoT dashboard with API client libraries in your favorite framework or language.
+weight: 4
+menu:
+ influxdb_cloud:
+ name: Client library starter
+ parent: Client library tutorials
+influxdb/cloud/tags: [api]
+---
+
+{{% duplicate-oss %}}
diff --git a/content/influxdb/cloud/api-guide/tutorials/client-library-starter/nodejs.md b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/nodejs.md
new file mode 100644
index 000000000..89881eb9d
--- /dev/null
+++ b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/nodejs.md
@@ -0,0 +1,16 @@
+---
+title: JavaScript client library starter
+seotitle: Use JavaScript client library to build a sample application
+list_title: JavaScript client library starter
+description: >
+ Build a JavaScript application that writes, queries, and manages devices with the
+ InfluxDB client library.
+menu:
+ influxdb_cloud:
+ identifier: client-library-starter-js
+ name: JavaScript
+ parent: Client library starter
+influxdb/cloud/tags: [api, javascript, nodejs]
+---
+
+{{% duplicate-oss %}}
diff --git a/content/influxdb/cloud/api-guide/tutorials/client-library-starter/python.md b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/python.md
new file mode 100644
index 000000000..683360a54
--- /dev/null
+++ b/content/influxdb/cloud/api-guide/tutorials/client-library-starter/python.md
@@ -0,0 +1,17 @@
+---
+title: Python client library starter
+seotitle: Use Python client library to build a sample application
+list_title: Python
+description: >
+ Build an application that writes, queries, and manages devices with the InfluxDB
+ client library for Python.
+weight: 3
+menu:
+ influxdb_cloud:
+ identifier: client-library-starter-py
+ name: Python
+ parent: Client library starter
+influxdb/cloud/tags: [api, python]
+---
+
+{{% duplicate-oss %}}
diff --git a/content/influxdb/v2.2/api-guide/_index.md b/content/influxdb/v2.2/api-guide/_index.md
index 4aa736384..47e1d394e 100644
--- a/content/influxdb/v2.2/api-guide/_index.md
+++ b/content/influxdb/v2.2/api-guide/_index.md
@@ -12,20 +12,28 @@ influxdb/v2.2/tags: [api]
The InfluxDB v2 API provides a programmatic interface for interactions with InfluxDB.
Access the InfluxDB API using the `/api/v2/` endpoint.
+## Developer guides
+
+- [API starter guide](/influxdb/v2.2/api-guide/starter/)
+
## InfluxDB client libraries
+
InfluxDB client libraries are language-specific packages that integrate with the InfluxDB v2 API.
-For information about supported client libraries, see [InfluxDB client libraries](/{{< latest "influxdb" >}}/api-guide/client-libraries/).
+For tutorials and information about client libraries, see [InfluxDB client libraries](/{{< latest "influxdb" >}}/api-guide/client-libraries/).
## InfluxDB v2 API documentation
+
InfluxDB OSS {{< current-version >}} API documentation
-#### View InfluxDB API documentation locally
+### View InfluxDB API documentation locally
+
InfluxDB API documentation is built into the `influxd` service and represents
the API specific to the current version of InfluxDB.
To view the API documentation locally, [start InfluxDB](/influxdb/v2.2/get-started/#start-influxdb)
and visit the `/docs` endpoint in a browser ([localhost:8086/docs](http://localhost:8086/docs)).
## InfluxDB v1 compatibility API documentation
+
The InfluxDB v2 API includes [InfluxDB 1.x compatibility endpoints](/influxdb/v2.2/reference/api/influxdb-1x/)
that work with InfluxDB 1.x client libraries and third-party integrations like
[Grafana](https://grafana.com) and others.
diff --git a/content/influxdb/v2.2/api-guide/api_intro.md b/content/influxdb/v2.2/api-guide/api_intro.md
index 3d59e4382..acef91942 100644
--- a/content/influxdb/v2.2/api-guide/api_intro.md
+++ b/content/influxdb/v2.2/api-guide/api_intro.md
@@ -1,11 +1,11 @@
---
title: API Quick Start
seotitle: Use the InfluxDB API
-description: Interact with InfluxDB 1 using a rich API for writing and querying data and more.
+description: Interact with InfluxDB using a rich API for writing and querying data and more.
weight: 3
menu:
influxdb_2_2:
- name: Quick Start
+ name: Quick start
parent: Develop with the API
aliases:
- /influxdb/v2.2/tools/api/
diff --git a/content/influxdb/v2.2/api-guide/tutorials/_index.md b/content/influxdb/v2.2/api-guide/tutorials/_index.md
new file mode 100644
index 000000000..55856b4a7
--- /dev/null
+++ b/content/influxdb/v2.2/api-guide/tutorials/_index.md
@@ -0,0 +1,13 @@
+---
+title: InfluxDB API client library tutorials
+seotitle: Get started with InfluxDB API client libraries
+description: Follow step-by-step tutorials to for InfluxDB API client libraries in your favorite framework or language.
+weight: 4
+menu:
+ influxdb_2_2:
+ name: Client library tutorials
+ parent: Develop with the API
+influxdb/v2.2/tags: [api]
+---
+
+{{< children >}}
diff --git a/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/_index.md b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/_index.md
new file mode 100644
index 000000000..5444176ea
--- /dev/null
+++ b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/_index.md
@@ -0,0 +1,30 @@
+---
+title: InfluxDB API client library starter
+seotitle: Starter tutorial for InfluxDB API client libraries
+description: Follow step-by-step tutorials to build an IoT dashboard with API client libraries in your favorite framework or language.
+weight: 4
+menu:
+ influxdb_2_2:
+ name: Client library starter
+ parent: Client library tutorials
+influxdb/v2.2/tags: [api]
+---
+
+Follow step-by-step tutorials to build an Internet-of-Things (IoT) application with InfluxData client libraries and your favorite framework or language.
+InfluxData and the user community maintain client libraries for developers who want to take advantage of:
+
+- Idioms for InfluxDB requests, responses, and errors.
+- Common patterns in a familiar programming language.
+- Faster development and less boilerplate code.
+
+These tutorials walk through using the InfluxDB API and
+client libraries to build a modern application as you learn the following:
+
+- InfluxDB core concepts.
+- How the application interacts with devices and InfluxDB.
+- How to authenticate apps and devices to the API.
+- How to install a client library.
+- How to write and query data in InfluxDB.
+- How to use the InfluxData UI libraries to format data and create visualizations.
+
+{{< children >}}
diff --git a/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/nodejs.md b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/nodejs.md
new file mode 100644
index 000000000..2b617c688
--- /dev/null
+++ b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/nodejs.md
@@ -0,0 +1,508 @@
+---
+title: JavaScript client library starter
+seotitle: Use JavaScript client library to build a sample application
+list_title: JavaScript client library starter
+description: >
+ Build a JavaScript application that writes, queries, and manages devices with the
+ InfluxDB client library.
+menu:
+ influxdb_2_2:
+ identifier: client-library-starter-js
+ name: JavaScript
+ parent: Client library starter
+influxdb/v2.2/tags: [api, javascript, nodejs]
+---
+
+{{% api/iot-starter-intro %}}
+
+## Contents
+
+- [Contents](#contents)
+- [Set up InfluxDB](#set-up-influxdb)
+ - [Authenticate with an InfluxDB API token](#authenticate-with-an-influxdb-api-token)
+- [Introducing IoT Starter](#introducing-iot-starter)
+- [Create the application](#create-the-application)
+- [Install InfluxDB client library](#install-influxdb-client-library)
+- [Configure the client library](#configure-the-client-library)
+- [Build the API](#build-the-api)
+- [Create the API to list devices](#create-the-api-to-list-devices)
+ - [Handle requests for device information](#handle-requests-for-device-information)
+ - [Retrieve and list devices](#retrieve-and-list-devices)
+- [Create the API to register devices](#create-the-api-to-register-devices)
+ - [Create an authorization for the device](#create-an-authorization-for-the-device)
+ - [Write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket)
+- [Install and run the UI](#install-and-run-the-ui)
+
+## Set up InfluxDB
+
+If you haven't already, [create an InfluxDB Cloud account](https://www.influxdata.com/products/influxdb-cloud/) or [install InfluxDB OSS](https://www.influxdata.com/products/influxdb/).
+
+### Authenticate with an InfluxDB API token
+
+For convenience in development,
+[create an _All-Access_ token](/influxdb/v2.2/security/tokens/create-token/)
+for your application. This grants your application full read and write
+permissions on all resources within your InfluxDB organization.
+
+{{% note %}}
+
+For a production application, create and use a
+{{% cloud-only %}}custom{{% /cloud-only %}}{{% oss-only %}}read-write{{% /oss-only %}}
+token with minimal permissions and only use it with your application.
+
+{{% /note %}}
+
+## Introducing IoT Starter
+
+The application architecture has four layers:
+
+- **InfluxDB API**: InfluxDB v2 API.
+- **IoT device**: Virtual or physical devices write IoT data to the InfluxDB API.
+- **UI**: Sends requests to the server and renders views in the browser.
+- **API**: Receives requests from the UI, sends requests to InfluxDB, and processes responses from InfluxDB.
+
+{{% note %}}
+For the complete code referenced in this tutorial, see the [influxdata/iot-api-js repository](https://github.com/influxdata/iot-api-js).
+{{% /note %}}
+
+## Create the application
+
+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:
+
+```bash
+mkdir ~/iot-api-apps
+cd ~/iot-api-apps
+```
+
+Use [Next.js](https://nextjs.org/), a framework for full-stack JavaScript applications, to create your application.
+
+1. 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](https://github.com/vercel/next-learn/tree/master/basics/learn-starter):
+
+ ```bash
+ npx create-next-app iot-api-js --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
+ ```
+
+2. After the installation completes, enter the following commands in your terminal to go into your `./iot-api-js` directory and start the development server:
+
+ ```bash
+ cd iot-api-js
+ npm run dev -p 3001
+ ```
+
+To view the application, visit in your browser.
+
+## Install InfluxDB client library
+
+The InfluxDB client library provides the following InfluxDB API interactions:
+
+- Query data with the Flux language.
+- Write data to InfluxDB.
+- Batch data in the background.
+- Retry requests automatically on failure.
+
+1. Enter the following command into your terminal to install the client library:
+
+ ```bash
+ npm i @influxdata/influxdb-client
+ ```
+
+2. 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:
+
+ ```bash
+ npm i @influxdata/influxdb-client-apis
+ ```
+
+For more information about the client library, see the [influxdata/influxdb-client-js repo](https://github.com/influxdata/influxdb-client-js).
+
+## Configure the client library
+
+InfluxDB client libraries require configuration properties from your InfluxDB environment.
+Typically, you'll provide the following properties as environment variables for your application:
+
+- `INFLUX_URL`
+- `INFLUX_TOKEN`
+- `INFLUX_ORG`
+- `INFLUX_BUCKET`
+- `INFLUX_BUCKET_AUTH`
+
+Next.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.
+
+```bash
+# .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:
+
+```sh
+# .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:
+
+ 1. `CONTROL+C` to stop the application.
+ 2. `npm run dev` to start the application.
+
+Next.js sets variables that you can access in the `process.env` object--for example:
+
+```ts
+console.log(process.env.INFLUX_ORG)
+```
+
+## Build the API
+
+Your application API provides server-side HTTP endpoints that process requests from the UI.
+Each API endpoint is responsible for the following:
+
+1. Listen for HTTP requests (from the UI).
+2. Translate requests into InfluxDB API requests.
+3. Process InfluxDB API responses and handle errors.
+4. Respond with status and data (for the UI).
+
+## Create the API to list devices
+
+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.
+
+### Handle requests for device information
+
+1. Create a `./pages/api/devices/[[...deviceParams]].js` file to handle requests for `/api/devices` and `/api/devices//measurements/`.
+
+2. In the file, export a Next.js request `handler` function.
+[See the example](https://github.com/influxdata/iot-api-js/blob/18d34bcd59b93ad545c5cd9311164c77f6d1995a/pages/api/devices/%5B%5B...deviceParams%5D%5D.js).
+
+ {{% note %}}
+In Next.js, the filename pattern `[[...param]].js` creates a _catch-all_ API route.
+To learn more, see [Next.js dynamic API routes](https://nextjs.org/docs/api-routes/dynamic-api-routes).
+ {{% /note %}}
+
+### Retrieve and list devices
+
+Retrieve registered devices in `INFLUX_BUCKET_AUTH` and process the query results.
+
+1. Create a Flux query that gets the last row of each [series](/influxdb/v2.2/reference/glossary#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).
+
+ ```js
+ // Flux query finds devices
+ from(bucket:`${INFLUX_BUCKET_AUTH}`)
+ |> range(start: 0)
+ |> filter(fn: (r) => r._measurement == "deviceauth" and r._field != "token")
+ |> last()
+ ```
+
+2. 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](#nodejs)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+{{% truncate %}}
+
+```ts
+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.
+ */
+ 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)](https://github.com/influxdata/iot-api-js/blob/18d34bcd59b93ad545c5cd9311164c77f6d1995a/pages/api/devices/_devices.js){{% /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:
+
+```ts
+queryRows(
+ query: string | ParameterizedQuery,
+ consumer: FluxResultObserver
+): void
+```
+
+{{% caption %}}[@influxdata/influxdb-client-js QueryAPI](https://github.com/influxdata/influxdb-client-js/blob/3db2942432b993048d152e0d0e8ec8499eedfa60/packages/core/src/QueryApi.ts){{% /caption %}}
+
+The `consumer` that you provide must implement the [`FluxResultObserver` interface](https://github.com/influxdata/influxdb-client-js/blob/3db2942432b993048d152e0d0e8ec8499eedfa60/packages/core/src/results/FluxResultObserver.ts) 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](https://rxjs.dev/guide/observer).
+
+## Create the API to register devices
+
+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](/influxdb/v2.2/security/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`.
+
+1. Add a `./pages/api/devices/create.js` file to handle requests for `/api/devices/create`.
+2. In the file, export a Next.js request `handler` function that does the following:
+
+ 1. Accept a device ID in the request body.
+ 2. Query `INFLUX_BUCKET_AUTH` and respond with an error if an authorization exists for the device.
+ 3. [Create an authorization for the device](#create-an-authorization-for-the-device).
+ 4. [Write the device ID and authorization to `INFLUX_BUCKET_AUTH`](#write-the-device-authorization-to-a-bucket).
+ 5. Respond with `HTTP 200` when the write request completes.
+
+[See the example](https://github.com/influxdata/iot-api-js/blob/25b38c94a1f04ea71f2ef4b9fcba5350d691cb9d/pages/api/devices/create.js).
+
+### Create an authorization for the device
+
+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:
+
+1. Instantiate the `AuthorizationsAPI` client and `BucketsAPI` client with the configuration.
+2. Retrieve the bucket ID.
+3. Use the client library to send a `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](#nodejs)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+{{% truncate %}}
+
+```ts
+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](https://github.com/influxdata/iot-api-js/blob/42a37d683b5e4df601422f85d2c22f5e9d592e68/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:
+
+- Bucket ID.
+- Organization ID.
+- Description: `IoTCenterDevice: DEVICE_ID`.
+- List of permissions to the bucket.
+
+To learn more about API tokens and authorizations, see [Manage API tokens](/influxdb/v2.2/security/tokens/).
+
+Next, [write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket).
+
+### 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:
+
+- Report device authorization history.
+- Manage devices with and without tokens.
+- Assign the same token to multiple devices.
+- Refresh tokens.
+
+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](#nodejs)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+```ts
+/** 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](https://github.com/influxdata/iot-api-js/blob/25b38c94a1f04ea71f2ef4b9fcba5350d691cb9d/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:
+
+1. Initialize `InfluxDBClient()` with `url`, `token`, and `org` values from the configuration.
+2. Initialize a `WriteAPI` client for writing data to an InfluxDB bucket.
+3. Create a `Point`.
+4. Use `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 |
+
+## Install and run the UI
+
+`influxdata/iot-api-ui` is a standalone [Next.js React](https://nextjs.org/docs/basic-features/pages) UI that uses your application API to write and query data in InfluxDB.
+`iot-api-ui` uses Next.js _[rewrites](https://nextjs.org/docs/api-reference/next.config.js/rewrites)_ to route all requests in the `/api/` path to your API.
+
+To install and run the UI, do the following:
+
+1. In your `~/iot-api-apps` directory, clone the [`influxdata/iot-api-ui` repo](https://github.com/influxdata/iot-api-ui) and go into the `iot-api-ui` directory--for example:
+
+ ```bash
+ cd ~/iot-api-apps
+ git clone git@github.com:influxdata/iot-api-ui.git
+ cd ./iot-app-ui
+ ```
+
+2. The `./.env.development` file contains default configuration settings that you can
+ edit or override (with a `./.env.local` file).
+3. To start the UI, enter the following command into your terminal:
+
+ ```bash
+ yarn dev
+ ```
+
+ To view the list and register devices, visit in your browser.
+
+To learn more about the UI components, see [`influxdata/iot-api-ui`](https://github.com/influxdata/iot-api-ui).
diff --git a/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/python.md b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/python.md
new file mode 100644
index 000000000..a735a3bb9
--- /dev/null
+++ b/content/influxdb/v2.2/api-guide/tutorials/client-library-starter/python.md
@@ -0,0 +1,583 @@
+---
+title: Python client library starter
+seotitle: Use Python client library to build a sample application
+list_title: Python
+description: >
+ Build an application that writes, queries, and manages devices with the InfluxDB
+ client library for Python.
+weight: 3
+menu:
+ influxdb_2_2:
+ identifier: client-library-starter-py
+ name: Python
+ parent: Client library starter
+influxdb/v2.2/tags: [api, python]
+---
+
+{{% api/iot-starter-intro %}}
+- How to use the InfluxData UI libraries to format data and create visualizations.
+
+## Contents
+
+- [Contents](#contents)
+- [Set up InfluxDB](#set-up-influxdb)
+ - [Authenticate with an InfluxDB API token](#authenticate-with-an-influxdb-api-token)
+- [Introducing IoT Starter](#introducing-iot-starter)
+- [Create the application](#create-the-application)
+- [Install InfluxDB client library](#install-influxdb-client-library)
+- [Configure the client library](#configure-the-client-library)
+- [Build the API](#build-the-api)
+- [Create the API to register devices](#create-the-api-to-register-devices)
+ - [Create an authorization for the device](#create-an-authorization-for-the-device)
+ - [Write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket)
+- [Create the API to list devices](#create-the-api-to-list-devices)
+- [Create IoT virtual device](#create-iot-virtual-device)
+- [Write telemetry data](#write-telemetry-data)
+- [Query telemetry data](#query-telemetry-data)
+- [Define API responses](#define-api-responses)
+- [Install and run the UI](#install-and-run-the-ui)
+
+## Set up InfluxDB
+
+If you haven't already, [create an InfluxDB Cloud account](https://www.influxdata.com/products/influxdb-cloud/) or [install InfluxDB OSS](https://www.influxdata.com/products/influxdb/).
+
+### Authenticate with an InfluxDB API token
+
+For convenience in development,
+[create an _All-Access_ token](/influxdb/v2.2/security/tokens/create-token/)
+for your application. This grants your application full read and write
+permissions on all resources within your InfluxDB organization.
+
+{{% note %}}
+
+For a production application, create and use a
+{{% cloud-only %}}custom{{% /cloud-only %}}{{% oss-only %}}read-write{{% /oss-only %}}
+token with minimal permissions and only use it with your application.
+
+{{% /note %}}
+
+## Introducing IoT Starter
+
+The application architecture has four layers:
+
+- **InfluxDB API**: InfluxDB v2 API.
+- **IoT device**: Virtual or physical devices write IoT data to the InfluxDB API.
+- **UI**: Sends requests to the server and renders views in the browser.
+- **API**: Receives requests from the UI, sends requests to InfluxDB,
+ and processes responses from InfluxDB.
+
+{{% note %}}
+For the complete code referenced in this tutorial, see the [influxdata/iot-api-python repository](https://github.com/influxdata/iot-api-python).
+{{% /note %}}
+
+## Create the application
+
+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:
+
+```bash
+mkdir ~/iot-api-apps
+cd ~/iot-api-apps
+```
+
+Use [Flask](https://flask.palletsprojects.com/), a lightweight Python web
+framework,
+to create your application.
+
+1. In your `~/iot-api-apps` directory, open a terminal and enter the following commands to create and navigate into a new project directory:
+
+ ```bash
+ mkdir iot-api-python && cd $_
+ ```
+
+2. Enter the following commands in your terminal to create and activate a Python virtual environment for the project:
+
+ ```bash
+ # Create a new virtual environment named "virtualenv"
+ # Python 3.8+
+ python -m venv virtualenv
+
+ # Activate the virtualenv (OS X & Linux)
+ source virtualenv/bin/activate
+ ```
+
+3. After activation completes, enter the following commands in your terminal to install Flask with the `pip` package installer (included with Python):
+
+ ```bash
+ pip install Flask
+ ```
+
+4. In your project, create a `app.py` file that:
+
+ 1. Imports the Flask package.
+ 2. Instantiates a Flask application.
+ 3. Provides a route to execute the application.
+
+ ```python
+ from flask import Flask
+ app = Flask(__name__)
+
+ @app.route("/")
+ def hello():
+ return "Hello World!"
+ ```
+
+ {{% caption %}}[influxdata/iot-api-python app.py](https://github.com/influxdata/iot-api-python/blob/main/app.py){{% /caption %}}
+
+ Start your application.
+ The following example code starts the application
+ on `http://localhost:3001` with debugging and hot-reloading enabled:
+
+ ```bash
+ export FLASK_ENV=development
+ flask run -h localhost -p 3001
+ ```
+
+ In your browser, visit to view the “Hello World!” response.
+
+## Install InfluxDB client library
+
+The InfluxDB client library provides the following InfluxDB API interactions:
+
+- Query data with the Flux language.
+- Write data to InfluxDB.
+- Batch data in the background.
+- Retry requests automatically on failure.
+
+Enter the following command into your terminal to install the client library:
+
+```bash
+pip install influxdb-client
+```
+
+For more information about the client library, see the [influxdata/influxdb-client-python repo](https://github.com/influxdata/influxdb-client-python).
+
+## Configure the client library
+
+InfluxDB client libraries require configuration properties from your InfluxDB environment.
+Typically, you'll provide the following properties as environment variables for your application:
+
+- `INFLUX_URL`
+- `INFLUX_TOKEN`
+- `INFLUX_ORG`
+- `INFLUX_BUCKET`
+- `INFLUX_BUCKET_AUTH`
+
+To set up the client configuration, create a `config.ini` in your project's top
+level directory and paste the following to provide the necessary InfluxDB credentials:
+
+```ini
+[APP]
+INFLUX_URL =
+INFLUX_TOKEN =
+INFLUX_ORG =
+INFLUX_BUCKET = iot_center
+INFLUX_BUCKET_AUTH = iot_center_devices
+```
+
+{{% caption %}}[/iot-api-python/config.ini](https://github.com/influxdata/iot-api-python/blob/main/config.ini){{% /caption %}}
+
+Replace the following:
+
+- **``**: your InfluxDB instance URL.
+- **``**: your InfluxDB [API token](#authorization) with permission to query (_read_) buckets
+and create (_write_) authorizations for devices.
+- **``**: your InfluxDB organization ID.
+
+## Build the API
+
+Your application API provides server-side HTTP endpoints that process requests from the UI.
+Each API endpoint is responsible for the following:
+
+1. Listen for HTTP requests (from the UI).
+2. Translate requests into InfluxDB API requests.
+3. Process InfluxDB API responses and handle errors.
+4. Respond with status and data (for the UI).
+
+## Create the API to register devices
+
+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](/influxdb/v2.2/security/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`.
+
+### Create an authorization for the device
+
+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:
+
+1. Instantiate the `AuthorizationsAPI` client and `BucketsAPI` client with the configuration.
+2. Retrieve the bucket ID.
+3. Use the client library to send a `POST` request to the `/api/v2/authorizations` InfluxDB API endpoint.
+
+Create a `./api/devices.py` file that contains the following:
+
+{{< code-tabs-wrapper >}}
+{{% code-tabs %}}
+[Python](#python)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+{{% truncate %}}
+
+```python
+# Import the dependencies.
+import configparser
+from datetime import datetime
+from uuid import uuid4
+
+# Import client library classes.
+from influxdb_client import Authorization, InfluxDBClient, Permission, PermissionResource, Point, WriteOptions
+from influxdb_client.client.authorizations_api import AuthorizationsApi
+from influxdb_client.client.bucket_api import BucketsApi
+from influxdb_client.client.query_api import QueryApi
+from influxdb_client.client.write_api import SYNCHRONOUS
+
+from api.sensor import Sensor
+
+# Get the configuration key-value pairs.
+
+config = configparser.ConfigParser()
+config.read('config.ini')
+
+def create_authorization(device_id) -> Authorization:
+ influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'),
+ token=os.environ.get('INFLUX_TOKEN'),
+ org=os.environ.get('INFLUX_ORG'))
+
+ authorization_api = AuthorizationsApi(influxdb_client)
+ # get bucket_id from bucket
+ buckets_api = BucketsApi(influxdb_client)
+ buckets = buckets_api.find_bucket_by_name(config.get('APP', 'INFLUX_BUCKET')) # function returns only 1 bucket
+ bucket_id = buckets.id
+ org_id = buckets.org_id
+ desc_prefix = f'IoTCenterDevice: {device_id}'
+ org_resource = PermissionResource(org_id=org_id, id=bucket_id, type="buckets")
+ read = Permission(action="read", resource=org_resource)
+ write = Permission(action="write", resource=org_resource)
+ permissions = [read, write]
+ authorization = Authorization(org_id=org_id, permissions=permissions, description=desc_prefix)
+ request = authorization_api.create_authorization(authorization)
+ return request
+```
+
+{{% /truncate %}}
+{{% caption %}}[iot-api-python/api/devices.py](https://github.com/influxdata/iot-api-python/blob/d389a0e072c7a03dfea99e5663bdc32be94966bb/api/devices.py#L145){{% /caption %}}
+
+To create an authorization that has _read_-_write_ permission to `INFLUX_BUCKET`, you need the bucket ID.
+To retrieve the bucket ID, `create_authorization(deviceId)` calls the
+`BucketsAPI find_bucket_by_name` function that sends a `GET` request to
+the `/api/v2/buckets` InfluxDB API endpoint.
+`create_authorization(deviceId)` then passes a new authorization in the request body with the following:
+
+- Bucket ID.
+- Organization ID.
+- Description: `IoTCenterDevice: DEVICE_ID`.
+- List of permissions to the bucket.
+
+To learn more about API tokens and authorizations, see [Manage API tokens](/influxdb/v2.2/security/tokens/).
+
+Next, [write the device authorization to a bucket](#write-the-device-authorization-to-a-bucket).
+
+### 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:
+
+- Report device authorization history.
+- Manage devices with and without tokens.
+- Assign the same token to multiple devices.
+- Refresh tokens.
+
+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 `./api/devices.py`, add the following `create_device(device_id)` function:
+
+{{< code-tabs-wrapper >}}
+{{% code-tabs %}}
+[Python](#python)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+```python
+def create_device(device_id=None):
+ influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'),
+ token=config.get('APP', 'INFLUX_TOKEN'),
+ org=config.get('APP', 'INFLUX_ORG'))
+ if device_id is None:
+ device_id = str(uuid4())
+ write_api = influxdb_client.write_api(write_options=SYNCHRONOUS)
+ point = Point('deviceauth') \
+ .tag("deviceId", device_id) \
+ .field('key', f'fake_auth_id_{device_id}') \
+ .field('token', f'fake_auth_token_{device_id}')
+ client_response = write_api.write(bucket=config.get('APP', 'INFLUX_BUCKET_AUTH'), record=point)
+ # write() returns None on success
+ if client_response is None:
+ return device_id
+ # Return None on failure
+ return None
+```
+
+{{% caption %}}[iot-api-python/api/devices.py](https://github.com/influxdata/iot-api-python/blob/f354941c80b6bac643ca29efe408fde1deebdc96/api/devices.py#L47){{% /caption %}}
+
+{{% /code-tab-content %}}
+{{< /code-tabs-wrapper >}}
+
+`create_device(device_id)` takes a _`device_id`_ and writes data to `INFLUX_BUCKET_AUTH` in the following steps:
+
+1. Initialize `InfluxDBClient()` with `url`, `token`, and `org` values from the configuration.
+2. Initialize a `WriteAPI` client for writing data to an InfluxDB bucket.
+3. Create a `Point`.
+4. Use `write_api.write()` to write the `Point` to the bucket.
+5. Check for failures--if the write was successful, `write_api` returns `None`.
+6. Return _`device_id`_ if successful; `None` otherwise.
+
+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 |
+
+Next, [create the API to list devices](#create-the-api-to-list-devices).
+
+## Create the API to list devices
+
+Add the `/api/devices` API endpoint that retrieves, processes, and lists registered devices.
+
+1. Create a Flux query that gets the last row of each [series](/influxdb/v2.2/reference/glossary#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).
+
+ ```js
+ // Flux query finds devices
+ from(bucket:`${INFLUX_BUCKET_AUTH}`)
+ |> range(start: 0)
+ |> filter(fn: (r) => r._measurement == "deviceauth" and r._field != "token")
+ |> last()
+ ```
+
+2. Use the `QueryApi` client to send the Flux query to the `POST /api/v2/query` InfluxDB API endpoint.
+
+ In `./api/devices.py`, add the following:
+
+ {{< code-tabs-wrapper >}}
+ {{% code-tabs %}}
+ [Python](#python)
+ {{% /code-tabs %}}
+ {{% code-tab-content %}}
+
+ {{% truncate %}}
+
+ ```python
+ def get_device(device_id=None) -> {}:
+ influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'),
+ token=os.environ.get('INFLUX_TOKEN'),
+ org=os.environ.get('INFLUX_ORG'))
+ # Queries must be formatted with single and double quotes correctly
+ query_api = QueryApi(influxdb_client)
+ device_filter = ''
+ if device_id:
+ device_id = str(device_id)
+ device_filter = f'r.deviceId == "{device_id}" and r._field != "token"'
+ else:
+ device_filter = f'r._field != "token"'
+
+ flux_query = f'from(bucket: "{config.get("APP", "INFLUX_BUCKET_AUTH")}") ' \
+ f'|> range(start: 0) ' \
+ f'|> filter(fn: (r) => r._measurement == "deviceauth" and {device_filter}) ' \
+ f'|> last()'
+
+ response = query_api.query(flux_query)
+ result = []
+ for table in response:
+ for record in table.records:
+ try:
+ 'updatedAt' in record
+ except KeyError:
+ record['updatedAt'] = record.get_time()
+ record[record.get_field()] = record.get_value()
+ result.append(record.values)
+ return result
+ ```
+
+{{% /truncate %}}
+
+{{% caption %}}[iot-api-python/api/devices.py get_device()](https://github.com/influxdata/iot-api-python/blob/9bf44a659424a27eb937d545dc0455754354aef5/api/devices.py#L30){{% /caption %}}
+
+{{% /code-tab-content %}}
+{{< /code-tabs-wrapper >}}
+
+The `get_device(device_id)` function does the following:
+
+1. Instantiates a `QueryApi` client and sends the Flux query to InfluxDB.
+2. Iterates over the `FluxTable` in the response and returns a list of tuples.
+
+## Create IoT virtual device
+
+Create a `./api/sensor.py` file that generates simulated weather telemetry data.
+Follow the [example code](https://github.com/influxdata/iot-api-python/blob/f354941c80b6bac643ca29efe408fde1deebdc96/api/sensor.py) to create the IoT virtual device.
+
+Next, generate data for virtual devices and [write the data to InfluxDB](#write-telemetry-data).
+
+## Write telemetry data
+
+In this section, you write telemetry data to an InfluxDB bucket.
+To write data, use the InfluxDB client library to send a `POST` request to the `/api/v2/write` InfluxDB API endpoint.
+
+The example below uses the following steps to generate data and then write it to InfluxDB:
+
+1. Initialize a `WriteAPI` instance.
+2. Create a `Point` with the `environment` measurement and data fields for temperature, humidity, pressure, latitude, and longitude.
+3. Use the `WriteAPI write` method to send the point to InfluxDB.
+
+In `./api/devices.py`, add the following `write_measurements(device_id)` function:
+
+{{< code-tabs-wrapper >}}
+{{% code-tabs %}}
+[Python](#python)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+```python
+def write_measurements(device_id):
+ influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'),
+ token=config.get('APP', 'INFLUX_TOKEN'),
+ org=config.get('APP', 'INFLUX_ORG'))
+ write_api = influxdb_client.write_api(write_options=SYNCHRONOUS)
+ virtual_device = Sensor()
+ coord = virtual_device.geo()
+ point = Point("environment") \
+ .tag("device", device_id) \
+ .tag("TemperatureSensor", "virtual_bme280") \
+ .tag("HumiditySensor", "virtual_bme280") \
+ .tag("PressureSensor", "virtual_bme280") \
+ .field("Temperature", virtual_device.generate_measurement()) \
+ .field("Humidity", virtual_device.generate_measurement()) \
+ .field("Pressure", virtual_device.generate_measurement()) \
+ .field("Lat", coord['latitude']) \
+ .field("Lon", coord['latitude']) \
+ .time(datetime.utcnow())
+ print(f"Writing: {point.to_line_protocol()}")
+ client_response = write_api.write(bucket=config.get('APP', 'INFLUX_BUCKET'), record=point)
+ # write() returns None on success
+ if client_response is None:
+ # TODO Maybe also return the data that was written
+ return device_id
+ # Return None on failure
+ return None
+```
+
+{{% caption %}}[iot-api-python/api/devices.py write_measurement()](https://github.com/influxdata/iot-api-python/blob/f354941c80b6bac643ca29efe408fde1deebdc96/api/devices.py){{% /caption %}}
+
+{{% /code-tab-content %}}
+{{< /code-tabs-wrapper >}}
+
+## Query telemetry data
+
+In this section, you retrieve telemetry data from an InfluxDB bucket.
+To retrieve data, use the InfluxDB client library to send a `POST` request to the `/api/v2/query` InfluxDB API endpoint.
+The example below uses the following steps to retrieve and process telemetry data:
+
+ 1. Query `environment` measurements in `INFLUX_BUCKET`.
+ 2. Filter results by `device_id`.
+ 3. Return CSV data that the [`influxdata/giraffe` UI library](https://github.com/influxdata/giraffe) can process.
+
+In `./api/devices.py`, add the following `get_measurements(device_id)` function:
+
+{{< code-tabs-wrapper >}}
+{{% code-tabs %}}
+[Python](#python)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+```python
+def get_measurements(query):
+ influxdb_client = InfluxDBClient(url=config.get('APP', 'INFLUX_URL'),
+ token=os.environ.get('INFLUX_TOKEN'), org=os.environ.get('INFLUX_ORG'))
+ query_api = QueryApi(influxdb_client)
+ result = query_api.query_csv(query,
+ dialect=Dialect(
+ header=True,
+ delimiter=",",
+ comment_prefix="#",
+ annotations=['group', 'datatype', 'default'],
+ date_time_format="RFC3339"))
+ response = ''
+ for row in result:
+ response += (',').join(row) + ('\n')
+ return response
+```
+
+{{% caption %}}[iot-api-python/api/devices.py get_measurements()](https://github.com/influxdata/iot-api-python/blob/9bf44a659424a27eb937d545dc0455754354aef5/api/devices.py#L122){{% /caption %}}
+
+{{% /code-tab-content %}}
+{{< /code-tabs-wrapper >}}
+
+## Define API responses
+
+In `app.py`, add API endpoints that match incoming requests and respond with the results of your modules.
+In the following `/api/devices/` route example, `app.py` retrieves _`device_id`_ from `GET` and `POST` requests, passes it to the `get_device(device_id)` method and returns the result as JSON data with CORS `allow-` headers.
+
+{{< code-tabs-wrapper >}}
+{{% code-tabs %}}
+[Python](#python)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+```python
+@app.route('/api/devices/', methods=['GET', 'POST'])
+def api_get_device(device_id):
+ if request.method == "OPTIONS": # CORS preflight
+ return _build_cors_preflight_response()
+ return _corsify_actual_response(jsonify(devices.get_device(device_id)))
+```
+
+{{% caption %}}[iot-api-python/app.py](https://github.com/influxdata/iot-api-python/blob/9bf44a659424a27eb937d545dc0455754354aef5/app.py){{% /caption %}}
+
+{{% /code-tab-content %}}
+{{< /code-tabs-wrapper >}}
+
+Enter the following commands into your terminal to restart the application:
+
+ 1. `CONTROL+C` to stop the application.
+ 2. `flask run -h localhost -p 3001` to start the application.
+
+To retrieve devices data from your API, visit in your browser.
+
+## Install and run the UI
+
+`influxdata/iot-api-ui` is a standalone [Next.js React](https://nextjs.org/docs/basic-features/pages) UI that uses your application API to write and query data in InfluxDB.
+`iot-api-ui` uses Next.js _[rewrites](https://nextjs.org/docs/api-reference/next.config.js/rewrites)_ to route all requests in the `/api/` path to your API.
+
+To install and run the UI, do the following:
+
+1. In your `~/iot-api-apps` directory, clone the [`influxdata/iot-api-ui` repo](https://github.com/influxdata/iot-api-ui) and go into the `iot-api-ui` directory--for example:
+
+ ```bash
+ cd ~/iot-api-apps
+ git clone git@github.com:influxdata/iot-api-ui.git
+ cd ./iot-app-ui
+ ```
+
+2. The `./.env.development` file contains default configuration settings that you can
+ edit or override (with a `./.env.local` file).
+3. To start the UI, enter the following command into your terminal:
+
+ ```bash
+ yarn dev
+ ```
+
+ To view the list and register devices, visit in your browser.
+
+To learn more about the UI components, see [`influxdata/iot-api-ui`](https://github.com/influxdata/iot-api-ui).
diff --git a/layouts/shortcodes/api/iot-starter-intro b/layouts/shortcodes/api/iot-starter-intro
new file mode 100644
index 000000000..da7c49ccf
--- /dev/null
+++ b/layouts/shortcodes/api/iot-starter-intro
@@ -0,0 +1,10 @@
+Follow this step-by-step tutorial to build an Internet-of-Things (IoT) application with InfluxData client libraries and your favorite framework or language.
+
+In this tutorial, you'll use the InfluxDB API and
+client libraries to build a modern application as you learn the following:
+
+- InfluxDB core concepts.
+- How the application interacts with devices and InfluxDB.
+- How to authenticate apps and devices to the API.
+- How to install a client library.
+- How to write and query data in InfluxDB.
diff --git a/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/_devices.js b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/_devices.js
new file mode 100644
index 000000000..141fd5702
--- /dev/null
+++ b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/_devices.js
@@ -0,0 +1,49 @@
+import { flux } from '@influxdata/influxdb-client'
+import influxdb from '../_influxdb'
+
+const INFLUX_ORG = process.env.INFLUX_ORG
+const INFLUX_BUCKET_AUTH = process.env.INFLUX_BUCKET_AUTH
+
+/**
+ * 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.
+ */
+ 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 = {}
+ console.log(`*** QUERY *** \n ${fluxQuery}`)
+ 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() {
+ console.log(JSON.stringify(devices))
+ resolve(devices)
+ },
+ })
+ })
+ }
+
+
\ No newline at end of file
diff --git a/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/create.js b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/create.js
new file mode 100644
index 000000000..ece1c404d
--- /dev/null
+++ b/shared/text/api/v2.0/client-library-examples/nodejs/server/devices/create.js
@@ -0,0 +1,78 @@
+import { getDevices } from './devices/_devices'
+
+import influxdb from '../_influxdb'
+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
+
+export default async function handler(req, res) {
+ try {
+ const deviceId = JSON.parse(req.body)?.deviceId
+ const devices = await createDevice(deviceId)
+ res.status(200).json(
+ Object.values(devices)
+ .filter((x) => x.deviceId && x.key) // ignore deleted or unknown devices
+ .sort((a, b) => a.deviceId.localeCompare(b.deviceId))
+ )
+ } catch(err) {
+ res.status(500).json({ error: `failed to load data: ${err}` })
+ }
+}
+
+/** 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 authorization
+ }
+}
+
+ /**
+ * 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},
+ },
+ ],
+ },
+ })
+ }
+
\ No newline at end of file