Tricking Laravel to load SPA frontend using basic webhost server
In the quest to leverage ith web hosting that I have already been using for this blog, I wanted to develop and self-host (cheaply) with what I already have. I started to learn Laravel. I think anyone familiar with Laraval knows how much of a beauty this framework is.
With some research, I decided to build my application using Laravel + React. A mix of something new and something I already know to speed things up.
One of the biggest challenges for me is the setup to host the whole application (frontend and backend) using my hostinger service. By design, because it is already running on PHP, the backend part was pretty straightforward. However, the front end was the challenging one. For context, my code was bootstrapped using this laravel-react-purity template which has the structure like this:
/root
|- /laravel-api
|- /react-ui
In short, for development, you would have laravel-api
running on one localhost port, and react-ui
running on a separate localhost port. If you plan to run both on a separate domain (eg. api.yourdomain.com
and app.yourdomain.com
) there shouldn’t be any issue.
Just that…
I am pretty stubborn. It should be possible to share both frontend and backend. That is where I found out there aren’t many guides with Google search I ended up using fragmented information to piece together this workflow which works so seamlessly with my webhosting server.
Breaking down the challenge
Tricking Laravel’s view
to load the react SPA page
With some smart hack, this is how I did it with my GHA script.
- name: configure-deploy-directory
run: |
mkdir deploy
# deploy laravel as root
mv laravel-api/* deploy
# replace to index.php for webserver to detect
mv deploy/server.php deploy/index.php
# address htaccess issue
cp deploy/public/.htaccess deploy
# rename index.html to blade using web view resource
mv react-ui/build deploy/client
mv deploy/client/index.html deploy/resources/views/app.blade.php
# static file using public url defined by the build steps
mv deploy/client/* deploy/public
Let’s dive into detail:
Firstly, I treat the laravel-api
as the first-class citizen in the deployment. Thus, it goes into the root of the directory.
Then, I created a server.php
to emulate Apache’s “mod_rewrite” functionality from the built-in PHP web server. I chose not to commit as index.php
is to preserve my development environment.
<?php
/**
* Laravel - A PHP Framework For Web Artisans
*
* @package Laravel
* @author Taylor Otwell <taylor@laravel.com>
*/
$uri = urldecode(
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);
// This file allows us to emulate Apache's "mod_rewrite" functionality from the
// built-in PHP web server. This provides a convenient way to test a Laravel
// application without having installed a "real" web server software here.
if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
return false;
}
require_once __DIR__.'/public/index.php';
Unfortunately, we also need to tweak the htaccess
so that are aware of the loading of the /root
(index.php). Thus, my setup for the file is as follows:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Finally, build the react into a static page, and renaming with app.blade.php
. This is to trick Laravel into loading the HTML as though is coming from Laravel. All the resources can be access through /public
directory. Which is also what I did.
Remember to update the API call to the server accordingly in the config/constant.js
export const API_SERVER = process.env.NODE_ENV === "production"
? "/api/"
: "http://localhost:5000/api/";
Automating the compilation for the web hosting server
Once all the bells and whistles are setup, we are prepared to push everything to the web server. Credit to Stackoverflow for the idea. This is the GHA for it:
- name: push-to-special-branch
uses: s0/git-publish-subdir-action@develop
env:
REPO: self
BRANCH: hostinger # The branch name where you want to push the assets
FOLDER: deploy # The directory where your assets are generated
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub will automatically add this - you don't need to bother getting a token
MESSAGE: "Build: ({sha}) {msg}" # The commit message
What it does, is to push everything into a separate or reserve branch. In my case above, I call it hostinger
.
Setup continuous deployment with Hostinger
If the above is done right, just need to follow the steps provided by hostinger.
Conclusion
Every time, when I finish my code, and am ready to deploy to production, I simply just need to trigger the workflow_dispatch
and I am done. With this setup, I have eased my step needed to upload to Hostinger and do not need to worry about how to redeploy if I do happen to make some fixes or new features.