Alaa Amer Articles

We offer a comprehensive collection of essential educational articles in web development to turn your ideas into digital reality

Laravel API Development: Building Professional REST & GraphQL APIs

Laravel 2026-01-01 Alaa Amer

Laravel API Development: Building Professional REST & GraphQL APIs

Expert Guide by Alaa Amer – Professional Web Developer & Applications Designer

Laravel provides powerful tools for API development. Master REST APIs, GraphQL implementation, authentication strategies, and enterprise-level API architecture patterns.

2️⃣ Advanced API Validation & Requests

Comprehensive API Request Validation:

<?php
// app/Http/Requests/Api/BaseApiRequest.php
namespace App\Http\Requests\Api;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;

class BaseApiRequest extends FormRequest
{
    protected function failedValidation(Validator $validator)
    {
        throw new HttpResponseException(
            response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
                'timestamp' => now()->toISOString()
            ], 422)
        );
    }

    protected function failedAuthorization()
    {
        throw new HttpResponseException(
            response()->json([
                'success' => false,
                'message' => 'This action is unauthorized',
                'timestamp' => now()->toISOString()
            ], 403)
        );
    }
}

// app/Http/Requests/Api/StorePostRequest.php
namespace App\Http\Requests\Api;

class StorePostRequest extends BaseApiRequest
{
    public function authorize(): bool
    {
        return auth()->check() && auth()->user()->can('create-posts');
    }

    public function rules(): array
    {
        return [
            'title' => 'required|string|min:5|max:255|unique:posts,title',
            'slug' => 'nullable|string|max:255|unique:posts,slug',
            'excerpt' => 'nullable|string|max:500',
            'content' => 'required|string|min:100',
            'status' => 'required|in:draft,published,scheduled',
            'published_at' => 'nullable|date|after:now',
            'category_id' => 'required|exists:categories,id',
            'tags' => 'nullable|array|max:10',
            'tags.*' => 'string|max:50',
            'featured_image' => 'nullable|image|mimes:jpeg,png,webp|max:2048',
            'settings' => 'nullable|array',
            'settings.allow_comments' => 'boolean',
            'settings.is_featured' => 'boolean',
            'meta_title' => 'nullable|string|max:60',
            'meta_description' => 'nullable|string|max:160'
        ];
    }

    public function messages(): array
    {
        return [
            'title.required' => 'Post title is required',
            'title.min' => 'Post title must be at least 5 characters',
            'title.unique' => 'A post with this title already exists',
            'content.required' => 'Post content is required',
            'content.min' => 'Post content must be at least 100 characters',
            'category_id.required' => 'Please select a category',
            'category_id.exists' => 'Selected category does not exist',
            'featured_image.image' => 'Featured image must be a valid image file',
            'featured_image.max' => 'Featured image size cannot exceed 2MB'
        ];
    }

    public function attributes(): array
    {
        return [
            'category_id' => 'category',
            'published_at' => 'publication date'
        ];
    }

    protected function prepareForValidation(): void
    {
        // Auto-generate slug if not provided
        if (!$this->has('slug') && $this->has('title')) {
            $this->merge([
                'slug' => \Str::slug($this->title)
            ]);
        }

        // Set default published_at for scheduled posts
        if ($this->status === 'scheduled' && !$this->has('published_at')) {
            $this->merge([
                'published_at' => now()->addHour()->toISOString()
            ]);
        }
    }

    public function withValidator($validator): void
    {
        $validator->after(function ($validator) {
            // Custom validation: scheduled posts must have future date
            if ($this->status === 'scheduled') {
                if (!$this->published_at || $this->published_at <= now()) {
                    $validator->errors()->add('published_at', 'Scheduled posts must have a future publication date');
                }
            }

            // Custom validation: featured posts need image
            if ($this->input('settings.is_featured') && !$this->hasFile('featured_image')) {
                $validator->errors()->add('featured_image', 'Featured posts require an image');
            }
        });
    }
}

// app/Rules/UniqueSlugForUser.php - Custom Validation Rule
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use App\Models\Post;

class UniqueSlugForUser implements Rule
{
    protected $userId;
    protected $excludeId;

    public function __construct($userId, $excludeId = null)
    {
        $this->userId = $userId;
        $this->excludeId = $excludeId;
    }

    public function passes($attribute, $value): bool
    {
        $query = Post::where('user_id', $this->userId)
                    ->where('slug', $value);

        if ($this->excludeId) {
            $query->where('id', '!=', $this->excludeId);
        }

        return !$query->exists();
    }

    public function message(): string
    {
        return 'You already have a post with this slug.';
    }
}

4️⃣ GraphQL API Implementation

Laravel GraphQL Setup:

<?php
// Install lighthouse-php/lighthouse
// composer require pusher/pusher-php-server lighthouse/lighthouse

// config/lighthouse.php
return [
    'route' => [
        'uri' => '/graphql',
        'middleware' => [
            'api',
            \Nuwave\Lighthouse\Support\Http\Middleware\AcceptJson::class,
        ],
        'prefix' => '',
        'name' => 'lighthouse',
    ],
    'schema' => [
        'register' => base_path('graphql/schema.graphql'),
    ],
    'namespaces' => [
        'models' => 'App\\Models',
        'queries' => 'App\\GraphQL\\Queries',
        'mutations' => 'App\\GraphQL\\Mutations',
    ],
];

// graphql/schema.graphql
"""
Main GraphQL Schema
"""

type Query {
    posts(
        first: Int = 15
        page: Int = 1
        orderBy: [PostOrderByClause!]
        filter: PostFilter
    ): PostPaginator! @paginate(defaultCount: 15, maxCount: 100)

    post(id: ID! @eq): Post @find

    categories: [Category!]! @all

    me: User @auth
}

type Mutation {
    createPost(input: CreatePostInput! @spread): Post
        @create
        @can(ability: "create", model: "App\\Models\\Post")

    updatePost(id: ID!, input: UpdatePostInput! @spread): Post
        @update
        @can(ability: "update", find: "id")

    deletePost(id: ID!): Post
        @delete
        @can(ability: "delete", find: "id")

    login(input: LoginInput! @spread): AuthPayload
        @field(resolver: "App\\GraphQL\\Mutations\\Auth@login")

    logout: LogoutResponse
        @field(resolver: "App\\GraphQL\\Mutations\\Auth@logout")
        @guard
}

type Post {
    id: ID!
    title: String!
    slug: String!
    excerpt: String
    content: String!
    status: PostStatus!
    published_at: DateTime
    created_at: DateTime!
    updated_at: DateTime!

    # Relationships
    author: User! @belongsTo(relation: "user")
    category: Category! @belongsTo
    tags: [Tag!]! @belongsToMany
    comments: [Comment!]! @hasMany

    # Computed fields
    reading_time: Int @method(name: "getReadingTimeAttribute")
    can_edit: Boolean @method(name: "userCanEdit")
}

type User {
    id: ID!
    name: String!
    email: String!
    avatar: String
    created_at: DateTime!

    posts: [Post!]! @hasMany
}

type Category {
    id: ID!
    name: String!
    slug: String!
    description: String
    posts_count: Int @count(relation: "posts")
}

enum PostStatus {
    DRAFT @enum(value: "draft")
    PUBLISHED @enum(value: "published")
    SCHEDULED @enum(value: "scheduled")
}

input PostFilter {
    title: String @where(operator: "like")
    status: PostStatus @eq
    category_id: ID @eq
    published_at: DateRange @whereBetween
}

input CreatePostInput {
    title: String! @rules(apply: ["required", "min:5", "max:255"])
    slug: String @rules(apply: ["nullable", "unique:posts,slug"])
    excerpt: String @rules(apply: ["nullable", "max:500"])
    content: String! @rules(apply: ["required", "min:100"])
    status: PostStatus! @rules(apply: ["required"])
    category_id: ID! @rules(apply: ["required", "exists:categories,id"])
    tags: [ID!] @rules(apply: ["nullable", "array"])
    published_at: DateTime
}

# GraphQL Mutations
// app/GraphQL/Mutations/Auth.php
namespace App\GraphQL\Mutations;

use App\Models\User;
use Illuminate\Support\Facades\Hash;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

class Auth
{
    public function login($root, array $args, GraphQLContext $context, ResolveInfo $info)
    {
        $credentials = $args['input'];

        $user = User::where('email', $credentials['email'])->first();

        if (!$user || !Hash::check($credentials['password'], $user->password)) {
            throw new \Exception('Invalid credentials');
        }

        if ($user->isLocked()) {
            throw new \Exception('Account temporarily locked');
        }

        $token = $user->createApiToken('GraphQL API');
        $user->recordSuccessfulLogin();

        return [
            'user' => $user,
            'access_token' => $token,
            'token_type' => 'Bearer'
        ];
    }

    public function logout($root, array $args, GraphQLContext $context, ResolveInfo $info)
    {
        $user = auth()->user();
        $user->currentAccessToken()->delete();

        return [
            'message' => 'Successfully logged out'
        ];
    }
}

Advanced GraphQL Features:

<?php
// app/GraphQL/Queries/PostQueries.php
namespace App\GraphQL\Queries;

use App\Models\Post;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

class PostQueries
{
    public function trending($root, array $args, GraphQLContext $context, ResolveInfo $info)
    {
        $days = $args['days'] ?? 7;

        return Post::published()
            ->where('published_at', '>=', now()->subDays($days))
            ->withCount(['comments', 'likes'])
            ->orderByDesc('likes_count')
            ->limit($args['limit'] ?? 10)
            ->get();
    }

    public function search($root, array $args, GraphQLContext $context, ResolveInfo $info)
    {
        $query = $args['query'];

        return Post::published()
            ->where(function ($q) use ($query) {
                $q->where('title', 'LIKE', "%{$query}%")
                  ->orWhere('content', 'LIKE', "%{$query}%")
                  ->orWhereFullText(['title', 'content'], $query);
            })
            ->with(['user', 'category'])
            ->paginate($args['first'] ?? 15);
    }
}

// app/GraphQL/Scalars/Upload.php - File Upload Support
namespace App\GraphQL\Scalars;

use GraphQL\Type\Definition\ScalarType;
use Illuminate\Http\UploadedFile;

class Upload extends ScalarType
{
    public function serialize($value)
    {
        return $value;
    }

    public function parseValue($value)
    {
        if ($value instanceof UploadedFile) {
            return $value;
        }

        throw new \Exception('Value must be an uploaded file');
    }

    public function parseLiteral($valueNode, array $variables = null)
    {
        throw new \Exception('Upload scalar can only parse variables');
    }
}

Next Steps

Learn comprehensive Testing in Laravel to ensure your APIs are bulletproof and maintainable.

📩 Need help with Laravel APIs?

Laravel API REST GraphQL API Authentication API Testing API Documentation Microservices
Article Category
Laravel

Laravel API Development: Building Professional REST & GraphQL APIs

Master Laravel API development with REST, GraphQL, authentication, versioning, documentation, testing, and performance optimization for enterprise applications.

Laravel API Development: Building Professional REST & GraphQL APIs
01

Consultation & Communication

Direct communication via WhatsApp or phone to understand your project needs precisely.

02

Planning & Scheduling

Creating clear work plan with specific timeline for each project phase.

03

Development & Coding

Building projects with latest technologies ensuring high performance and security.

04

Testing & Delivery

Comprehensive testing and thorough review before final project delivery.

Alaa Amer
Alaa Amer

Professional web developer with over 10 years of experience in building innovative digital solutions.

Need This Service?

Contact me now for a free consultation and quote

WhatsApp Your satisfaction is our ultimate goal

What We Offer

  • Website Maintenance & Updates

    Keep your website secure updated optimized

  • API Integration

    Connect your systems with powerful APIs

  • Database Design & Optimization

    Faster queries cleaner structure fewer issues

  • Website Security Hardening

    Protect your site from cyber threats

  • Automation & Scripts

    Automate repetitive tasks and save time

Have Questions?

Call Us Now

00201014714795