What is API?
API is a method where two or more software can communicate each other.
What is REST API
REST (Representational State Transfer) API is an architecture that allow server and client to transfer the representation of the state of the resource to the requester or endpoint. The data can be transferred using JSON, XML, or another format but the most popular option is JSON (JavaScript Object Notation). You can watch this video if you feel don't familiar with JSON format.
REST API Implementation with Laravel
Prerequisites
- PHP 8.0+
- Composer v2.0+
- Postman
Project Initialization
First, let initialize our Laravel project. This should automatically install the latest version of Laravel.
composer create-project laravel/laravel rest-api-implementation-laravel cd rest-api-implementation-laravel
Then ensure the server can be up and running:
php artisan serve
Then open http://127.0.0.1:8000, you should see this sweet Laravel welcome screen:
Setting Up Database
I want to make this tutorial to be as simple as possible, so we're going to use SQLite3 for our database connection.
First, install sqlite and create sqlite file:
# Change 8.1 according to your php version sudo apt install php8.1-sqlite3 sqlite3 touch database.sqlite
Open your .env file and change DB_CONNECTION to sqlite, then delete DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD:
DB_CONNECTION=sqlite
Creating Migrations and Model
We want to create model and migration for Developer table. Fortunately, Laravel provides easy way to make model and migrations with one single command:
php artisan make:model Developer --migration
Then, you will have two new files app/Models/Developer.php for the model and database/migrations/2022_03_18_090902_create_developers_table.php for the migrations. Note that the timestamp of migration file depends on when you execute the command.
Migrate table
<?php use IlluminateDatabaseMigrationsMigration; use IlluminateDatabaseSchemaBlueprint; use IlluminateSupportFacadesSchema; return new class extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('developers', function (Blueprint $table) { $table->id(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('developers'); } };
Short explanation:
-
up()method will be called when we run the migrations -
down()method will be called when we rollback the migrations -
Schema::dropIfExists('developers')will dropdeveloperstable from database if exists -
Schema::create('developers')will createdeveloperstable -
$table->id()will create incremental primary key with nameid -
$table->timestamps()will addcreated_atandupdated_atcolumns to the table
Supposed we want to add name and fav_lang columns with string type, we can modify the migration to be like this:
Schema::create('developers', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('fav_lang'); $table->timestamps(); });
Then, run the migration:
php artisan migrate
Creating Controller
To make simple controller for API, we can use default artisan command with --model and --api arguments:
php artisan make:controller DeveloperController --api --model=Developer
Here is our controller:
<?php namespace AppHttpControllers; use AppModelsDeveloper; use IlluminateHttpRequest; class DeveloperController extends Controller { /** * Display a listing of the resource. * * @return IlluminateHttpResponse */ public function index() { // } /** * Store a newly created resource in storage. * * @param IlluminateHttpRequest $request * @return IlluminateHttpResponse */ public function store(Request $request) { // } /** * Display the specified resource. * * @param AppModelsDeveloper $developer * @return IlluminateHttpResponse */ public function show(Developer $developer) { // } /** * Update the specified resource in storage. * * @param IlluminateHttpRequest $request * @param AppModelsDeveloper $developer * @return IlluminateHttpResponse */ public function update(Request $request, Developer $developer) { // } /** * Remove the specified resource from storage. * * @param AppModelsDeveloper $developer * @return IlluminateHttpResponse */ public function destroy(Developer $developer) { // } }
Look, our controller methods have $developer argument. Laravel will find record in database automatically for Developer table with Route model binding.
Setting Up Routes
Now, open routes/api.php and add DeveloperController to the route:
Route::apiResource('developers', DeveloperController::class);
Now check our route list:
php artisan route:list
Short explanations:
-
GETmethod forapi/developerswill be handled byindex()method inDeveloperController. This route is for display a listing of the resource. -
GETmethod forapi/developers/{developers}will be handled byshow()method inDeveloperController. This route is for display specific resource. -
POSTmethod forapi/developerswill be handled bystore()method inDeveloperController. This route is for creating new resource. -
PUT/PATCHmethod forapi/developers/{developers}will be handled byupdate()method inDeveloperController. This route is for updating specific resource. -
DELETEmethod forapi/developers/{developers}will be handled bydestroy()method inDeveloperController. This route is for deleting specific resource.
Handling Request
Creating Resource
When creating new resource, we need to take input name and fav_lang from user and put it to the database.
public function store(Request $request) { $developer = new Developer(); $developer->name = $request->input('name'); $developer->fav_lang = $request->input('fav_lang'); $developer->save(); return response()->json([ 'message' => 'Resource created' ]); }
Test Create Resource API
We're going to make POST request to http://localhost:8000/api/developers using Postman. First, we need to set Content-Type and Accept header to application/json to make sure we always receive JSON response from Laravel.
Now, we want to insert developer with name Zydhan Linnar Putra and his favourite languange which is C++:
Nice! We received response that we want.
Listing Resources
We only need to return all records in database using Developer::all():
public function index() { return Developer::all(); }
Test Resources Listing
To test this functionality, we should make GET request to http://localhost:8000/api/developers:
Showing Specific Resource
We only need to return $developer from method parameter:
public function show(Developer $developer) { return $developer; }
Test Display Specific Resource
We should make GET request to http://localhost:8000/api/developers/{resource_id}. According to resources listing the resource id is 1, so we need to test http://localhost:8000/api/developers/1:
Updating resources
Like resource creation, we also need to take input from user, and update it to database. But for updating, we don't need to create new Developer instance, we only need to update instance that given in method param:
public function update(Request $request, Developer $developer) { $developer->name = $request->input('name'); $developer->fav_lang = $request->input('fav_lang'); $developer->save(); return response()->json([ 'message' => 'Resource updated' ]); }
Test Update Specific Resource
Make a PUT request to http://localhost:8000/api/developers/1 with body structure resource creation, but in this case we want to change his name to Zydhan and make PHP as his favourite language:
Then, confirm if the resource is updated:
If you look at it, Laravel also automatically update updated_at column.
Delete resource
Eloquent provide delete() method to delete resource from table:
public function destroy(Developer $developer) { $developer->delete(); return response()->json([ 'message' => 'Resource deleted' ]); }
Test resource deletion
Method for this request is DELETE and we want to delete developer with name Zydhan which has ID of 1. So we make DELETE request to http://localhost:8000/api/developers/1:
Now if we access resource listing, we should receive empty array:
And if we try to access with resource ID, we got 404 not found:
Verdict
Laravel bring us ease of API development with eloquent, API controller, migration, etc. There are many room of improvements here, right now we don't have any authentication method so that random user could access our API and do whatever they want. Let me know if you want i make the tutorial to secure your API!
Bonus
API Resources
Typically, you don't want to return all column for listing resources, to achieve it, you can use API Resource to transform eloquent model before returning it.
First, create resource file:
php artisan make:resource DeveloperResource
We only want to return developer ID and its name, so we only put those columns to resource file:
public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name ]; }
Then, in DeveloperController@index, we should call resource file when returning it.
public function index() { return DeveloperResource::collection(Developer::all()); }
Now, it only return id and name. If we look closer, it also add data, that will be useful if you use pagination. You can read more about API resources here.
Input Validation
What if we don't fill all the input when creating resource? Let's try it:
Wow, we got Internal server error. We shouldn't return those error because the problem is from the user. Instead, we should validate data before doing any business logic. First, create request file:
php artisan make:request StoreDeveloperRequest
Now we have new file app/Http/Requests/StoreDeveloperRequest.php:
<?php namespace AppHttpRequests; use IlluminateFoundationHttpFormRequest; class StoreDeveloperRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return false; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ // ]; } }
Short explanation:
-
authorize()method is for checking whether it is authorized to execute that request. For now, set it totrueto authorize all request. -
rules()method is for validating input.
Now, we want to make name and fav_lang to be required. Let add it to the rules array:
public function rules() { return [ 'name' => 'required', 'fav_lang' => 'required' ]; }
Then, in the controller, substitute Request with StoreDeveloperRequest:
public function store(StoreDeveloperRequest $request) { $developer = new Developer(); $developer->name = $request->input('name'); $developer->fav_lang = $request->input('fav_lang'); $developer->save(); return response()->json([ 'message' => 'Resource created' ]); }
Test it again:
Now we got proper 422 – Unprocessable Content error and message that we must fill the name and fav_lang column. You can read more about another validation rules beside required here.



