Laravel vs. AdonisJs: Which should you use?

Nov 20, 20205 min read

Laravel and AdonisJs are both powerful MVC frameworks, but they serve different needs. Let's compare their features and help you choose the right one for your project.

Basic Setup

Laravel Setup

php
// routes/web.php
Route::get('/', function () {
    return view('welcome');
});

// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

class UserController extends Controller
{
    public function index()
    {
        $users = User::all();
        return view('users.index', compact('users'));
    }
}

AdonisJs Setup

typescript
// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'

Route.get('/', async () => {
  return view.render('welcome')
})

// app/Controllers/Http/UsersController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class UsersController {
  public async index({ view }: HttpContextContract) {
    const users = await User.all()
    return view.render('users/index', { users })
  }
}

Database Operations

Laravel Eloquent

php
// app/Models/User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $fillable = ['name', 'email'];
    
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

// Usage
$users = User::where('active', true)
    ->with('posts')
    ->get();

AdonisJs Lucid

typescript
// app/Models/User.ts
import { BaseModel, column, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import Post from 'App/Models/Post'

export default class User extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public name: string

  @column()
  public email: string

  @hasMany(() => Post)
  public posts: HasMany<typeof Post>
}

// Usage
const users = await User
  .query()
  .where('active', true)
  .preload('posts')

Authentication

Laravel Authentication

php
// config/auth.php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'sanctum',
        'provider' => 'users',
    ],
],

// Routes
Route::middleware('auth')->group(function () {
    Route::get('/dashboard', function () {
        return view('dashboard');
    });
});

AdonisJs Authentication

typescript
// config/auth.ts
import { AuthConfig } from '@ioc:Adonis/Addons/Auth'

const authConfig: AuthConfig = {
  guard: 'web',
  guards: {
    web: {
      driver: 'session',
      provider: {
        driver: 'lucid',
        model: () => import('App/Models/User')
      }
    },
    api: {
      driver: 'oat',
      provider: {
        driver: 'lucid',
        model: () => import('App/Models/User')
      }
    }
  }
}

// Routes
Route.group(() => {
  Route.get('dashboard', 'DashboardController.index')
}).middleware('auth')

Middleware

Laravel Middleware

php
// app/Http/Middleware/CheckRole.php
namespace App\Http\Middleware;

use Closure;

class CheckRole
{
    public function handle($request, Closure $next, $role)
    {
        if (!$request->user()->hasRole($role)) {
            return redirect('home');
        }
        return $next($request);
    }
}

// Usage
Route::get('/admin', function () {
    // ...
})->middleware('role:admin');

AdonisJs Middleware

typescript
// app/Middleware/CheckRole.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class CheckRole {
  public async handle(
    { auth, response }: HttpContextContract,
    next: () => Promise<void>,
    roles: string[]
  ) {
    if (!auth.user?.hasRole(roles)) {
      return response.redirect().back()
    }
    await next()
  }
}

// Usage
Route.get('admin', 'AdminController.index').middleware('checkRole:admin')

Form Validation

Laravel Validation

php
// app/Http/Requests/StoreUserRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8|confirmed',
        ];
    }
}

// Controller
public function store(StoreUserRequest $request)
{
    $validated = $request->validated();
    User::create($validated);
}

AdonisJs Validation

typescript
// app/Validators/CreateUser.ts
import { schema, rules } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class CreateUserValidator {
  constructor(protected ctx: HttpContextContract) {}

  public schema = schema.create({
    name: schema.string({ trim: true }, [
      rules.maxLength(255)
    ]),
    email: schema.string({ trim: true }, [
      rules.email(),
      rules.unique({ table: 'users', column: 'email' })
    ]),
    password: schema.string({}, [
      rules.minLength(8),
      rules.confirmed()
    ])
  })
}

// Controller
public async store({ request }: HttpContextContract) {
  const payload = await request.validate(CreateUserValidator)
  await User.create(payload)
}

Queue Processing

Laravel Queue

php
// app/Jobs/ProcessPodcast.php
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;

class ProcessPodcast implements ShouldQueue
{
    use Queueable, SerializesModels;

    public function handle()
    {
        // Process the job
    }
}

// Dispatch job
ProcessPodcast::dispatch();

AdonisJs Queue

typescript
// app/Jobs/ProcessPodcast.ts
import { JobContract } from '@ioc:Adeonis/Core/Queue'

export default class ProcessPodcast implements JobContract {
  public key = 'ProcessPodcast'

  public async handle() {
    // Process the job
  }
}

// Dispatch job
import Queue from '@ioc:Adonis/Core/Queue'
await Queue.dispatch(new ProcessPodcast())

Performance Comparison

Laravel

php
// Cache implementation
Cache::remember('users', 3600, function () {
    return DB::table('users')->get();
});

// Route caching
php artisan route:cache

AdonisJs

typescript
// Cache implementation
import Cache from '@ioc:Adonis/Addons/Cache'

await Cache.remember('users', 3600, async () => {
  return Database.from('users').select('*')
})

// Route caching is automatic in production

Framework-Specific Features

Laravel's Unique Features

php
// Blade Templates
@extends('layouts.app')

@section('content')
    @foreach($users as $user)
        {{ $user->name }}
    @endforeach
@endsection

// Artisan Commands
php artisan make:model User -mcr

AdonisJs's Unique Features

typescript
// Edge Templates
@layout('layouts/app')

@section('content')
  @each(user in users)
    {{ user.name }}
  @end
@end

// Ace Commands
node ace make:model User -m

Conclusion

Laravel Strengths:

  • Mature ecosystem
  • Extensive documentation
  • Large community
  • Rich package ecosystem
  • PHP's ubiquity

AdonisJs Strengths:

  • TypeScript first
  • Better performance
  • Modern architecture
  • Built-in features
  • Node.js ecosystem

Choose Laravel if:

  • You need extensive community support
  • Working with PHP hosting
  • Need many ready-made packages
  • Traditional web applications

Choose AdonisJs if:

  • You prefer TypeScript
  • Building APIs
  • Need better performance
  • Modern JavaScript ecosystem
  • Microservices architecture

For more information: