This sample application shows how to use the TypeScript client libraries for Azure in some common scenarios.
In this sample, we build a simple application in React Native using Expo and integrating with Azure Event Hubs and Service Bus.
The samples are compatible with LTS versions of Node.js.
The sample is using Expo, a framework and platform for universal React applications.
You need [an Azure subscription][freesub] and the following resources created to run this sample:
- Create the project using Expo
npx create-expo-app messaging
- Add TypeScript support by adding a
tsconfig.json
file with following content
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
}
}
- Run the following Expo command to start the app. Expo will prompt to install TypeScript and types packages for React/React-Native.
npx expo start
Now the application should be bundled and ready to run on device, emulators, or web browsers.
- Ctrl+C to exit the npm script.
They will be used to choose a testing scenario and run it. Open App.tsx and replace the content with following code
import { StatusBar } from "expo-status-bar";
import React, { useState } from "react";
import { Button, StyleSheet, Text, View, FlatList, TouchableOpacity } from "react-native";
import { testSDK } from "./src/testSDK";
const DATA = [
{
id: "eh-send-msgs",
title: "Event Hub: Send Messages",
},
{
id: "eh-receive-msgs",
title: "Event Hub: Receive Messages",
},
{
id: "sb-send-msgs",
title: "Service Bus: Send Messages",
},
{
id: "sb-receive-msgs",
title: "Service Bus: Receive Messages",
},
];
const Item = ({ item, onPress, backgroundColor, textColor }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, backgroundColor]}>
<Text style={[styles.title, textColor]}>{item.title}</Text>
</TouchableOpacity>
);
export default function App() {
const [selectedId, setSelectedId] = useState(null);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
const color = item.id === selectedId ? "white" : "black";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
backgroundColor={{ backgroundColor }}
textColor={{ color }}
/>
);
};
return (
<View style={styles.container}>
<Text>@azure/service-bus test app</Text>
<Button title="Run tests!" onPress={() => testSDK(selectedId)} />
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
/>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
Add a src
directory at the root of the project and add the following files
- testSDK.ts - contains the code to run the scenarios.
- ehSendEvents.ts - contains the code to send messages to Event Hub.
- ehReceiveEvents.ts - contains the code to receive messages from Event Hub.
- sbSendMessages.ts - contains the code to send messages to Service Bus.
- sbReceiveMessages.ts - contains the code to receive messages from Service Bus.
- wsWrapper.ts - contains the code to wrap
WebSocket
implementation in React Native to set default value of itsbinaryType
property to"blob"
.
We need to add JavaScript Client SDK libraries for Event Hubs and Service Bus as dependencies.
We use @babel/plugin-proposal-async-generator-functions
to help transform async iterator usage.
We also use babel-plugin-inline-dotenv
to help load secrets from our .env
file while developing.
yarn add @azure/event-hubs @azure/service-bus
yarn add --dev babel-plugin-inline-dotenv @babel/plugin-proposal-async-generator-functions
Then add the following into babel.config.js
to enable the plugins
presets: ["babel-preset-expo"],
+ plugins: [
+ "@babel/plugin-proposal-async-generator-functions",
+ ["inline-dotenv", { unsafe: true }],
+ ],
Create a .env
file in the project directory. Retrieve your connection strings from Azure portal and add them to the .env
file. Since this file contains secrets you need to add it to the ignore list if your code is committed to a repository. We also add variables for messaging entity names.
Note We use connection string directly here for testing purpose. You should consider the trade-off between security and convenience and better use a backend to dynamically provide secrets to only authenticated users.
# Service Bus
SERVICEBUS_CONNECTION_STRING=
QUEUE_NAME=
# Event Hub
EVENTHUB_CONNECTION_STRING=
EVENTHUB_NAME=
CONSUMER_GROUP_NAME=
Note: Whenever you update the .env file again, you need to clear expo cache and rebuild. It can be done by passing -c
to the start command, for example, in package.json
- "android": "expo start --android",
+ "android": "expo start --android -c",
Our dependency rhea
depends a few NodeJS modules. We need to provide polyfills for them. In this sample, We will be using node-libs-react-native
and react-native-get-random-values
. Note that it may be possible to slim the polyfills further as the SDK really only needs process
, and Buffer
at runtime.
yarn add node-libs-react-native react-native-get-random-values
Add a metro.config.js
to the root of the project with content of
module.exports = {
resolver: {
extraNodeModules: require("node-libs-react-native"),
},
};
In App.tsx
we need to import the polyfills at the top, before importing any Azure SDK module.
+import "node-libs-react-native/globals";
+import "react-native-get-random-values";
import { testSDK } from "./src/testSDK";
The implementation of WebSocket
on React-Native doesn't follow the standard, which is causing problem for rhea
when receiving binary data. We need to add a wrapper around WebSocket
to fix it.
Add the following to src/wsWrapper.ts
export class WebSocketWrapper {
constructor(...args) {
const instance = new globalThis.WebSocket(...args);
instance.binaryType = "blob";
return instance;
}
}
Then update our testing code to pass the webSocketOptions
via the client options when creating clients:
-import { ServiceBusClient, ServiceBusMessage, ServiceBusMessageBatch } from "@azure/service-bus";
+import { ServiceBusClient, ServiceBusMessage, ServiceBusMessageBatch } from "@azure/service-bus";
+import { WebSocketWrapper } from "./wsWrapper";
// ...
export async function main() {
- const sbClient = new ServiceBusClient(connectionString);
+ const sbClient = new ServiceBusClient(connectionString, {
+ webSocketOptions: {
+ webSocket: WebSocketWrapper,
+ },
+ });
Now the application should work as expected, sending and receiving Event Hub Events/Service Bus messages.