Backend-Frontend with Laravel 8, Vue.js 3 and API for Blogs

Teguh Arief

Jun 05, 2023

Share article to

Laravel 8, Vue.js 3. Image: Logrocket.com.

This is basic programming for Full Stack Developer or Backend and Frontend using Laravel 8, Vue.js 3 and JSON API for Blogs.

Let's get started!

First we will set up a new project:


composer create-project laravel/laravel master --prefer-dist


Configure Laravel .env to connect database and install requirements e.g. Laravel Breeze.

Backend - API



Next, we will start the backend for API, starting with preparing the database.




use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->integer('id_member');
$table->string('title');
$table->text('content');
$table->enum('status', ['Draft', 'Published'])->default('Draft');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}



After that we will create Models and Controllers for the backend.

Blog Models





namespace App\Models;

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

class Post extends Model
{
use HasFactory;

protected $fillable = [
'id_member', 'title', 'content', 'status'
];
}



Blog Controllers





namespace App\Http\Controllers\api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Post;
use ErrorException;
use Illuminate\Database\QueryException;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use League\CommonMark\Node\Query;

class BlogController extends Controller
{
public function index()
{
try {
$blog = Post::findOrFail($id);
return response()->json($blog, Response::HTTP_OK);
} catch (QueryException $e) {
$error = [
'error' => $e->getMessage()
];
return response()->json($error, Response::HTTP_INTERNAL_SERVER_ERROR);
}
}

public function store(Request $request)
{
try {
$validator = validator::make($request->all(), [
'title' => 'required'
]);
if ($validator->fails()) {
return response()->json(['error' => $validator->errors()], Response::HTTP_UNPROCESSABLE_ENTITY);
}
Post::create($request->all());
$response = [
'Success' => 'New Blog Created',
];
return response()->json($response, Response::HTTP_CREATED);
} catch (QueryException $e) {
$error = [
'error' => $e->getMessage()
];
return response()->json($error, Response::HTTP_UNPROCESSABLE_ENTITY);
}
}

public function show($id)
{
try {
$blog = Post::findOrFail($id);
$response = $blog;
return response()->json($response, Response::HTTP_OK);
} catch (\Exception $e) {
return response()->json([
'error' => 'No result'
], Response::HTTP_FORBIDDEN);
}
}

public function update(Request $request, $id)
{
try {
$blog = Post::findOrFail($id);
$validator = Validator::make($request->all(), [
'title' => 'required'
]);
if ($validator->fails()) {
return response()->json(['succeed' => false, 'Message' => $validator->errors()], Response::HTTP_UNPROCESSABLE_ENTITY);
}
$blog->update($request->all());
$response = [
'Success' => 'Blog Updated'
];
return response()->json($response, Response::HTTP_OK);
} catch (\Exception $e) {
return response()->json([
'error' => 'no result',
], Response::HTTP_UNPROCESSABLE_ENTITY);
}
}

public function destroy($id)
{
try {
Post::findOrFail($id)->delete();
return response()->json(['success' => 'Blog deleted successfully.']);
} catch (\Exception $e) {
return response()->json([
'error' => 'No result'
], Response::HTTP_FORBIDDEN);
}
}
}



Blog Routers



And the last step for backend is setting up Routers.




use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\api\BlogController;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::apiResource('blog', BlogController::class);



Frontend - Vue.js 3



For the frontend we will use Vue.js 3.



npm install && npm run dev npm run watch



Then we need Vue Router as navigation inside the Vue.js application to be "Single Page Application (SPA)". And vue-loader, a webpack loader that supports defining Vue.js components in single files known as single-file components.



npm install vue@next vue-router@next vue-loader@next



App - Vue.js 3


resources/js/app.js



import './bootstrap';

import Alpine from 'alpinejs';

window.Alpine = Alpine;

Alpine.start();

import { createApp } from 'vue';
import router from './router'

createApp().use(router).mount('#app')



Router - Vue.js 3


resources/js/router/index.js



import { createRouter, createWebHistory } from 'vue-router'

import BlogIndex from '../components/blogs/BlogIndex.vue';
import BlogRead from '../components/blogs/BlogRead.vue';
import BlogCreate from '../components/blogs/BlogCreate.vue';
import BlogUpdate from '../components/blogs/BlogUpdate.vue';

const routes = [
{
path: '/dashboard',
name: 'blog.index',
component: BlogIndex
},
{
path: '/blog/:id',
name: 'blog.read',
component: BlogRead
},
{
path: '/blog/create',
name: 'blog.create',
component: BlogCreate
},
{
path: '/blog/edit/:id',
name: 'blog.update',
component: BlogUpdate
},
];

export default createRouter({
history: createWebHistory(),
routes
})



Lists - Vue.js 3


resources/js/components/blogs/BlogIndex.vue



<template>
    <div>
        <router-link :to="{ name: 'blog.create' }">+Create Blog</router-link>
    </div>
 
    <div>
        <table>
            <thead>
            <tr>
                <th>
                    <span>Title</span>
                </th>
                <th>
                    <span>Status</span>
                </th>
                <th>
                    <span>Action</span>
                </th>
            </tr>
            </thead>
 
            <tbody>
            <template v-for="blog in blogs" :key="blog.id">
                <tr>
                    <td>
                        <router-link :to="'/blog/'+blog.id">{{ blog.title }}</router-link>
                    </td>
                    <td>
                        {{ blog.status }}
                    </td>
<td>
<router-link :to="'/blog/edit/'+blog.id">Edit</router-link>&nbsp;
<button v-on:click="deleteData(blog.id)">Delete</button>
</td>
                </tr>
            </template>
            </tbody>
        </table>
    </div>
</template>
 
<script>
export default {
  data() {
    return {
      blogs: []
    };
  },
  created() {
    this.loadData();
  },
  methods: {
    loadData() {
      axios.get("http://localhost:8000/api/blog").then(response => {
        this.blogs = response.data;
      });
    },
    deleteData(id) {
      axios.delete("http://localhost:8000/api/blog/" + id).then(response => {
        this.loadData();
      });
    }
  }
};
</script>



Detail - Vue.js 3


resources/js/components/blogs/BlogRead.vue



<template>
    <h2>{{ form.title }}</h2>
    <p>{{ form.content }}</p>
</template>
 
<script>
export default {
  data() {
    return {
      form: {
        title: '',
        content: ''
      }
    };
  },
  created() {
    this.loadData();
  },
  methods: {
    loadData() {
      axios
        .get('http://localhost:8000/api/blog/' + this.§route.params.id)
        .then(response => {
          this.form.title = response.data.title;
          this.form.content = response.data.content;
        });
    }
  }
};
</script>



Create - Vue.js 3


resources/js/components/blogs/BlogCreate.vue



<template>
  <div>
    <div class="row">
      <div class="col-md-6">
        <h4>Create new data</h4>
        <br>
        <form @submit.prevent="addData()">
          <div class="form-group">
            <label>Title</label><br>
            <input type="text" v-model="form.nameTitle" required>
          </div>
          <div class="form-group">
            <label>Content</label><br>
            <textarea v-model="form.nameContent" required></textarea>
          </div>
          <button>Submit</button>
        </form>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data(){
    return{
      form:{
        nameTitle: '',
        nameContent: ''
      }
    }
  },
  methods: {
    addData() {
      axios
        .post("http://localhost:8000/api/blog", {
          title: this.form.nameTitle,
          content: this.form.nameContent
        })
        .then(response => {
          this.§router.push("/dashboard");
        });
    }
  }
};
</script>



Update - Vue.js 3


resources/js/components/blogs/BlogUpdate.vue



<template>
    <h4>Edit</h4>
    <br>
    <form @submit.prevent="updateData()">
  <div class="form-group">
<label>Title</label><br>
<input type="text" v-model="form.title" required>
  </div>
  <div class="form-group">
<label>Content</label><br>
<textarea v-model="form.content" required></textarea>
  </div>
  <button>Update</button>
</form>
</template>
 
<script>
export default {
  data() {
    return {
      form: {
        title: '',
        content: ''
      }
    };
  },
  created() {
    this.loadData();
  },
  methods: {
    loadData() {
      axios
        .get('http://localhost:8000/api/blog/' + this.§route.params.id)
        .then(response => {
          this.form.title = response.data.title;
          this.form.content = response.data.content;
        });
    },
    updateData() {
      axios
        .put('http://localhost:8000/api/blog/' + this.§route.params.id, {
          title: this.form.title,
          content: this.form.content
        })
        .then(response => {
          this.§router.push('/dashboard');
        });
    }
  }
};
</script>



After the steps above have been done we will try it on localhost.



php artisan serve



And open a browser for http://localhost:8000/ about the blog.

Related Posts