Laravel 10 Restful CRUD API
Build an API in PHP with Laravel Sanctum for a blog
Laravel is a popular open-source PHP web application framework known for its elegant syntax and developer-friendly features.
This blog will walk you through a simple crud API with Laravel for a blog. Users will be able to register, log in, authenticate, create, read, update and delete blogs
Creating a CRUD REST API for a blog using Laravel is a multi-step process that involves setting up routes, controllers, models, and authentication. Here's a high-level overview of how you can achieve this:
Step 1: Set Up a New Laravel Project
Create a new Laravel project by running
composer create-project --prefer-dist laravel/laravel api
.Before we run the migrations the MYSQL database will need to be set up
CREATE DATABASE laravel_10; CREATE USER 'brian'@'localhost' IDENTIFIED BY 'MY_PASSWORD_123'; GRANT ALL PRIVILEGES ON laravel_10.* TO 'brian'@'localhost'; FLUSH PRIVILEGES;
This MySQL query sequence is used to create a new database for the app called
laravel_10
, create a MySQL userbrian@localhost
with a passwordMY_PASSWORD_123
, grant the user full privileges on the database and ensure the privileges take immediate effect.
Step 2: Create a Model and Migration
Create a model and migration for your blog users and their posts:
php artisan make:model Post -m php artisan make:model Wallet -m
In the migration file (
database/migrations/yyyy_mm_dd_create_posts_table.php
), define the columns for your blog posts (e.g., title, content, user_id, etc.) and run the migration usingphp artisan migrate
.
Step 3: Create Controllers
Create controllers for your CRUD operations:
php artisan make:controller PostController php artisan make:controller AuthController php artisan make:controller UserController
In the
PostController
, create methods for CRUD operations (show, store, update, delete).
Step 4: Define Routes
Define API routes for your CRUD operations in
routes/api.php
. For example:Route::prefix('/v2/blog/')->middleware(['auth'])->group(function () { Route::post('/post', [PostController::class, 'createPost']); Route::get('/blog/{id}', [PostController::class, 'getPost']); Route::patch('/update/{id}', [PostController::class, 'updatePost']); Route::delete('/remove/{id}', [PostController::class, 'deletePost']); }); Route::prefix('/v2/authorize')->middleware(['throttle'])->group(function () { Route::post('/login', [AuthController::class, 'login']); Route::post('/register', [AuthController::class, 'createAccount']); });
Step 5: Implement Authentication with Sanctum
Laravel Sanctum provides a featherweight authentication system for token-based APIs. To use Sanctum we need to use
HasApiTokens
Trait Class in User/Wallet Model.<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Sanctum\HasApiTokens; class Wallet extends Model { use HasApiTokens; protected $table = 'wallet'; protected $fillable = ['id']; }
Registering your Users
```sql public function register(Request $request) { try {
$params = json_decode($request->getContent()); if (empty($params->email) || !filter_var($params->email, FILTER_VALIDATE_EMAIL)) { return $this->errorResponse('Please provide a valid email', 400, false); }
if (empty($params->password)) { return $this->errorResponse('Password cannot be empty', 400, false); } //dd($params); $fetch = Wallet::query()->where('email', $params->email)->first(); if (!is_null($fetch)) { return $this->errorResponse('This account already exists. Please Register a different Email', 400, false);
} //hash password //there should be a backup of this incase the env file is missing this value $getSecret = empty(env('APP_KEY')) ? 'base64:UaY73KVgGOa99uFjacq29YLXZBqFGBund7YLahdDA5A=' : env('APP_KEY'); $passwordHash = hash("sha256", trim($params->password) . $getSecret); $username = uniqid().rand(100,9999); //save the details $user = new Wallet(); $user->email = $params->email; $user->password = $passwordHash; $user->wallet_id = md5(uniqid()); $user->username = $username; $user->save(); //Generate token $token = $user->createToken(env('APP_KEY'))->plainTextToken; //Return Token. Can store this in localstorage and use it to make request in headers return $this->successResponse(['username' => $username, 'email' => $params->email, 'token' => $token], 200, true);
} catch (Exception $e) { return $this->errorResponse($e->getMessage(), 400, false); } }
First, we validate the incoming request to make sure all required variables are present. Then we persist the supplied details into the database. Once a user has been created, we create a new personal access token for them using the `createToken()` method. Because `createToken()` will return an instance of `Laravel\Sanctum\NewAccessToken`, we call the `plainTextToken`property on the instance to access the plain-text value of the token. Finally, we return a JSON response containing the generated token as well as the type of the token.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1693645502976/fc0c250d-9417-46e5-a878-181c5153515c.png align="center")
3. **Restricting the Endpoint to Only Authenticated Users:**
Add your blog routes like below.
```php
Route::prefix('/v2/blog/')->middleware(['auth'])->group(function () {
Route::post('/post', [PostController::class, 'createPost']);
Route::get('/find/{id}', [PostController::class, 'getPost']);
Route::patch('/update/{id}', [PostController::class, 'updatePost']);
Route::delete('/remove/{id}', [PostController::class, 'deletePost']);
The provided code defines a set of routes in a Laravel application that are prefixed with '/v2/blog/' and are protected by the 'auth' middleware. Here's a brief explanation of what each part does:
Route::prefix('/v2/blog/')
: This line specifies that all the subsequent routes defined within the group will have a prefix of '/v2/blog/'. In other words, the URLs for these routes will start with '/v2/blog/'.->middleware(['auth:sanctum'])
: This middleware declaration means that all routes within this group are protected by Sanctum authentication middleware. It ensures that only authenticated users can access these routes. If a user is not authenticated, they will be redirected to the login page or receive a 401 Unauthorized response, depending on the context.group(function () { ... })
: This is a grouping mechanism in Laravel that allows you to define multiple routes with common properties, such as a prefix or middleware. All routes within the group share these common properties.
Inside the group, you have defined four routes:
Route::post('/post', [PostController::class, 'createPost']);
: This route is for creating a new blog post using the 'createPost' method from the 'PostController' class. It expects an HTTP POST request to '/v2/blog/post'.Route::get('/find/{id}', [PostController::class, 'getPost']);
: This route is for retrieving a specific blog post using the 'getPost' method from the 'PostController' class. It expects an HTTP GET request to '/v2/blog/blog/{id}', where '{id}' is a placeholder for the post's unique identifier.Route::patch('/update/{id}', [PostController::class, 'updatePost']);
: This route is for updating a specific blog post using the 'updatePost' method from the 'PostController' class. It expects an HTTP PATCH request to '/v2/blog/update/{id}', where '{id}' is a placeholder for the post's unique identifier.Route::delete('/remove/{id}', [PostController::class, 'deletePost']);
: This route is for deleting a specific blog post using the 'deletePost' method from the 'PostController' class. It expects an HTTP DELETE request to '/v2/blog/remove/{id}', where '{id}' is a placeholder for the post's unique identifier.
In summary, this code defines a group of routes related to managing blog posts under the '/v2/blog/' prefix, and all these routes require authentication before they can be accessed.
Step 6: Creating a blog
This PHP function creates a blog post in Laravel. It checks if essential fields (title, message, and photo URL) are empty and returns an error if they are. It inserts the new post into the database and responds with success. Any exceptions trigger an error response with the exception message.
```php public function createPost(Request $request) { try { $params = json_decode($request->getContent()); if (empty($params->title)) { return $this->errorResponse('Title cannot be empty', 400, false); } if (empty($params->blog_message)) { return $this->errorResponse('Message cannot be empty', 400, false); } if (empty($params->blog_photo_url)) { return $this->errorResponse('Photo URL cannot be empty', 400, false); } Post::insert([ 'title' => $params->title, 'blog_message' => $params->blog_message, 'photo_url' => $params->blog_photo_url,
]); return $this->successResponse('Inserted blog', 200, true); } catch (Exception $e) { return $this->errorResponse($e->getMessage(), 400, false); } }
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1693648654056/dd8b57d5-728f-4dad-8cb8-cbd7e2dd20d8.png align="center")
**Step 7: Reading a Blog**
1. Implement user permissions to restrict access to specific API endpoints. You can use policies or gates in Laravel for this purpose.
```php
public function getPost($id)
{
try {
$fetch = Post::query()->where('id', $id)->first();
if (is_null($fetch)) {
return $this->errorResponse('Cnnot find blog', 400, false);
}
return $this->successResponse($fetch, 200, true);
}catch (Exception $e){
return $this->errorResponse($e->getMessage(), 200, true);
}
}
Step 8: Updating a Blog
This PHP function updates a blog post in a Laravel app. It checks if required fields (id, title, message, and photo URL) are empty and returns an error if any are missing. It then attempts to fetch the post by 'id,' updating its details if found and returning a success response. In case of exceptions, it provides an error response with the exception message.
public function updatePost(Request $request)
{
try {
$params = json_decode($request->getContent());
if (empty($params->id)) {
return $this->errorResponse('Id cannot be empty', 400, false);
}
if (empty($params->title)) {
return $this->errorResponse('Title cannot be empty', 400, false);
}
if (empty($params->blog_message)) {
return $this->errorResponse('Message cannot be empty', 400, false);
}
if (empty($params->blog_photo_url)) {
return $this->errorResponse('Photo URL cannot be empty', 400, false);
}
$fetch = Post::query()->where('id', $params->id)->first();
if (is_null($fetch)) {
return $this->errorResponse('Cannot find blog', 400, false);
}
Post::query()->where('id', $params->id)->update(
['title' => $params->title,
'blog_message' => $params->blog_message,
'photo_url' => $params->blog_photo_url,]
);
return $this->successResponse('Successfully edited post', 200, true);
} catch (Exception $e) {
return $this->errorResponse($e->getMessage(), 200, true);
}
}
Step 9: Delete Post
In summary, this code attempts to retrieve a blog post by its 'id' from the database. If the post is found, it returns a success response with the post data; otherwise, it returns an error response indicating that the blog post cannot be found.
public function deletePost($id)
{
try {
$fetch = Post::query()->where('id', $id)->first();
if (is_null($fetch)) {
return $this->errorResponse('Cannot find blog', 400, false);
}
Post::query()->where('id', $id)->delete();
return $this->successResponse('Deleted blog '.$id, 200, true);
} catch (Exception $e) {
return $this->errorResponse($e->getMessage(), 200, true);
}
}
Conclusion
In this tutorial, we looked at what Laravel Sanctum is and what it does. Also, we looked at how it can be used to create CRUD APIs with Laravel. Finally, we covered how to use Laravel Sanctum to give access to users by creating tokens that can be used to authenticate and give access to users to consume Laravel APIs.
Also, note this is a high-level overview of creating a CRUD REST API for a blog using Laravel. The implementation details for each step can be extensive and may vary depending on your specific requirements. Be sure to consult the Laravel documentation and relevant package documentation for detailed instructions on each part of the process.
To learn more about Laravel Sanctum, check out the docs.
You can find the complete source code for this tutorial on GitHub.
Brian Kiplagat is a software developer and instructor. You can learn more about him here: