Laravel 9 Rest Api with Vue 3 Composition Api CRUD Example

In this section we will perform crud operations using laravel 9 rest api with vue 3 composition api. create will laravel backend api crud with vue 3 frontend crud using vue composition api.

Laravel 9 Backend Api Connect with Vue 3 Using Axios Example


Create Laravel 9 Rest Api CRUD

Install fresh laravel app and connect to database.

composer create-project laravel/laravel laravel-api


Create Post Api Model Migration Controller and Route

Run below command to create model and migration.

php artisan make:model Post -m 

create_posts_table.php

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->timestamps();
    });
}

migrate table

php artisan migrate


app/Models/Post.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;
    protected $fillable = [
        'title',
        'content'
    ];
}


Create Post Api Controller

run below command to create pots api controller.

 php artisan make:controller Api/PostController --model=Post


Add Logic Post Api crud in PostController.

app/Http/Controllers/Api/PostController.php

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $posts = Post::latest()->get();
        return response()->json([
            'success' => true,
            'message' => 'List data post',
            'data' => $posts
        ], 200);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required'
        ]);
        $posts = Post::create([
            'title' => $request->title,
            'content' => $request->content
        ]);

        return response()->json([
            'success' => true,
            'message' => 'Post created',
            'data' => $posts
        ], 201);
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function show(Post $post)
    {

        return response()->json([
            'success' => true,
            'message' => 'Detail data post',
            'data' => $post
        ], 200);
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function edit(Post $post)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Post $post)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required'
        ]);
        $post = Post::find($post->id);

        if ($post) {
            $post->update([
                'title' => $request->title,
                'content' => $request->content
            ]);

            return response()->json([
                'success' => true,
                'message' => 'Post updated',
                'data' => $post
            ], 200);
        }
        return response()->json([
            'success' => false,
            'message' => 'Post not found'
        ], 404);

    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function destroy(Post $post)
    {
        if ($post) {
            $post->delete();

            return response()->json([
                'success' => true,
                'message' => 'Post deleted'
            ], 200);
        }
        return response()->json([
            'success' => false,
            'message' => 'Post not found'
        ], 404);
    }
}

routes/api.php

Route::apiResource('posts',PostController::class);

Run the server

php artisan serve



Vue 3 Composition Api CRUD Using Laravel REST API

Create vue 3 project.

npm init [email protected]

Select your requirements.

Vue.js - The Progressive JavaScript Framework
 Project name:  frontend
 Add TypeScript?  No / Yes
 Add JSX Support?  No / Yes
 Add Vue Router for Single Page Application development?  No / Yes
 Add Pinia for state management?  No / Yes
 Add Vitest for Unit Testing?  No / Yes
 Add Cypress for both Unit and End-to-End testing?  No / Yes
 Add ESLint for code quality?  No / Yes
 Add Prettier for code formatting?  No / Yes
Scaffolding project in /vue-project/frontend...
Done. Now run:
 cd frontend
 npm install
npm run dev


Install axios In Vue 3

Run below command to install vue-axios

npm i vue-axios

Install Tailwind CSS in Vue 3

How to Install Tailwind CSS in Vue 3

or

Read Tailwind Official Doc

Set Axios Path in vue 3

Setup Api path in main.js using axios.

src/main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './index.css'
import axios from 'axios'

axios.defaults.baseURL = 'http://localhost:8000/api/';

const app = createApp(App)

app.use(router)

app.mount('#app')


Create Post (Index,Create,Edit) routes file

src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Posts from '../views/posts/Index.vue'
import PostCreate from '../views/posts/Create.vue'
import PostEdit from '../views/posts/Edit.vue'


const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/posts',
      name: 'posts',
      component: Posts
    },
    {
      path: '/posts/create',
      name: 'posts.create',
      component: PostCreate
    },
    {
      path: '/posts/:id',
      name: 'posts.edit',
      component: PostEdit
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('../views/AboutView.vue')
    }
  ]
})

export default router


Perform CRUD Operations Vue 3 Composition Api using Laravel Backend API

views/posts/Create.vue

<template>
  <div class="flex items-center justify-center h-screen">
    <div class="">
      <span v-if="validation.message" class="px-2 py-2 mb-4 text-red-600 rounded shadow">
        {{ validation.message }}
      </span>
      <form @submit.prevent="submit">
        <div>
          <label for="title">Title</label>
          <input
            type="text"
            name="title"
            v-model="post.title"
            class="w-full px-4 py-2 border border-gray-300 outline-none"
          />
        </div>
        <div>
          <label for="content">Content</label>
          <textarea
            name="content"
            v-model="post.content"
            class="w-full px-4 py-2 border border-gray-300 outline-none"
            cols="10"
            rows="4"
          ></textarea>
        </div>
        <button class="px-6 py-2 text-white bg-blue-600" type="submit">
          Button
        </button>
      </form>
    </div>
  </div>
</template>

<script>
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import axios from 'axios';

export default {
  setup() {
    const post = reactive({
      title: '',
      content: '',
    });

    const validation = ref([]);

    const router = useRouter();

    function submit() {
      let title = post.title;
      let content = post.content;

      axios
        .post('http://localhost:8000/api/posts', {
          title: title,
          content: content,
        })
        .then(() => {
          router.push({
            name: 'posts',
          });
        })
        .catch((error) => {
          validation.value = error.response.data;
        });
    }

    return {
      post,
      validation,
      router,
      submit,
    };
  },
};
</script>
laravel 9 rest api with vue 3 composition api create

laravel 9 rest api with vue 3 composition api create

views/posts/Index.vue

<template>
  <main class="container flex items-center justify-center h-screen mx-auto">
    <div class="flex flex-col">
      <div class="overflow-x-auto">
        <div class="p-1.5 w-full inline-block align-middle">
          <RouterLink to="/posts/create" class="p-2 text-white bg-blue-600"
            >Create</RouterLink
          >
          <div class="mt-3 overflow-hidden border rounded-lg">
            <table class="min-w-full divide-y divide-gray-200">
              <thead class="bg-gray-50">
                <tr>
                  <th
                    scope="col"
                    class="px-6 py-3 text-xs font-bold text-left text-gray-500 uppercase"
                  >
                    ID
                  </th>
                  <th
                    scope="col"
                    class="px-6 py-3 text-xs font-bold text-left text-gray-500 uppercase"
                  >
                    Title
                  </th>
                  <th
                    scope="col"
                    class="px-6 py-3 text-xs font-bold text-left text-gray-500 uppercase"
                  >
                    Edit
                  </th>
                  <th
                    scope="col"
                    class="px-6 py-3 text-xs font-bold text-center text-gray-500 uppercase"
                  >
                    Delete
                  </th>
                </tr>
              </thead>
              <tbody class="divide-y divide-gray-200">
                <tr v-for="post in posts" :key="post.id">
                  <td
                    class="px-6 py-4 text-sm font-medium text-gray-800 whitespace-nowrap"
                  >
                    {{ post.id }}
                  </td>
                  <td class="px-6 py-4 text-sm text-gray-800 whitespace-nowrap">
                    {{ post.title }}
                  </td>
                  <td class="px-6 py-4 text-sm text-gray-800 whitespace-nowrap">
                    <RouterLink
                      :to="{ name: 'posts.edit', params: { id: post.id } }"
                      class="text-green-500"
                      >EDIT</RouterLink
                    >
                  </td>
                  <td class="px-6 py-4 text-sm text-gray-800 whitespace-nowrap">
                    <button
                      @click.prevent="postDelete(post.id, index)"
                      class="text-red-600"
                    >
                      DELETE
                    </button>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </main>
</template>
<script>
import axios from 'axios';
import { onMounted, ref } from 'vue';
export default {
  setup() {
    let posts = ref([]);

    onMounted(() => {
      // fetch api from laravel backend
      axios
        .get('http://localhost:8000/api/posts')
        .then((res) => {
          posts.value = res.data.data;
        })
        .catch((error) => {
          console.log(error.res.data);
        });
    });
    function postDelete(id, index) {
      axios
        .delete(`http://localhost:8000/api/posts/${id}`)
        .then(() => {
          posts.value.splice(index, 1);
        })
        .catch((error) => {
          console.log(error.response.data);
        });
    }
    return {
      posts,
      postDelete,
    };
  },
};
</script>
laravel 9 rest api with vue 3 composition api index

laravel 9 rest api with vue 3 composition api index

views/posts/Edit.vue

<template>
  <div class="flex items-center justify-center h-screen">
    <div class="">
      <span
        v-if="validation.message"
        class="px-2 py-2 mb-4 text-red-600 rounded shadow"
      >
        {{ validation.message }}
      </span>
      <form @submit.prevent="submit">
        <div>
          <label for="title">Title</label>
          <input
            type="text"
            name="title"
            v-model="post.title"
            class="w-full px-4 py-2 border border-gray-300 outline-none"
          />
        </div>
        <div>
          <label for="content">Content</label>
          <textarea
            name="content"
            v-model="post.content"
            class="w-full px-4 py-2 border border-gray-300 outline-none"
            cols="10"
            rows="4"
          ></textarea>
        </div>
        <button class="px-6 py-2 text-white bg-blue-600" type="submit">
          Button
        </button>
      </form>
    </div>
  </div>
</template>
<script>
import { reactive, ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import axios from 'axios';
export default {
  setup() {
    const post = reactive({
      title: '',
      content: '',
    });
    const validation = ref([]);
   
    const router = useRouter();
    const route = useRoute();
    
    onMounted(() => {
      // fetch api from laravel backend
      axios
        .get(`http://localhost:8000/api/posts/${route.params.id}`)
        .then((response) => {
          post.title = response.data.data.title;
          post.content = response.data.data.content;
        })
        .catch((error) => {
          console.log(error.response.data);
        });
    });
    function submit() {
      let title = post.title;
      let content = post.content;
      axios
        .put(`http://localhost:8000/api/posts/${route.params.id}`, {
          title: title,
          content: content,
        })
        .then(() => {
          router.push({
            name: 'posts',
          });
        })
        .catch((error) => {
          validation.value = error.response.data;
        });
    }
    return {
      post,
      validation,
      router,
      submit,
    };
  },
};
</script>

Run vue server.

npm run dev