Prerequisites
Before we get started, you should have a basic understanding of Reactjs, Laravel, and how to set up a local development environment. You will also need to have Node.js and PHP installed on your computer.
Setting Up the Backend with Laravel
The first step in building a full-stack web application is to set up the backend of our application using Laravel. Laravel provides a powerful set of tools for building scalable web applications quickly.
To get started, open up a terminal window and create a new Laravel project:
composer create-project --prefer-dist laravel/laravel myapp
Once the project has been created, navigate to the project directory and start the development server:
cd myapp
php artisan serve
Next, let's create a database for our application. Open up the .env file and update the following lines with your database information:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=
Now, let's create a migration for our users table:
php artisan make:migration create_users_table
In the migration file, add the following code to create the users table:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
Now, let's run the migration to create the users table:
php artisan migrate
With our database set up and our users table created, we can move on to creating API endpoints for our frontend to consume.
Let's create a new controller for our API endpoints:
php artisan make:controller Api/UserController
In the controller file, add the following code to create a create method for creating new users:
public function create(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8',
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 400);
}
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
return response()->json(['message' => 'User created', 'user' => $user], 201);
}
This method uses Laravel's built-in validation and authentication tools to create a new user and return a response to the frontend
Let's add a list method to our controller to return a list of all users:
public function list()
{
$users = User::all();
return response()->json(['users' => $users], 200);
}
Now that we have our API endpoints set up, let's move on to creating the frontend of our application using Reactjs.
Creating the Frontend with Reactjs
To create the frontend of our application, we will use Create React App, a tool for quickly creating Reactjs applications.
To get started, open up a new terminal window and create a new Reactjs project:
npx create-react-app myapp-frontend
Once the project has been created, navigate to the project directory and start the development server:
cd myapp-frontend
npm start
Now, let's create a new component for our user list:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get('/api/users')
.then(response => {
setUsers(response.data.users);
})
.catch(error => {
console.log(error);
});
}, []);
return (
User List
- {user.name} - {user.email}
{users.map(user => (
))}
);
}
export default UserList;
This component fetches the list of users from our API endpoint and renders them in a list.
Let's also create a component for creating new users:
import React, { useState } from 'react';
import axios from 'axios';
function CreateUser() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
axios.post('/api/users', {
name,
email,
password,
})
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
};
return (
Create User
);
}
export default CreateUser;
This component allows the user to create a new user by submitting a form.
Now that we have our components set up, let's add them to our app:
import React from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function App() {
return (
);
}
export default App;
Finally, let's connect our frontend to our Laravel backend using Axios:
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:8000';
function App() {
return (
);
}
export default App;
Now our frontend will be able to communicate with our backend API.
Authentication and Authorization
Authentication and authorization are important parts of any web application. In Laravel, we can use the built-in `auth` middleware to protect our API endpoints.
Let's modify our user controller to use the `auth` middleware:
class UserController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
// ...
}
Now, our API endpoints will require authentication before they can be accessed.
Let's also modify our Reactjs app to handle authentication. We can use the `axios-auth-refresh` library to automatically refresh the access token when it expires:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import axiosAuthRefresh from 'axios-auth-refresh';
axios.defaults.baseURL = 'http://localhost:8000';
function App() {
const [authenticated, setAuthenticated] = useState(false);
const login = (email, password) => {
axios.post('/api/login', {
email,
password,
})
.then(response => {
localStorage.setItem('token', response.data.access_token);
axios.defaults.headers.common.Authorization = Bearer ${response.data.access_token};
setAuthenticated(true);
})
.catch(error => {
console.log(error);
});
};
const logout = () => {
localStorage.removeItem('token');
delete axios.defaults.headers.common.Authorization;
setAuthenticated(false);
};
useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
axios.defaults.headers.common.Authorization = Bearer ${token};
setAuthenticated(true);
}
const refreshAuthLogic = failedRequest =>
axios.post('/api/refresh')
.then(tokenRefreshResponse => {
localStorage.setItem('token', tokenRefreshResponse.data.access_token);
failedRequest.response.config.headers.Authorization = `Bearer ${tokenRefreshResponse.data.access_token}`;
return Promise.resolve();
})
.catch(error => {
console.log(error);
});
axiosAuthRefresh.interceptor(axios, refreshAuthLogic);
}, []);
return (
{authenticated ? (
<>
>
) : (
)}
);
}
export default App;
This app uses a login form to authenticate the user, and refreshes the access token using `axios-auth-refresh`.
Deployment
Now that our application is working locally, we can deploy it to a production server. There are many ways to deploy a Laravel and Reactjs application, but one popular method is to use Docker and Docker Compose.
To deploy our application using Docker Compose, we will need to create a `Dockerfile` for our Laravel backend and a `Dockerfile` and `docker-compose.yml` file for our Reactjs frontend.
Here's an example `Dockerfile` for our Laravel backend:
FROM php:7.4-fpm-alpine
RUN apk update
&& apk add --no-cache
curl
g++
gcc
git
make
mariadb-dev
npm
openssl-dev
&& docker-php-ext-install pdo_mysql
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
WORKDIR /var/www
COPY . .
RUN composer install --no-dev --no-scripts --no-autoloader --prefer
Here's the rest of the Dockerfile for our Laravel backend:
RUN composer dump-autoload --no-scripts --optimize
RUN chown -R www-data:www-data /var/www
COPY .docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini
EXPOSE 8000
CMD ["php", "artisan", "serve", "--host", "0.0.0.0", "--port", "8000"]
This Dockerfile installs the necessary dependencies, copies our application files, installs the composer dependencies, and sets the command to start the development server.
Here's an example Dockerfile for our Reactjs frontend:
FROM node:16-alpine
WORKDIR /app
COPY package.json .
COPY yarn.lock .
RUN yarn install
COPY . .
RUN yarn build
EXPOSE 3000
CMD ["yarn", "start"]
FROM node:16-alpine
WORKDIR /app
COPY package.json .
COPY yarn.lock .
RUN yarn install
COPY . .
RUN yarn build
EXPOSE 3000
CMD ["yarn", "start"]
This Dockerfile installs the necessary dependencies, copies our application files, installs the yarn dependencies, builds the production version of our application, and sets the command to start the development server.
Finally, here's an example docker-compose.yml file to deploy both containers:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- .:/var/www
environment:
DB_HOST: mysql
DB_PORT: 3306
DB_DATABASE: myapp
DB_USERNAME: root
DB_PASSWORD: secret
depends_on:
- mysql
networks:
- app-network
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
volumes:
- ./data:/var/lib/mysql
ports:
- "3306:3306"
networks:
- app-network
frontend:
build:
context: ./myapp-frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
volumes:
- ./myapp-frontend:/app
networks:
- app-network
networks:
app-network:
driver: bridge
This docker-compose.yml file defines three services: our Laravel backend, our MySQL database, and our Reactjs frontend. It also defines a network for the services to communicate over.
To deploy our application using Docker Compose, we can simply run:
docker-compose up --build
This command will build and start our containers.
Conclusion
In this tutorial, we have covered the process of building a full-stack web application using Reactjs and Laravel. We have covered everything from setting up the development environment to deploying the application to a production server.