BAB 9: Responses

Mengirim berbagai jenis response: HTML, JSON, redirect, dan download file di Laravel.

Di bab sebelumnya kita membedah apa yang masuk ke aplikasi melalui objek Request. Sekarang saatnya melihat sisi yang lain: apa yang dikirim kembali ke browser atau klien API. Setiap method controller yang sudah kita tulis — dari index() yang mengembalikan view, hingga store() yang mengembalikan redirect — semuanya menghasilkan response. Bab ini membongkar semua jenis response yang tersedia di Laravel dan kapan masing-masing digunakan.

Jenis-jenis Response

Secara teknis, hampir semua yang kamu return dari controller atau route closure akan dikonversi oleh Laravel menjadi response HTTP. String menjadi response teks, array menjadi JSON, Eloquent model pun otomatis dikonversi ke JSON. Tapi untuk kontrol yang lebih detail, Laravel menyediakan helper response().

Response Teks Sederhana

// routes/web.php

// String — otomatis jadi response 200 dengan Content-Type text/html
Route::get('/ping', function () {
    return 'pong';
});

// Array — otomatis jadi JSON dengan Content-Type application/json
Route::get('/status', function () {
    return ['status' => 'ok', 'waktu' => now()->toIso8601String()];
});

Untuk mengontrol status code dan header secara eksplisit, gunakan helper response():

// routes/web.php

Route::get('/versi', function () {
    return response('Laravel 12', 200)
        ->header('Content-Type', 'text/plain')
        ->header('X-Versi-Api', '1.0');
});

Method header() bisa di-chain sepanjang yang dibutuhkan. Jika perlu menetapkan banyak header sekaligus, gunakan withHeaders():

return response($konten)
    ->withHeaders([
        'Content-Type'  => 'text/plain',
        'Cache-Control' => 'no-cache, no-store',
        'X-Aplikasi'    => 'Catatan App',
    ]);

Response JSON

Response JSON adalah pilihan utama untuk API. Gunakan response()->json() agar Laravel secara otomatis mengatur header Content-Type: application/json dan mengubah data ke format JSON:

<?php
// app/Http/Controllers/Api/CatatanController.php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Catatan;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class CatatanController extends Controller
{
    public function index(): JsonResponse
    {
        $catatan = Catatan::latest()->get();

        return response()->json([
            'data'  => $catatan,
            'total' => $catatan->count(),
        ]);
    }

    public function show(Catatan $catatan): JsonResponse
    {
        return response()->json([
            'data' => $catatan,
        ]);
    }

    public function store(Request $request): JsonResponse
    {
        $catatan = Catatan::create($request->only(['judul', 'isi']));

        // Status 201 Created untuk resource baru
        return response()->json([
            'pesan' => 'Catatan berhasil disimpan.',
            'data'  => $catatan,
        ], 201);
    }
}

Eloquent model bisa langsung di-return tanpa dibungkus json() — hasilnya sama. Namun membungkusnya di dalam json() memberikan fleksibilitas untuk menambahkan metadata seperti total atau pesan.

Untuk membangun API dengan konsistensi tinggi, pertimbangkan menggunakan API Resources Laravel — sebuah layer transformasi antara model Eloquent dan response JSON. Ini memungkinkan kamu mengontrol persis field apa yang tampil di API tanpa mengubah model.

Redirect

Redirect adalah response yang menginstruksikan browser untuk mengunjungi URL lain. Laravel menyediakan beberapa cara untuk membuat redirect tergantung tujuannya.

Redirect ke URL dan Named Route

<?php
// app/Http/Controllers/CatatanController.php

namespace App\Http\Controllers;

use App\Models\Catatan;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class CatatanController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        $catatan = Catatan::create($request->only(['judul', 'isi']));

        // Redirect ke named route dengan parameter
        return redirect()->route('catatan.show', $catatan);
    }

    public function destroy(Catatan $catatan): RedirectResponse
    {
        $catatan->delete();

        // Redirect ke named route tanpa parameter
        return redirect()->route('catatan.index');
    }
}

redirect()->route() lebih aman daripada hardcode URL karena mengikuti perubahan URL secara otomatis ketika nama route tidak berubah.

Redirect Kembali dengan Flash Data

Pola paling umum di aplikasi web adalah redirect kembali ke halaman sebelumnya setelah operasi — biasanya disertai pesan sukses atau error:

public function update(Request $request, Catatan $catatan): RedirectResponse
{
    $catatan->update($request->only(['judul', 'isi', 'prioritas']));

    // Redirect ke halaman sebelumnya dengan flash message
    return redirect()->back()
        ->with('sukses', 'Catatan berhasil diperbarui.');
}

public function store(Request $request): RedirectResponse
{
    // Jika ada error validasi manual
    if (empty($request->judul)) {
        return redirect()->back()
            ->withInput()                              // pertahankan nilai form
            ->withErrors(['judul' => 'Judul wajib diisi.']); // kirim error
    }

    Catatan::create($request->only(['judul', 'isi']));

    return redirect()->route('catatan.index')
        ->with('sukses', 'Catatan berhasil ditambahkan.');
}

Di Blade template, tampilkan flash message dari session:

{{-- resources/views/catatan/index.blade.php --}}

@if (session('sukses'))
    <div class="alert alert-success">
        {{ session('sukses') }}
    </div>
@endif

@if ($errors->any())
    <ul class="alert alert-danger">
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif

Redirect ke URL Eksternal

Gunakan redirect()->away() untuk redirect ke domain lain. Method ini tidak melakukan validasi atau encoding URL:

public function hubungi(): RedirectResponse
{
    return redirect()->away('https://github.com/abdasis');
}

Response View

Ketika controller perlu mengembalikan HTML, gunakan helper view() secara langsung atau melalui response()->view() jika perlu mengatur status code atau header:

<?php
// app/Http/Controllers/CatatanController.php

namespace App\Http\Controllers;

use App\Models\Catatan;
use Illuminate\Http\Response;
use Illuminate\View\View;

class CatatanController extends Controller
{
    public function index(): View
    {
        // Cara paling umum — return view langsung
        return view('catatan.index', [
            'catatanList' => Catatan::latest()->paginate(10),
        ]);
    }

    public function tidakDitemukan(): Response
    {
        // Perlu mengatur status code — gunakan response()->view()
        return response()
            ->view('errors.catatan-kosong', [], 404);
    }
}

Umumnya return view() sudah cukup. response()->view() hanya diperlukan ketika status code atau header perlu dikustomisasi.

Download dan Tampil File

Untuk mengunduh atau menampilkan file yang tersimpan di server, Laravel menyediakan dua method yang berbeda perilakunya di browser.

Download File

response()->download() menginstruksikan browser untuk mengunduh file, bukan membukanya:

<?php
// app/Http/Controllers/LampiranController.php

namespace App\Http\Controllers;

use App\Models\Catatan;
use Illuminate\Http\Response;

class LampiranController extends Controller
{
    public function unduh(Catatan $catatan): Response
    {
        // Path file di server
        $pathFile = storage_path('app/' . $catatan->path_lampiran);

        // Nama file yang tampil di dialog download browser
        $namaUnduhan = 'catatan-' . $catatan->id . '.pdf';

        return response()->download($pathFile, $namaUnduhan);
    }
}

Tampilkan File di Browser

response()->file() menginstruksikan browser untuk membuka file langsung — berguna untuk gambar, PDF, atau file teks yang ingin ditampilkan inline:

public function pratinjau(Catatan $catatan): Response
{
    $pathFile = storage_path('app/' . $catatan->path_lampiran);

    return response()->file($pathFile);
}

Pastikan file yang ingin diakses benar-benar ada sebelum memanggil download() atau file(). Jika file tidak ditemukan, Laravel akan melempar exception. Gunakan file_exists($pathFile) untuk mengeceknya terlebih dahulu.

Response Macro

Jika ada pola response yang berulang di banyak tempat, kamu bisa mendefinisikannya sekali sebagai macro. Ini berguna untuk memastikan format response API konsisten di seluruh aplikasi:

<?php
// app/Providers/AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Format sukses yang konsisten untuk semua API endpoint
        Response::macro('sukses', function (mixed $data, string $pesan = 'Berhasil', int $status = 200) {
            return Response::json([
                'sukses' => true,
                'pesan'  => $pesan,
                'data'   => $data,
            ], $status);
        });

        // Format error yang konsisten
        Response::macro('gagal', function (string $pesan, int $status = 400) {
            return Response::json([
                'sukses' => false,
                'pesan'  => $pesan,
                'data'   => null,
            ], $status);
        });
    }
}

Setelah macro didefinisikan, gunakan seperti method biasa:

// Di controller mana saja
public function show(Catatan $catatan)
{
    return response()->sukses($catatan, 'Catatan ditemukan.');
}

public function store(Request $request)
{
    if (empty($request->judul)) {
        return response()->gagal('Judul tidak boleh kosong.', 422);
    }

    $catatan = Catatan::create($request->only(['judul', 'isi']));

    return response()->sukses($catatan, 'Catatan berhasil disimpan.', 201);
}

Macro adalah cara sederhana membangun layer respons yang konsisten tanpa harus membuat class tambahan.

Latihan

Coba kerjakan latihan berikut untuk memantapkan pemahaman tentang responses:

  1. Response adaptif — Modifikasi CatatanController@index agar mengembalikan JSON jika request mengandung header Accept: application/json, dan view HTML untuk request browser biasa. Gunakan $request->expectsJson() yang sudah kita pelajari di Bab 8.

  2. Macro response API — Definisikan dua macro sukses dan gagal seperti contoh di atas. Refactor method store() dan destroy() di CatatanController agar menggunakan macro ini ketika $request->expectsJson() bernilai true.

  3. Download laporan — Buat endpoint GET /catatan/ekspor yang menghasilkan file teks berisi semua judul catatan (satu per baris) menggunakan response()->streamDownload(). Nama file yang diunduh harus catatan-{tanggal-hari-ini}.txt.

Penutup Bab

Response adalah kata akhir yang diucapkan aplikasi ke browser atau API client — formatnya menentukan apakah pengguna melihat halaman HTML, mendapat data JSON, diarahkan ke halaman lain, atau mengunduh sebuah file. Dengan memahami seluruh toolset response Laravel, kamu bisa memilih bentuk yang paling tepat untuk setiap situasi.

Kita sudah menyelesaikan layer HTTP secara penuh: routing menentukan ke mana request diarahkan, middleware memeriksa request sebelum masuk, controller memproses logika, request membawa data masuk, dan response membawa data keluar. Layer berikutnya adalah tampilan — bagaimana HTML yang dikirim ke browser dihasilkan. Sistem view Laravel yang akan kita bahas di Bab 10 adalah tempat logika presentasi tinggal, terpisah rapi dari logika controller yang baru saja kita kuasai.

Referensi

  1. 1HTTP Responses — Laravel 12.x Documentation
  2. 2HTTP Redirects — Laravel 12.x Documentation
  3. 3Eloquent: API Resources — Laravel 12.x Documentation