nats.ws
nats.ws copied to clipboard
React Native - URL.protocol is not implemented
I had this hook to connect to nats server. I am working with TS and React Native. I already tried this The error I see in the console is: [ERROR: URL.protocol is not implemented]
import {connect, NatsConnection} from 'nats.ws';
import {useEffect, useState} from 'react';
export default function useMessages() {
const [nats, setNats] = useState<NatsConnection>();
useEffect(() => {
(async () => {
const nc = await connect({
servers: ['wss://demo.nats.io:8443'],
});
setNats(nc);
console.log('connected to NATS');
})();
return () => {
nats?.drain();
console.log('closed NATS connection');
};
}, []);
return {};
}
React Native info
System:
OS: macOS 13.2.1
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 112.41 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 16.13.2 - ~/.nvm/versions/node/v16.13.2/bin/node
Yarn: 1.22.15 - /usr/local/bin/yarn
npm: 8.1.2 - ~/.nvm/versions/node/v16.13.2/bin/npm
Watchman: Not Found
Managers:
CocoaPods: 1.11.3 - /Users/user/.rvm/gems/ruby-2.7.5/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4
Android SDK:
API Levels: 23, 24, 27, 28, 29, 30, 31, 33
Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.2, 30.0.3, 31.0.0
System Images: android-25 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom, android-29 | Google APIs Intel x86 Atom, android-29 | Google Play Intel x86 Atom, android-30 | Google APIs Intel x86 Atom, android-30 | Google APIs Intel x86 Atom_64, android-31 | Google APIs Intel x86 Atom_64, android-33 | Google APIs Intel x86 Atom_64
Android NDK: Not Found
IDEs:
Android Studio: 2020.3 AI-203.7717.56.2031.7678000
Xcode: 14.3/14E222b - /usr/bin/xcodebuild
Languages:
Java: 11.0.2 - /Users/user/.jenv/shims/javac
npmPackages:
@react-native-community/cli: Not Found
react: 18.1.0 => 18.1.0
react-native: 0.70.5 => 0.70.5
react-native-macos: Not Found
npmGlobalPackages:
*react-native*: Not Found
nats.ws version: 1.14.0
Effectively the react native is unlikely to work - nats.ws is expecting a compliant ES w3c websocket environment.
Can you polyfill this with https://www.npmjs.com/package/react-native-url-polyfill?
you can try - I know that there are a few folks that have gotten older versions to work, but it is not clear if they got https://github.com/nats-io/nats.js to work or if it was https://github.com/nats-io/nats.ws.
@aricart is there a recommended approach for integrating NATS with React Native? Certainly this is a common ask, no?
It is not a very common ask, and it has been discussed that it should possibly be one of the supported clients.
@jfols Work with that polyfill So I'm going put all steps to run this package in react native
- Run in your terminal
yarn add fastestsmallesttextencoderdecoder nats.ws node-libs-react-native react-native-url-polyfill text-encoding-polyfill
- Update the
metro.config.js
like this:
module.exports = {
resolver: {
extraNodeModules: require('node-libs-react-native'),
},
...more config
};
- In your
index.js
/**
* Polyfills for nats.ws package
*/
import 'react-native-url-polyfill/auto';
import 'text-encoding-polyfill';
if (!Symbol.asyncIterator) {
Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator');
}
And that's all 🌟
Hi, anyone any tips on what the status is regarding working with nats on react-native?
Hi @pietgk I worked with nats and react native, so far I've not seen any problem. In order to use it: First follow the steps that I wrote Then you can create a hook to manage the subscriptions in the react native side, it could be some like this code:
import {connect, NatsConnection, consumerOpts} from 'nats.ws';
import {useCallback, useEffect, useRef} from 'react';
import {decode} from 'base-64';
const decodeMessageFromNatsServer = () => {
// YOUR LOGIC TO DECODE THE MESSAGE
}
const useNatsSubscription = ({
table,
onMessage,
}: {
table: string;
onMessage: (msg: object) => void;
}) => {
const nats = useRef<NatsConnection>();
const onSubscriptionMessage = useCallback(
async ({nameSubscription}: {nameSubscription: string}) => {
if (!nats.current) {
return;
}
const opts = consumerOpts({
name: 'MY_CHANNEL',
description: 'MY_CHANNEL_DESC',
});
opts.deliverNew();
opts.ackExplicit();
opts.manualAck();
nats.current
?.jetstream()
.pullSubscribe(nameSubscription, opts)
.then(async psub => {
psub?.pull({batch: 1});
for await (const m of psub) {
const payload = decodeMessageFromNatsServer(m);
if (payload) {
onMessage(payload);
}
m.ack();
psub.pull({batch: 1});
}
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[table],
);
const onSubscribe = useCallback(async () => {
try {
const nameSubscription = 'NAME_SUBSCRIPTION';
if (!nameSubscription) {
return;
}
// eslint-disable-next-line no-undef
const encoder = new TextEncoder();
const credentials = decode('NATS_USER_CRED_STRING');
const encodedCredentials = encoder.encode(credentials);
const nc = await connect({
debug: false,
servers: ['NATS_SERVER_ADDRESS'],
authenticator: encodedCredentials,
});
nats.current = nc;
await onSubscriptionMessage({nameSubscription});
} catch (error) {
console.log("ERROR: NATS Subscription", error);
}
}, []);
useEffect(() => {
onSubscribe();
return async () => {
await nats.current?.drain();
};
}, [onSubscribe]);
return {
onSubscribe,
};
};
export default useNatsSubscription;
// IN YOU COMPONENT
const sendedMessage = useRef(false);
const onRefresh = async () => {
// YOUT LOGIC
}
useNatsSubscription({
table: 'message',
onMessage: () => {
/* This code is part of a subscription to a NATS message queue. When a new message is received, it
checks if the `sendedMessage.current` flag is set to true. If it is, it means that the message
was sent by the current user and there is no need to refresh the conversation. If it is not
set, it calls the `onRefresh` function to refresh data with the new data/message. */
const refresh = async () => {
try {
if (sendedMessage.current) {
sendedMessage.current = false;
return;
}
await onRefresh(false);
} catch (error) {
} finally {
sendedMessage.current = false;
}
};
refresh().catch(error => console.log('on refresh', error));
},
});
@jfols Work with that polyfill So I'm going put all steps to run this package in react native
- Run in your terminal
yarn add fastestsmallesttextencoderdecoder nats.ws node-libs-react-native react-native-url-polyfill text-encoding-polyfill
- Update the
metro.config.js
like this:module.exports = { resolver: { extraNodeModules: require('node-libs-react-native'), }, ...more config };
- In your
index.js
/** * Polyfills for nats.ws package */ import 'react-native-url-polyfill/auto'; import 'text-encoding-polyfill'; if (!Symbol.asyncIterator) { Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator'); }
And that's all 🌟
It works, thank you so much mate! I tried to develop my own TCP library, it worked, but was pretty unstable.
For other folks trying to fix this problem. You can follow steps above and start with this simple example to ensure that it works:
https://nats.io/blog/getting-started-nats-ws/#reactjs
Basically, once you are able to connect, you are good to go