This is a new part of my Laravel + React dockerized series. Before moving on, you can check out the previous parts.
Laravel + React and Docker Part 1
Laravel + React and Docker
I will continue now with the next part of the backend. This time, we’ll go a little deeper into Laravel, and even touch on some PHP-level details, starting with the bootstrap file located in:
backend/bootstrap
/app.php
Basically, we slightly overwrote Laravel’s core:
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
//
})
->withExceptions(function (Exceptions $exceptions): void {
//
})->create();
I added four lines to bring the API into our app, which improves performance and reminds me of Lumen, or even other microframeworks like Slim PHP.
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withProviders([
App\Infrastructure\Providers\AppServiceProvider::class,
])
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
}
)
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
$middleware->redirectGuestsTo('/v1/login');
})
->withExceptions(function (Exceptions $exceptions): void {
//
})->create();
Because we want to communicate between the backend and frontend via HTTP, we need to allow access in Laravel by defining some configurations. One of them is creating a new file in backend/config named cors.php with the following code:
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie', "*"],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
We are still working in the config folder, but this time we overwrite an existing file instead of creating a new one: database.php.
Inside the 'sqlite' => [ section, after 'synchronous' => null, we add:
'transaction_mode' => 'DEFERRED',
At the JavaScript level, Axios is already included in Laravel 12, so there’s no need to install it separately. Since we are not using Inertia or Blade in Laravel, we can skip the resources/views folder.
There are no navigation pages, commands, or console interactions on the Laravel backend side, so in this case we can also skip web.php and console.php from the routes folder.
However, some routes are still required to send data from the database to React. For this purpose, we use api.php in the routes folder, which contains the following code:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Infrastructure\Http\Controllers\API\User\MeController;
use App\Infrastructure\Http\Controllers\API\User\LoginController;
use App\Infrastructure\Http\Controllers\API\User\LogoutController;
use App\Infrastructure\Http\Controllers\API\User\RegisterController;
use App\Infrastructure\Http\Controllers\API\Mentor\MentorListController;
use App\Infrastructure\Http\Controllers\API\Mentor\MentorProfileController;
// we can have the api routes here, no issue
Route::prefix('v1')->group(function () {
Route::middleware('guest')->group(function () {
Route::post('/register', RegisterController::class);
Route::post('/login', LoginController::class);
Route::get('/mentors', MentorListController::class);
Route::get('/mentors/{id}', MentorProfileController::class);
});
Route::middleware('auth:sanctum')->group(function () {
Route::get('/me', MeController::class);
Route::post('/logout', LogoutController::class);
});
});
Even though we are not talking about Sail, we should still discuss composer.json. We also mention Docker briefly, since in this case it’s just a small part.
Because we updated the folder structure to follow DDD, we defined our own namespaces. We then used them in our code, and it looks like this:
in array : "autoload": {
"psr-4": {
}
I added:
"App\\Domain\\": "app/Domain",
"App\\Application\\": "app/Application",
"App\\Infrastructure\\": "app/Infrastructure"
And finally, it looks like this:
"autoload": {
"psr-4": {
"App\\": "app/",
"App\\Domain\\": "app/Domain",
"App\\Application\\": "app/Application",
"App\\Infrastructure\\": "app/Infrastructure",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
The rest is the same with compose.json. The final step to dockerize a basic Laravel app is as follows: in the backend folder, create a new folder called docker. Inside it, create a file named Dockerfile (without any extension). Also, inside the docker folder, create another folder called nginx.
Our Dockerfile contains the following code:
# docker/Dockerfile
FROM php:8.3-fpm
# Install system dependencies including PostgreSQL dev headers
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
libpq-dev
RUN rm -rf /var/lib/apt/lists/*
# Install PHP extensions (now pdo_pgsql will work)
RUN docker-php-ext-install pdo pdo_pgsql mbstring exif pcntl bcmath
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# Copy composer files
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts
# Copy the rest of the application
COPY . .
# Fix permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 775 storage bootstrap/cache
EXPOSE 9000
CMD ["php-fpm"]
Our last file default.conf which is inside of nginx folder had following code :
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
To start this project (backend only for now), you need to follow these steps:
Install backend
Infrastructure following the DDD(ish) way of organizing the application.
1.cd backend
2.cp .env.example .env
3../dc.sh build ( docker compose buid )
4../dc.sh up -d ( docker compose up -d )
5../install.sh ( composer install, migrate and seed database )
Backend URL: http://localhost:8001/
I hope we can move forward with React after this. If you need a course—whether you’re at a junior or senior level—feel free to message me, or you can follow me on LinkedIn at LevelCoding
Top comments (0)