Its technique is not new, it's just server-side rendering via websocket and DOM patching (Morphdom is also very old)
> not appropriate at all
"At some" would be appropriate. Pretty much typical CRUD (real world) that needs to fire requests to server anyway (e.g. form, business logic validation).
The only reason not to use it _right now today_ is it's not 1.0 yet.
We're in a similar boat these days so in fact our latest projects are back to simple server side rendering, but we're still making the data retrieval calls with GraphQL. It ensures that that the mobile app and reporting tools we're also developing will have parity, and we don't need to write ad hoc query logic for each use case. The built in docs and validations have simply proven too useful to pass up, and you really don't need a heavy weight client to make requests.
It really would be easier. We are using this stack, and have separate backend and frontend devs. Each love their side of the stack. Being backend myself, I don't find the it too time consuming to get new entities going. However, I imagine if you were doing the whole stack and repeating ecto schema, absinthe schema, apollo queries, then it might get more tedious. I particularly enjoy how easy it is to modify the schemas once it's set up too. If we ever need to expose more data, it is usually done within minutes. There is a massive benefit in the forced standardization of GraphQL too. Being rigorous about standardizing APIs and how you do filtering, sorting, embedding, nesting and so on is tiring and a waste of time - you end up writing mini frameworks. Absinthe and GraphQL reduce this pain for us considerably.
If I would be to working on only 1 part it would be great, but instead of parallelizing effort it was sequenced which kind of sucked.
On top of all you said, the GraphQL stack is horrible for caching. You will not have this problem until you reach really high traffic, but once you do, it will eat you alive.
Unlike a rest endpoint, you can't cache a URL. You can't use HTTP headers. You don't know beforehand what GQL query will come and even with batch queries for different types it's super hard to optimize. It's very easy to have an N+1 query hell.
The lesson I learned is to use the boring stuff until it really needs to scale up. A REST API with static HTML and some sprinkles of JS will get you to the phase where you actually need to start using React, GQL, etc.
GQL trades a lot of things for flexibility, but 99% of the apps don't need that in the first place.
But hey, on the upside everyone can put in their resumes that they used all the new hot shit :)
Both Postgraphile and Hasura deal with this. I have no idea about Absinthe.
For a simple UI I'd generate HTML server side with eex and spare us the cost of front end development. It's also a productivity nightmare. The amount of work needed to add a single form field with React/Redux is insane.
I have forms either having their own state or (preferred) just use Formik for all of this. In my stack, this then allows to just add a field in the GraphQL schema (backend), add it in the query, add the formik field + yup validation and done.
That would mean that if you use Redux, a form also requires actions for form update/submit/success/error and the form data should be stored in the redux store.
That is one of the main issues I have with Redux, which I feel adds automatic complexity for simple things, but at the same time I'm not sure if it's very good to have a mix of tings happening from store/actions/reducers and others from local state/ajax.
I won't disagree that this is a popular opinion, but there's little practical benefit to storing state that's truly local to a single component (or a very small tree) in Redux just because it's there.
Even the maintainers of Redux maintain that it's perfectly acceptable to use local state - https://redux.js.org/faq/organizing-state#do-i-have-to-put-a...
In fact, it's most of the times not desired to update your store before you know the data has been validated anyway. The store should always be the source of truth, but that also means that it should be valid.
That's the approach I am going with in any case when working with some kind of global state.
Bonus: it's almost certainly more accessible than custom solutions.
We had a pool of 4 Node instances which is the default of the solution we were using (a patched version of https://github.com/hassox/std_json_io)
Each Node instance has a 1 GB memory limit (the heap? It seems Java like). We failed to find a way to raise it but, again, we didn't invest too much into it except some googling. It seems there used to be a command line option for that but it doesn't work anymore.
Each hit to Node raises the memory usage until it gets to 1 GB and throws an error and gets recycled, which unfortunately translates to a 502/503 to the client. We can intercept those errors in Elixir and try again but it's far from ideal.
To have less errors we naively decided to increase the number of workers but we also had to increase the RAM of the server. The first hit for each client gets served by Node so eventually Node's resource usage dwarfed Elixir's. We felt like we were doing it wrong (I'm sure there is a way to get a saner setup) and decided to turn off server side rendering. Nobody complained and we're saving some $40/50 per month on that single server, plus our time which is worth more than that.
I think that projects with little load should run on low tech uncomplicated solutions: a reverse proxy and an application server were enough in the 90's for the same scenario and are still OK now.
In my experience, SSR should not introduce more complexity than you already have.
I will remember that one "Right now we’re disassembling app to a classic REST-app and we’re seeing development speed increase on week-to-week basis" . thx
Although complex, GraphQL (done right) is much easier than REST (done right).
As for file uploads, it seems to me that the best way to do that is to have the front-end upload directly to your cloud storage service. Both S3 and Google Cloud Storage have a feature called signed URLs, where your back-end can create an object, grant the necessary permission, then give the client a temporary URL to upload to that object. Then just store the URL in the database.
Newer technology usually adds a ton of different patterns and abstractions that can hide a lot of things away from you, so it becomes hard to understand, unless this information is presented as a 101, or you invest all your time to read documentation about each individual part of everything. Which, to be frank, nobody has time for.
I have used GraphQL with Apollo and React for the last couple of years for different kinds of projects and what I have noticed is that the tools themselves, while quite abstracted, also try to support a lot of edge cases which can make it hard to apply to your project. I had to either use other libraries or build my own that helped me abstract some of the things that were most commonplace but required a lot of boilerplate otherwise.
I have found tremendous value by following the philosophy of not to pre-optimize for everything, until it becomes a burden to work with. Although, it can be hard to determine when something is going to become so big that it will be difficult if not impossible to refactor, but you learn that as you go.
We also took a step back and removed GraphQL-stack and use simple clean REST-Api only, it has increased our productivity and we don't have to divide our time for an other module, which must be maintained too. Before we used REST and GraphQL, becuase it make no sense to put everything into GraphQL.
For front end I wanted to move back to Ember since I had a lot of positive experience with it but that was impossible due to the business requirements.
As for Redux I try to stay away from it utilizing philosophy “you don’t need it unless you know you need it”. Cargo cult made Redux default piece of stack and it’s another very complex and sensitive to mis-design piece.
Instead of using Redux we rolled our own adapter since we had to work with some data outside Apollo and it’s we are rather happy with it. Not sure how it will scale to RN though.
Any alternatives that you would like to suggest?
Main reason is the absurd amount of complexity with costs heavily outhweighting benefits gained from the solution.
For example, simple task of adding new entity consists off: On backend: creating migration, creating data entity (Ecto), writing structure module (sanitization, validation, basic logic, if needed), mounting queries, writing input mutation(s), writing output query/queries, unit testing On frontend: creating component, creating form, writing graphql queries, writing mutation, wrapping components/queries in components, connecting query to components, providing action handlers for inputs, unit testing, integration testing
Now I have authors list. And even though I am full stack I haven’t yet spent even single minute on having Proper UX Design set in place. Oh, do we need to add author’s birthdate? Dang, let me adjust all of that.
In my opinion technical debt accumulates faster than in other solutions. GraphQL is complex. React (done right) is complex. Apollo is complex (Elixir is simple, yet it’s only one cog). Deciding on doing file upload in GraphQL led me to a rabbit hole, which took at least a week to dug out from.
When trying to find the source of all development issues my thoughts go towards GraphQL. Maybe it is too complex or we didn’t had enough experience in it? Yet it was really nice to work with when all the backend and frontend boilerplate was already written. It makes sense, even though requires some heavy thought behind it. Maybe it’s Apollo, which locks in one of two specific trains of thought, or Absinthe, which requires side-hacks in order to get some bit more advanced features working with Apollo, like file uploads or query batching.
From a perspective I’d say this is just too much. Every single part of this stack adds overhead to the other parts. Maybe if I had more specialized team members it would get easier, but being only 3 of us, with me, as a full stack, constantly switching between all of that was a tiresome, low-productive effort. Right now we’re disassembling app to a classic REST-app and we’re seeing development speed increase on week-to-week basis, even though we had to scrap most of the frontend.
I guess there would be some benefit on having the write up of all of it, since it doesn’t even scratch the surface of the year of development issues with this stack, but even in this very "short" form it may serve for a word of warning that this stack is not necessarily something you want to care for.