Should I use channels or a regular JSON web endpoint for my react native app?

channels
phoenix
react-native
#1

Pondering the pros and cons of making a JSON web endpoint (controller action) vs using a channel for the backend for a React Native app.

I had been looking into GraphQL, but I think it’s overkill for my app, at least now. It’s going to have have a single user’s data and not a large amount of data that would need to be queried in varying ways. It’s a handful of relations revolving around one main entity and the user will have a list of those. Thoughts regarding the benefits or pros/cons of the two approaches in this context?

One important criteria for the app is to work well with a fickle connection, so that makes me wonder if it’s worth the hassle of using channels. On the other hand, the stakeholder would like “save on blur” type behavior in the forms (I’d love to do it with LiveView, but the stakeholder also wants a “real” mobile app), which would work better with a channel. However, I could also have the client form save-on-blur locally with eventual consistency with the back end.

2 Likes
#2

Do both :slight_smile:

#3

From personal experience websockets tend to be lower latency but http is better when observability matters. It is usually easier to trace (logs, metrics) and debug both clientside (chrome dev tools) and server side, as firewalls, reverse proxies and almost every physical or virtual appliance that manages network traffic in some manner is much more capable of logging http request details (layer 7 packet inspection) than websocket traffic. If the server needs to push data to the client though a mixed solution (http for synchronous requests/responses, websockets for server pushed data).
I hope this helps.

3 Likes
#4

Yeah, I was thinking about it last night and it probably makes sense for a new or wiped client to pull the user’s entire state via an HTTP JSON endpoint and then just use the socket for updates of individual bits.

#5

Duly noted!

#6

I run everything over Phoenix channels in my react native app… push entire state over in an “after_join” and then listen for changes and make any changes over the channel… works wonderfully… and handles the offline/online out of sync scenarios beautifully…

#7

Hm, okay. Somehow the entire state seemed more suited to an HTTP request, but I suppose either way it is data moving over the wire.

#8

What about server’s connection limits? Have you had any issues with that? Let’s take Heroku for example where dynos are limited to 50 connections. Does it mean that 51st connection can’t connect until one of 50 active connections gets closed?

#9

yep, you do have edge scenario, which on mobile is not that edge with intermittent connectivity…

eg.
open app get state over http and connect over websocket… (even here you have a “gap issue” eg time/state of http request data vs time of websocket connect - ie changes might have happened in the gap)
mobile now goes offline…
and goes online after a few moments…
app is now out of sync eg in unknown state…

you can of course fire of the http request on channel connect… but now you are just intermingling different ways of getting data…

btw: I use redux-observables on react-native, and I’m a big fan…

of course there is no silver bullet, all depends on requirements etc etc… maintaining a stateful connection is by all means “more costly” serverside than a stateless state sync…
and for really bad connections a retrying http request might have more success than getting a websocket going etc etc.

#10

this is incorrect

the limit is “per router” and they have a lot of routers - you can ask support for the real limits, or do a load test… last time I checked the limit was around 1500 per dyno in the EU region ymmv…

2 Likes
#11

Do you make a channel per model/context? Per screen? I’m spinning a bit on what to make my channels model. The examples seem to always revolve around chat rooms or the like. I’m building a relatively small mobile app and want to use channels to just transfer all state and updates. It feels like, at least to start, I just need one generic channel with various events for particular data changes. The app user connects to that once on app startup and sticks with it regardles of what screen they’re on.

#12

I just have “users:all” and a “users:#{user_id}” - then the “public” stuff goes over the :all chan and the “private” stuff over the :user_id chan…

but all depends of course… if you have 10k different chat rooms you definitely want a chan for each and dynamic join/leave on the client…

since I load all state from the get go in an “after_join” I maintain all state updated through chans no matter where the user navigates around (ie. no spinning loader etc.) - but a larger app with loads of data could be different…

eg. in the admin app (which is react js - but still redux-observables) I load data dynamically as you navigate around (since “data” is big)

so +1 on a generic chan - and then I assume requirements sooner or later will need a user_id chan… ymmv but KISS always win :wink:

#13

Interesting. I’m going to at least start with that approach. For the foreseeable future, my mobile app will only display one user’s data and shouldn’t really be all that much data.

#14

So I successfully hooked up my channel to my react native app… but it was re-rendering over and over. Man, that makes my laptop very sad. So, I started following the approach outlined here:

using this lib: https://github.com/alexgriff/use-phoenix-channel

And my channel connects fine and my after_join handler fires and I see my data in the payload. But the UI never updates. Funny that first it re-rendered over and over and now I can’t get it to render the data at all.

I suppose this is a React Native issue rather than an Elixir/Phoenix issue, so I should find a RN forum to ask on. But if anyone has any tips or tutorials they can refer me to, I’d much appreciate it.

1 Like
#15

Ha. Helps to carefully read the docs. I was trying to destructuring my state out of an object returned from useChannel instead of an array as the README clearly states. It’s now hooked up and working fine.

I’m surprised there doesn’t seem to be much uptake for that use-phoenix-channel library. It’s not a lot of code, but it’s a nice abstraction and saves a little bit of work. Then again, we’ll see how I feel once I have a lot more data to deal with.

3 Likes
#16

@outlog I got the channel code working fine in development, but writing tests is problematic. I get ReferenceError: window is not defined for any test of a component using the Phoenix socket. I clearly need to mock that, or fake the DOM… or try to separate out the channel code so that it doesn’t get invoked. Any testing tips would be appreciated.

#17

One more follow-up here in case someone else tries to go down this path: I got channel communication working fine in iOS but have been struggling to get it working in Android. I think this is because Android 9 and above require secure network communications. I’ve changed my socket setup to use wss:// and created a self-signed cert and SSL endpoint in my app, but it still fails. Looking into how to get the Android emulator to accept that cert. Funny that it’s iOS that works fine!

Anyway, wanted to drop an update here… and if anyone has a tip on getting the wss:// socket working easily on Android, I’d love it. It seems like that should just work out of the box!

#18

what URI are you connecting to from the android emulator?
(localhost will not work - you need to use an IP… in my experience mdns ie .local is also flawed(not working) on android…)

ios only works because you most likely have exempted localhost in NSAppTransportSecurity

#19

Thanks @outlog! Unfortunately that is not fixing the Android socket connection for me, at least not just changing localhost to 127.0.0.1. Still works great in iOS, but Android still makes no apparent network attempt.

I started trying to add a network_security_config.xml to the Android app, but at least initially, that broke the emulator’s connection to the Metro Bundler Server. I’m still working on figuring out what I’m missing, but it would be very helpful to know if you had to mess with any Android configuration in order to get your Phoenix channel talking to Android. Thanks again!

#20

you need the local IP ie 192.168.1.XX or whatever your computer/server is on…