@novominteractive/ase-react-apollo
v2.0.0
Published
ASE React Apollo ===========================
Readme
ASE React Apollo
This is a library using Anyware Serverless Engine and Apollo Client to receive real-time data from an AWS IoT endpoint and merge that data into the Apollo Client's cache.
Licensed Work
This product requires a commercial license generated by Novom Interactive. This is not free software, this software is covered by copyright and to use you need a commercial license.
Please see LICENSE.md.
Peer Dependencies
| Package | Version |
|---|---|
| @apollo/client | ^4 |
| @novominteractive/anyware-stateless-client | 2.0.0-alpha.12 |
| react | >=18 |
Install
yarn add @novominteractive/ase-react-apolloSetup
1. Create an ASE client
Instantiate a Client with the IoT endpoint and a root topic. The root topic is typically scoped per project and environment.
import { Client } from '@novominteractive/ase-react-apollo';
const aseClient = new Client({
rootTopic: `${project}/${env}`,
licenseKey: '<YOUR_LICENSE_KEY>',
});2. Wrap your app with AseProvider
AseProvider must be a child of Apollo's ApolloProvider. Wrap them together so all child components have access to both the Apollo and ASE contexts.
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
import { AseProvider } from '@novominteractive/ase-react-apollo';
const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
uri: 'https://your-graphql-endpoint',
});
function App() {
return (
<ApolloProvider client={apolloClient}>
<AseProvider client={aseClient}>
<YourApp />
</AseProvider>
</ApolloProvider>
);
}3. Obtain an auth token and log in
The auth token must be generated server-side by an endpoint that signs a JWT using the secret key paired with your ASE license key. Never generate or store this token on the client. Retrieve it from your backend, then call login.
import { useAse } from '@novominteractive/ase-react-apollo';
import { useEffect } from 'react';
function AuthGate() {
const { login, logout } = useAse();
useEffect(() => {
// Fetch a signed token from your backend — do not generate it on the client.
fetch('/api/ase-token')
.then((res) => res.json())
.then(({ token }) => login(token));
return () => { logout(); };
}, [login, logout]);
return null;
}4. Subscribe to real-time data
Use useAseSubscription inside any child of AseProvider to execute a GraphQL query and keep it in sync with IoT messages.
const { data, loading } = useAseSubscription<MessagePayload, MyQueryType>(
'my-topic',
MY_QUERY,
(previousData, newPayload) => ({
...previousData,
messages: [...(previousData?.messages ?? []), newPayload],
}),
);API
Exports
| Export | Type | Description |
|---|---|---|
| Client | class | Re-exported from @novominteractive/anyware-stateless-client. Used to connect to the IoT endpoint. |
| AseProvider | component | React provider that exposes the ASE context to its children. |
| AseContext | context | The raw React context object for ASE. |
| useAse | hook | Returns the ASE context: { client, loggedIn, login, logout }. |
| useAseClient | hook | Returns the Client instance. Throws if the client is not defined. |
| useAseMessages | hook | Subscribes to an IoT topic and calls a handler on each message. |
| useAseSubscription | hook | Executes a GraphQL query and keeps its cache in sync with an IoT topic. |
AseProvider
Wraps your application (or a subtree) to provide the ASE context. Must be a parent of any component that calls useAse, useAseClient, useAseMessages, or useAseSubscription.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
| client | Client \| undefined | Yes | The ASE client instance. |
| children | ReactNode | Yes | Child components. |
import { AseProvider, Client } from '@novominteractive/ase-react-apollo';
import { useState, useEffect } from 'react';
function App() {
const [aseClient, setAseClient] = useState<Client | undefined>();
useEffect(() => {
const client = new Client({
connectionHandlers: {
onDisconnect: () => console.info('Disconnect'),
onError: (err: unknown) => console.error('err', err),
onInterrupt: (err: unknown) => console.error('interrupt', err),
onResume: (code: unknown, sessionPresent: unknown) => console.info(`Resume: ${code} ${sessionPresent}`),
},
iotConnect: {
authorizerName: '<YOUR_CUSTOM_AUTHORIZER>',
},
licenseKey: '<YOUR_LICENSE_KEY>',
rootTopic: '<YOUR_ROOT_TOPIC>',
});
setAseClient(client);
}, []);
return (
<AseProvider client={aseClient}>
{/* children have access to ASE context */}
</AseProvider>
);
}useAse
Returns the ASE context.
const { client, loggedIn, login, logout } = useAse();| Property | Type | Description |
|---|---|---|
| client | Client \| undefined | The ASE client instance. |
| loggedIn | boolean | Whether the client is currently connected. |
| login | (authToken: string) => Promise<void> | Connects the client to the IoT endpoint. The token must be generated server-side and signed with the secret key paired with your ASE license key. |
| logout | () => Promise<void> | Disconnects the client from the IoT endpoint. |
useAseClient
Returns the Client instance from the context. Throws an error if no client is defined.
const client = useAseClient();useAseMessages
Subscribes to an IoT topic and invokes a handler whenever a message is received. Automatically subscribes when the client is logged in and unsubscribes on cleanup.
useAseMessages<TPayload>(topic, handler, skip?)| Parameter | Type | Default | Description |
|---|---|---|---|
| topic | string | — | The IoT topic to subscribe to. |
| handler | (payload: TPayload) => any | — | Called with each incoming message payload. |
| skip | boolean | false | When true, the subscription is skipped. |
useAseSubscription
Executes a GraphQL query and keeps the Apollo cache in sync with messages received on an IoT topic. The initial data comes from the network; subsequent updates are applied via the cacheMerger function when IoT messages arrive.
const result = useAseSubscription<TPayload, TData, TVariables>(
topic,
query,
cacheMerger,
queryOptions?,
);| Parameter | Type | Description |
|---|---|---|
| topic | string | The IoT topic to subscribe to. |
| query | DocumentNode \| TypedDocumentNode<TData, TVariables> | The GraphQL query to execute. |
| cacheMerger | (previousData: TData \| null, newData: TPayload) => TData | Merges the current Apollo cache with the new IoT payload. |
| queryOptions | QueryHookOptions<TData, TVariables> | Apollo useQuery options (e.g. variables, skip). |
Returns the same result object as Apollo's useQuery.
Example
import { useAse, useAseSubscription } from '@novominteractive/ase-react-apollo';
import { useEffect } from 'react';
import { MY_QUERY } from './queries';
function MyComponent() {
const { login, logout } = useAse();
useEffect(() => {
login('your-auth-token');
return () => { logout(); };
}, [login, logout]);
const { data, loading, error } = useAseSubscription<string, MyQueryType>(
'my-topic',
MY_QUERY,
(previousData, newPayload) => ({
...previousData,
items: [...(previousData?.items ?? []), newPayload],
}),
);
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
return <ul>{data?.items.map((item) => <li key={item}>{item}</li>)}</ul>;
}Issue Reporting
If you are a customer and wish to report a bug or raise a new feature request, please send us an email at [email protected].
