Building a CRUD API with Laravel and Sanctum
Learn how to create a robust, secure CRUD API using Laravel and Laravel Sanctum for authentication
Laravel is a powerful PHP framework that allows developers to easily build web applications and APIs.
In this guide, we'll walk through the process of creating a CRUD (Create, Read, Update, Delete) API using Laravel, and we'll implement authentication using Laravel Sanctum.
By the end of this tutorial, you'll have a fully functional API that allows authenticated users to perform CRUD operations on a resource. We'll use a 'Task' model as our example resource and implement the necessary endpoints to manage tasks.
Prerequisites
Before we begin, ensure you have the following:
- PHP 8.1 or higher installed on your system
- Composer for managing PHP dependencies
- A Neon account for database hosting
- Basic knowledge of Laravel and RESTful API principles
Setting up the Project
Let's start by creating a new Laravel project and setting up the necessary components.
Creating a New Laravel Project
Open your terminal and run the following command to create a new Laravel project:
This will create a new Laravel project in a directory named laravel-crud-api
and install all the necessary dependencies.
Setting up the Database
Update your .env
file with your Neon database credentials:
Make sure to replace your-neon-hostname
, your_database_name
, your_username
, and your_password
with your actual database credentials.
Installing Laravel Sanctum
Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs. To install it, all you need to do is use the following command:
If you get asked to run all pending migrations, type yes
and press Enter. Else, run the migrations to create the necessary tables:
This will create the necessary tables for Sanctum to work.
Creating the Task Model and Migration
As mentioned earlier, for our CRUD API, we'll use a 'Task' model as our example resource. This model will have fields such as title, description, status, due date, and priority.
Let's create it along with its migration file:
Once created, open the newly created migration file in database/migrations
and update it to include the necessary columns for the 'tasks' table:
We've defined a foreign key user_id
to associate each task with a user. This allows us to implement user-specific tasks and ensure that each task belongs to a specific user. The constrained()
method creates a foreign key constraint that references the id
column of the users
table. The onDelete('cascade')
method ensures that when a user is deleted, all associated tasks are also deleted.
Run the migration to create the 'tasks' table:
After that, update the app/Models/Task.php
model file to include the necessary fields in the $fillable
array:
As a reference, the fillable
property specifies which fields can be mass-assigned when creating or updating a model. This helps protect against mass assignment vulnerabilities and ensures that only the specified fields can be modified.
Implementing API Authentication
Before we create our CRUD endpoints, let's set up authentication using Laravel Sanctum. This will allow users to register, log in, and access protected routes in our API.
Creating the User Registration and Login Controllers
By default, Laravel comes with a few route groups like web
, api
, and auth
. We'll use the api
group for our API routes which will be protected by Sanctum.
Start by creating a controller called AuthController
to handle user registration and login using the artisan command:
Then, update the app/Http/Controllers/Api/AuthController.php
file and add the necessary methods:
Rundown of the methods in the AuthController
:
register
: Handles user registration. Validates the request data, creates a new user, and returns an access token.login
: Handles user login. Validates the request data, checks the user credentials, and returns an access token.logout
: Logs out the authenticated user by deleting the current access token.
Setting up Authentication Routes
Update routes/api.php
to include the authentication routes within the api
route group:
The auth:sanctum
middleware protects the routes by requiring a valid access token. This ensures that only authenticated users can access the protected routes.
The /register
and /login
routes are public and do not require authentication. Users can register and log in to obtain an access token.
Issuing API Tokens
To issue API tokens, we need to update the User
model to use the HasApiTokens
trait. Laravel ships with a default User
model located at app/Models/User.php
.
Let's update the app/Models/User.php
file and add the HasApiTokens
trait:
After adding the HasApiTokens
trait, you can use the createToken
method to generate an access token for a user. We've used this method in the AuthController
to issue tokens during registration and login.
While we're here, let's also update the User
model to include a relationship with the Task
model:
This relationship allows us to retrieve all tasks associated with a user and create new tasks for a user, simplifying the task management process.
Testing the Authentication Endpoints
To test the authentication endpoints, you can use a tool like Postman or Insomnia.
For the sake of simplicity, you can use the curl
command in your terminal.
Let's start by testing the registration route and try to register a new user:
Note: Replace
laravel-crud-api.test
with your Laravel project URL.
The response should include an access token like this:
To log in with the registered user:
This will return another access token:
With the access token, you can now access the protected routes. To log out, use the /logout
route:
Replace <your_access_token_here>
with the access token you received during login. This will log out the user and delete the access token and you will get a response like:
Implementing CRUD Operations
Now that we have authentication set up, let's create our CRUD operations for the Task model.
Creating the TaskController
Generate a new controller for handling task operations:
The --api
flag generates a controller with the necessary methods for a RESTful API. This will create a new controller file at app/Http/Controllers/Api/TaskController.php
and will include methods like index
, store
, show
, update
, and destroy
.
After that, update app/Http/Controllers/Api/TaskController.php
and populate those methods with the necessary logic:
Note: We'll create the
TaskResource
class later to transform the task model into a JSON response.
Rundown of the methods in the TaskController
:
index
: Fetches all tasks. Returns a JSON response with all tasks. We'll update this method to use API Resources later.store
: Creates a new task. Validates the request data, creates a new task, and returns the task as JSON.show
: Fetches a single task. Returns a JSON response with the specified task.update
: Updates a task. Validates the request data, updates the task, and returns the updated task as JSON.destroy
: Deletes a task. Deletes the specified task and returns a 204 No Content response.
Adding Task Routes
Once we have the TaskController
set up, let's add the task routes to routes/api.php
to include the task routes within the authenticated group:
The Route::apiResource
method automatically generates the necessary routes for a RESTful resource. This will create routes for tasks
with the appropriate HTTP verbs and route names.
You can use the php artisan route:list
command to see a list of all registered routes.
Implementing API Resources
To provide a consistent and customizable way of transforming our models into JSON responses, let's use Laravel's API Resources.
A resource class represents a single model that needs to be transformed into a JSON structure. It allows you to customize the data that is returned when a model is converted to JSON rather than returning the entire model instance.
Generate a new resource for the Task model:
Open the app/Http/Resources/TaskResource.php
file and update it as follows:
Here, we've defined the fields that should be included in the JSON response for a task and how they should be formatted. For more complex transformations, you can customize the toArray
method as needed.
Now, update the TaskController
to use this resource when returning task data instead of returning the raw model:
Now, when you fetch tasks, create a new task, or update an existing task, the response will be formatted according to the TaskResource
class.
Testing the API Endpoints
To test the new Task API, you can again use curl
or a tool like Postman or Insomnia.
Let's first create a new task:
As a response, you should see the newly created task:
You can then fetch all tasks:
This will return a list of tasks in JSON format.
Adding Pagination
To improve performance and reduce payload size, we can add pagination to the task list. Laravel provides a simple way to paginate query results using the paginate
method when fetching data. That way the response will include only a subset of tasks per page instead of the entire collection which can be large depending on the number of entries in the database.
To do that, update the index
method in TaskController
and change the all
method to paginate
followed by the number of items per page:
This pagination method will limit the number of tasks returned to 15 per page, significantly reducing the payload size for large datasets.
The response will now include additional pagination metadata such as the total number of items, the number of pages, and links to the next and previous pages, allowing for easy navigation through the entire collection of tasks.
Implementing Request Classes
Request classes allow you to encapsulate request validation logic within dedicated classes. This helps keep your controller clean and improves reusability.
To keep our controller clean and improve reusability, let's create dedicated request classes for validation for creating and updating tasks:
Update app/Http/Requests/StoreTaskRequest.php
to include the validation rules:
Update app/Http/Requests/UpdateTaskRequest.php
the same way:
Now, we are ready to update the TaskController
to use these request classes instead of validating the request directly in the controller methods:
The end-user will still receive the same JSON response, but the validation logic is now encapsulated within the request classes. This makes the controller cleaner and easier to maintain.
Testing the API
To ensure our API works as expected, Laravel provides a powerful testing suite out of the box.
To learn more about testing in Laravel along with Neon branding, check out the Testing Laravel Applications with Neon's Database Branching.
Adding API Documentation
For better developer experience, it's important to have good API documentation.
You can use a third-party package called Scribe to generate your API documentation.
To install Scribe, run the following command:
Publish the configuration file:
Then update the config/scribe.php
file to configure the documentation settings according to your preferences.
Generate the documentation:
This will create a public/docs
directory with the generated API documentation. You can access it by visiting http://laravel-crud-api.test/docs
. The generated format will also include Postman collections and OpenAPI specifications.
Implementing API Versioning
As your API evolves, you might need to introduce breaking changes. API versioning allows you to do this without affecting existing clients.
To implement a simple versioning strategy, you can prefix your API routes with a version number. This way, you can maintain backward compatibility while introducing new features in future versions.
To do that, create a new directory for v1 of your API:
Move your TaskController.php
to this new directory and update its namespace:
Then update your routes/api.php
file to include versioning and the new namespace:
Now, your API endpoints will be prefixed with /api/v1
. This allows you to introduce breaking changes in future versions without affecting existing clients.
Later on, you can create a new version (e.g., v2
) and update the routes accordingly to maintain backward compatibility.
Implementing Caching
To improve performance, especially for frequently accessed and rarely changing data, it is a good idea to implement caching for our task list.
This can significantly reduce the response time and server load and reduce the number of database queries putting less pressure on the database.
As an example, let's implement that for the index
method in TaskController
:
This caches the task list for one hour. Remember to clear the cache when tasks are updated, created, or deleted.
Conclusion
In this guide, we've walked through the process of building a simple CRUD API with Laravel, secured with Laravel Sanctum for authentication.
We've covered setting up the project, configuring the database with Neon, and implementing CRUD operations for a Task model. We also added essential features such as API versioning, API documentation, and caching to improve the API's performance, security, and maintainability.
By following these steps, you now have a fully functional API that allows authenticated users to manage tasks effectively. This can be used as the foundation for more complex applications and extended with additional features as needed.
As next steps you can think about adding more features to the API, such as search, filtering, sorting, and more advanced authentication and authorization mechanisms.
Additionally, it is a good idea to implement throttling to protect your API from abuse and to ensure fair usage.