DEV Community

Cover image for PHP: Laravel, Ruby: Rails, JavaScript:?
JS for ZenStack

Posted on • Originally published at zenstack.dev

PHP: Laravel, Ruby: Rails, JavaScript:?

Recently, there has been a heated discussion on Twitter between JS developers and Laravel and Rails developers. It started with a lengthy tweet from Taylor Otwell, author of Laravel:


In short, he suggested that the whole JavaScript ecosystem lacks a real "full stack" framework like Laravel or Rails, which could allow a single developer to build the next GitHub, AirBnb, or Shopify.

I deeply empathize with this, as I share the same goal when building ZenStack, the typescript toolkit on top of Prisma ORM. In fact, I have often heard that kind of word from our community:

chat-image

No one can deny the popularity and fast-paced growth of the JS ecosystem, even among non-members. So why is it?

The Historical Reason

Men make their own history, but they do not make it as they please; they do not make it under self-selected circumstances, but under circumstances existing already, given and transmitted from the past -- Karl Marx

Both PHP and Ruby were designed as server-side languages from the start. PHP was created in 1994 to build dynamic web pages, while Ruby, which appeared in the mid-1990s, was designed for general-purpose programming.

Given their server-side origins, PHP and Ruby were naturally suited for comprehensive frameworks that could handle all aspects of web development, from routing and controllers to database interactions and templating engines. This led to the creation of frameworks like Laravel and Rails to offer a complete, opinionated way to build web apps.

Contrastingly, JavaScript was born as a client-side scripting language for web browsers. It had nothing to do with the backend until 2009, when Node.js was introduced. If you've heard of the "Browser Wars" between Netscape Navigator and Internet Explorer, you're likely aware of the ongoing chaos in the frontend, which continues to drive front-end developers crazy today in the name of browser compatibility. As a result, the early web was about piecing together disparate technologies. Therefore, JavaScript developers became accustomed to modularity, allowing flexibility to mix and match libraries and tools for survival. That's why NPM, which emerged alongside Node.js, has grown at an astonishing rate, quickly becoming the largest software registry in the world.

This different circumstances led to the different developer cultures:

  • PHP/Ruby devs: "Give me a framework that just works. I want conventions, stability, and a clear path to shipping."
  • JS devs: "Don't box me in! I want flexibility, the latest tools, and the freedom to build my way, even if it means more work upfront."

As a result, even after expanding to the backend world, a monolithic, "one-size-fits-all" approach could hardly fly in the Javascript ecosystem.

Contemporary Endeavor

On one hand, this culture leads to constant evolution, keeping the entire ecosystem exciting and innovative. However, it also results in more decision fatigue and a steeper learning curve for newcomers.

"Where there's muck, there's brass." Some individuals embarked on an adventurous journey to build a Rails-like, battery-included framework to challenge the status quo. Here are some popular examples:

  • RedwoodJS

    Full-stack JavaScript framework that integrates React, GraphQL, and Prisma. It simplifies development with a unified setup and automatic code generation, perfect for scalable applications.

  • Biltz.js

    Blitz.js extends Next.js into a full-stack framework featuring a zero-API data layer and Prisma for database access. It aims to simplify development by allowing direct server-side code calls from the frontend.

  • AdonisJS

    AdonisJS is a TypeScript-first web framework for building web apps and API servers. It offers a rich set of features out-of-the-box, including an ORM, authentication, and a powerful CLI, making it ideal for developers seeking a comprehensive and structured development environment.

Will they become the Laravel or Rails of the JS world? It's probably too early to say, but at least RedwoodJS shows great momentum:

Redwood trending

Another bunch of guys are trying to solve this problem by providing “start kits” with opinionated battery-included toolkits. Among these, the most popular one is Create-T3-App, which combines Next.js, tRPC, Tailwind CSS, and other powerful tools to give you a solid foundation for building typesafe web applications.

Interestingly, Theo, the creator of T3, seems to be pessimistic about this whole endeavor of JavaScript world:

Optimistic Future

Any application that can be written in JavaScript, will eventually be written in JavaScript. — Jeff Atwood

While I'm not entirely convinced by Atwood's law, I do envision a promising future for JavaScript in the field of web development. The reason is simple:

It’s the first time in history that the whole web app could be developed with one programming language.

This is a significant benefit, particularly for novice developers. Thanks to TypeScript's excellent type inference system, we are not only capable of doing it but also willing to do so.

A common critique from Laravel or Rails users is that these frameworks lack a conventional method for modeling the relationship between different entities in your system like the below:

While it may not have reached the level of Laravel or Rails, the current efforts in the JS world have recognized this issue. If you look at the toolkit of the solution mentioned above, you'll find a common name: Prisma

If you haven’t heard about Prisma, it is a modern TypeScript-first ORM that allows you to manage database schemas easily, make queries and mutations with great flexibility, and ensure excellent type safety. This empowers JavaScript developers with a level of data handling sophistication and ease of relationship modeling traditionally found in Laravel and Rails, much like Laravel’s Eloquent ORM.

The ZenStack toolkit I’m building on top of Prisma aims to narrow down the gap further. It adds an Authorization layer on top of the schema and then automatically generates both APIs and frontend hooks for you. So, put simply, once you're done with your schema, you're almost done with your backend. You can then choose whatever frontend framework, like React, Vue, or Svelte, to get your UI done.

zenstack-schema

Begin With the End in Mind

Will JavaScript ever have its Laravel/Rails moment? Personally, I believe, or at least hope, that having standardized conventions leads to global optimization for the entire ecosystem. However, given the history and culture of JavaScript, achieving this may take a significant amount of time. It's unclear whether AI will accelerate this process or completely overturn it.

So, it seems we just have to wait and see. However, let's not get lost in this debate and forget our original intention, as Lee Robinson says:

So, I will quote a statement from the W3C Web Platform Design Principles at the end:

User needs come before the needs of web page authors, which come before the needs of user agent implementors, which come before the needs of specification writers, which come before theoretical purity.

Top comments (15)

Collapse
 
yogeshgalav7 profile image
Yogesh Galav • Edited

I belong from Laravel community who recently started working on node/express (because of more salary, offcourse you get more salary on complex things).
Last time I had to use Sequalize ORM with express, First I was satisfied with bunch of material available online, then I realize it's not regularly maintained, so I had to take help from my friends
and all my friends had different choice knex, objection, prisma, drizzle. Now I had to choose one friend who can help me regularly.
After I started working knex I soon realize there is no way back, because I could not replace it easily with prisma or dirzzle easily.
Now I only left with hope with god that the knex repo is regularly maintained or else I have to make heavy code shift or as the js devs will say create your own.

Collapse
 
gitkearney profile image
Kearney Taaffe

yeah, I remember when I was first developing Node + Express. I couldn't figure out what ORM to use, then I just used MS SQL client directly. no ORM. I just wrote raw SQL queries. When I got a new job and worked with Postgres...raw SQL queries no ORM.

I got a job with an existing company using Mongo...no ORM, they used Mongoose to write queries.

I don't think I've used an ORM since like 2018. I finally tried Prisma, but, I dunno, I sort of like raw SQL

Collapse
 
asologor profile image
Andrew Sologor

Mongoose is actually an ODM (ORM for document databases). So it's not "raw" in any way, it also allows you to define model relations and load them like in relational database ORMs.

Collapse
 
psimondk profile image
Palle Simonsen

Agree Orms are good for devs that don’t get SQL and for introducing subtle bugs and supper performance. Oop is as good as dead and Orms should be a thing of the past too

Thread Thread
 
yogeshgalav7 profile image
Yogesh Galav

How did you use getter and setters?
What if you want to save name with first letter capital?
What if you want to add where('deleted_at', !=, null) to every place?

If OOP is dead why do js, old react, angular use classes?
Why does object prototypical inheritance exist in js?
According to you a Person, an Animal, an Employee is a function/verb?

Thread Thread
 
gitkearney profile image
Kearney Taaffe

save name where first letter is capitalized... stringValue.toUpperCase()

getters and setters are dead

function findActiveUsers = () => {
   const sql = `SELECT a, b, c FROM users WHERE deleted_at IS NOT NULL`
}
Enter fullscreen mode Exit fullscreen mode

or

const rs = await doQuery(params);
return rs.filter((record) => is_null(record.deleted_at) === false)
Enter fullscreen mode Exit fullscreen mode

now, you may have add the filter to a lot of places - but, that's the beauty of functional programming: you are not modifying rs and your functions stay pure.

Prototypical inheritance has been "banned" ever since MooTools, and no one does it anymore...at least they aren't supposed to.

React & Vue went functional. Angular went the opposite direction. Neither is right or wrong. But, functional patterns do away with getters/setters.

You have

const addPropToObject = (object: ObjectType, newProp, val) => {
  return {...object, newProp: val}
}
Enter fullscreen mode Exit fullscreen mode

which makes it much easier to see where an object gets modified

Thread Thread
 
yogeshgalav7 profile image
Yogesh Galav

Can't use this findActiveUsers() for chaining sql query like scopes in eloquent.

I wouldn't prefer filter method instead of sql for production application.

Functions are performed on some entity, Can't define it for database table like user, post etc.
In Js everything is object, you can't do anything without object and full form of oop is object oriented programming. Hence there is no functional programming without object instantiation. Objects and Functions both need each other for programming in any language. So please don't give noob comments like OOP is dead.

Thread Thread
 
gitkearney profile image
Kearney Taaffe

A language having objects is not OOP bruh...

const createUser = () => ({A: -1, S: '',  L: '' });
Enter fullscreen mode Exit fullscreen mode

This is object creation in JS. I get a distinct object on each function call. Object Instantiation is when you create a new class using the new keyword

A function should return the same value when it's called with the same arguments. OOP has a name for functions that modify state, "methods".

const createUser = () => ({A: -1, S: '',  L: '' });
const setLocation = (u, newL) => ({ ...u, L: newL });
const incrementAge = (u) => ({ ...u, A: u.A + 1 });

const main = () => {
   const john = createUser();
   setLocation(john, "Boston"); // john will never live in Boston
   incrementAge(john); // john's age is always -1

  console.log(john); // still: {A: -1, S: '',  L: '' }
} 
Enter fullscreen mode Exit fullscreen mode

Functions should NEVER MODIFY their parameters in ANY language. An object is immutable ALWAYS. If you want to change it's value...create a new object.

yeah, chaining is just modifying an existing object (IIRC the actual result set of the db call). Sort to the opposite of pure functional programming. Like, IIRC eloquent does a query, then another query, then sends the results of that query to another query. I remember hating Eloquent because I had a DB that used UUIDs instead of ints and getting eloquent to understand the primary key was not very easy.

OOP makes things so complicated to track down state changes. Functions are so nice
because it's explicit when there needs to be changes

Collapse
 
asologor profile image
Andrew Sologor

Check out MikroORM. It's inspired by Doctrine 2, and uses knex as a query builder.

Collapse
 
michi profile image
Michael Z

I've enjoyed writing in Adonis.js more than Laravel/Rails. It has the right amount of features without the magic of Rails or the giant ecosystem of Laravel.

Collapse
 
andric profile image
Andric Tham • Edited

I think that the JS/TS ecosystem’s browser roots also means that our Laravel moment is likely to be more client-side driven rather than a “server first” MVC approach.

The local-first movement is what’s most promising to me. There are many folks trying to make headway here, and I have no doubt we’ll see a breakthrough soon and it’ll be as huge as React, node.js, and TypeScript were for the ecosystem.

As Dax Raad was saying in this recent podcast, apps architected in a local-first way results in better UX because data is consistent throughout the entire app, and interactions feel instant. All async data fetching is abstracted away into a sync engine, and developers get a better DX too by working with all state in the app as if it were local and synchronous.

So perhaps the “Laravel/Rails/Django of TypeScript” looks less like Adonis and Nest.js, and more like a batteries-included framework for building apps that seamlessly transition between the following modes, with one unifying abstraction:

  • server-rendered pages and queries on the first load (like Next.js and Remix)
  • a live, reactive, multiplayer experience via CRDTs and WebSockets (like Yjs, Automerge, Liveblocks, PartyKit, Replicache)
  • an offline-friendly PWA that can sync up later with a backend without conflicts (like Replicache lets you do)

We’re already seeing a glimpse of this with async state managers like React Query and SWR that cache server-side state such as to enable instant optimistic mutations and offline operation.

But these async state managers are still not reaching deep enough into the server-side to enable change data capture on the database for mutations to reactively fan out into subscribed queries from other connected clients (like Prisma Pulse lets you do) and doesn’t resolve conflicts (like Replicache lets you do). Both Prisma Pulse and Replicache also all fail on the rich text editing front, and that is currently handled by a library like Yjs or Automerge.

So for the developer, you have to manage a lot of complexity and synchronization of state all over the place. This is where a framework could really help. Just like React made it so we didn’t have to think about the complexity and intricacies of DOM manipulation, perhaps one day we won’t have to think about the complexity and intricacies of state transfer, replication, conflict resolution, and cache invalidation.

I’m optimistic that we’ll have something soon. There are a few players trying to make it happen:

  • Prisma seems to be coming really close to launching a local-first syncing solution. This week they made the first step in this direction by launching a local Prisma client for SQLite, for React Native.

  • Replicache already handles most of this and they appear to be gearing up for a bigger release (unclear what it is though)

  • Jazz is slowly marching toward a full-stack solution for building a local-first app, complete with authentication, permission management, and realtime text editing

Collapse
 
gitkearney profile image
Kearney Taaffe

Thanks for the article, I will definitely be checking out ZenStack. I come from Laravel/Symfony. Honestly, every developer seems to be missing the BEST points of Node. It has a consistent API to represent a Request object, and a consistent API to represent a Response object.

I switched from Laravel to Express. Express using Jade. I did everything that I could do in Laravel with Express and Jade.

These people that are re-inventing the wheel with React + React Server components, it just blows my mind that they are forgetting Express + Jade. Now, PUG may not be as cool, but you can certainly make PUG files and include React components in them.

Node already has a Laravel, Rails killer...people just forget because it's apparently not cool

Collapse
 
mindplay profile image
Rasmus Schultz

I don't even understand why developers want this. You want someone else to make all your dependency choices and software architecture decisions, why? You want a base install of 500,000 lines of code, thousands of dependencies which, not even really knowing what half of it is, you think maybe you might need some of it some day?

You actually want a framework so big and integrated and opinionated about everything that it becomes just a black box, where you follow the manual, fill in the blanks where designated, painting by numbers, never really understanding much beyond "pulling this handle makes the machine go bling"?

You want everything you write to be proprietary and locked into your framework? You want software that couples everything to everything, breaking every piece of good software advice ever given about modularity and decoupling - and it's somehow okay to do that, because "it's the framework"?

JS is one of the last software ecosystems where you still get to build simple things and use your head. PHP used to be that, but it's pretty much over since Laravel.

I understand why some people need that - if your job is a software production line, I understand the benefits of everything being highly streamlined, being able to churn out as much code as possible for the next client as quickly and cheaply as possible.

But the rest of the industry really should ask themselves why they want this, make sure they need it, and make sure it's not just this. (it usually is.)

Collapse
 
axmad386 profile image
axmad386 • Edited

The problem with js developers are they don't like to use current technology. They keep create their own, especially for orm. You cannot force js devs to use one of your favorite orm. Some of them love to use typescript, some of them hate it, some of them love to overuse decorator, some of them like functional approach.
That's why I create my own nodejs framework 😬😂
lunox.js.org
I am not creating any new orm, just try to create adapter one for them. I use it for personal only, so I can switch from one orm to other orm depends on my team 😅

Btw I am Laravel devs, but falling in love with typescript

Collapse
 
martinsos profile image
Martin Šošić

He forgot to mention Wasp! Really full-stack, even captures the database, unique approach with its own DSL for config (similar like RoR has a DSL for routes), can't really get more full stack than that. Redwood is quite close, and Blitz, but those are kind of slowing down recently and Wasp is catching up in strides.