Originally posted at michaelzanggl.com.
In my first job, besides web development, there was also this application written in PowerBuilder. A rather old restrictive language to create data-driven cruddy Windows applications...
One thing that stood out for me, however, was the ease in which to access the database. On the event listener of a button, you can simply access or write to the database (or directly call a dedicated service that would do so). There is no distinction between backend and frontend.
It looked something like this
yikes... But the ease of use is incredible (if it wasn't for the syntax...).
Now let's look at the web. Backend and frontend are separate pieces. It doesn't matter how you look at it. If you have server driven applications, or an SPA that accesses your API, there is a clear separation.
You can't possibly have something like a database query running directly in the event listener of a button click. Well... What if I told you, it is both possible and secure to do so.
// FRONTEND
// resources/js/main.js
import { getUser } from '@/app/Actions/users.js'
getUser(1).then(user => {
document.getElementById('app').innerHTML = JSON.stringify(user)
})
// BACKEND
// app/Actions/users.js
import User from '@/app/Models/User'
exports.getUser = async (id) => {
return User.findOrFail(id)
}
So a script on the frontend simply imports a function from the backend and calls it to get the user.
Not mindblowing? Okay, how about this?
// FRONTEND
// resources/js/main.js
import { getUser } from '@/app/Actions/users.php'
getUser(1).then(user => {
document.getElementById('app').innerHTML = JSON.stringify(user)
})
In case you missed it, pay close attention to this line:
import { getUser } from '@/app/Actions/users.php'
Let's zoom in some more: '@/app/Actions/users.php'
. And some more .php
.
Yes, the approach is not limited to Node.js but can work with possibly any backend language.
So what's going on? Obviously we got rid of the API layer, but how?
Well, honestly, we haven't, we just swept it under the carpet. That means when you call getUser
it will still perform an ajax request to the server. There will still be an api route on the backend. But all of that boilerplate and dealing with HTTP is poof gone.
No more writing fetch requests to the backend, no more setting up API routes, no need for controllers. That means if you want to find out what the ajax request is doing, you no longer have to track down the routes file, go to the controller which again just goes to some service file. Just (ctrl/cmd) + click
on the function. It's seamless.
Note: While the project would be in a monorepo you can still deploy frontend and backend as separate units.
How it works
It's surprisingly simple. A roughly 10 line webpack loader (could be rollup, TS, etc.) on the frontend that intercepts module resolution for files from the backend. Instead of importing the backend code, it will import a function that performs an HTTP request for you pointing to the correct route. At the same time, the backend will automatically create the JSON API for all files inside the "actions" folder. So you can still use the JSON API are you in need of a mobile app for example.
So yes, this works with any server language, given that someone writes the script to generate the routes and provide the function names for the frontend loader.
Check it out here if you want to give it a try: https://github.com/MZanggl/byebye-api-prototype.
This is obviously still a prototype. But you might have seen something similar already in frameworks like Blitzjs.
I will also prepare an example with adonis.js on the backend and vue.js on the frontend to give a more real world example that covers more use cases.
If this article helped you, I have a lot more tips on simplifying writing software here.
Top comments (5)
+1Michael , Nice Article
I like it
Love this idea! I've been thinking a lot lately about how much wasted time there is code yup move data from one place to another.
But it's PHP specific?
It technically works with any backend language. The repository I linked to uses Node.js for example.