PHP code example of oybek-daniyarov / laravel-trpc

1. Go to this page and download the library: Download oybek-daniyarov/laravel-trpc library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

oybek-daniyarov / laravel-trpc example snippets


use Spatie\LaravelData\Data;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
class UserData extends Data
{
    public function __construct(
        public int $id,
        public string $name,
        public string $email,
    ) {}
}

#[TypeScript]
class CreateUserData extends Data
{
    public function __construct(
        public string $name,
        public string $email,
        public string $password,
    ) {}
}

use OybekDaniyarov\LaravelTrpc\Attributes\TypedRoute;

class UserController extends Controller
{
    #[TypedRoute(response: UserData::class, isPaginated: true)]
    public function index()
    {
        return UserData::collect(User::paginate());
    }

    #[TypedRoute(response: UserData::class)]
    public function show(User $user)
    {
        return UserData::from($user);
    }

    #[TypedRoute(request: CreateUserData::class, response: UserData::class)]
    public function store(CreateUserData $data)
    {
        return UserData::from(User::create($data->toArray()));
    }

    #[TypedRoute(request: UpdateUserData::class, response: UserData::class)]
    public function update(User $user, UpdateUserData $data)
    {
        $user->update($data->toArray());
        return UserData::from($user);
    }

    #[TypedRoute]
    public function destroy(User $user)
    {
        $user->delete();
        return response()->noContent();
    }
}

#[TypedRoute(response: UserData::class)]
public function show(User $user)
{
    return UserData::from($user);
}

#[TypedRoute(request: CreateUserData::class, response: UserData::class)]
public function store(CreateUserData $data)
{
    return UserData::from(User::create($data->toArray()));
}

#[TypedRoute(query: UserFilterData::class, response: UserData::class, isPaginated: true)]
public function index(UserFilterData $filters)
{
    return UserData::collect(
        User::filter($filters)->paginate()
    );
}

#[TypedRoute(response: UserData::class, isPaginated: true)]
public function index()
{
    return UserData::collect(User::paginate());
}
// TypeScript: PaginatedResponse<UserData>

#[TypedRoute(response: UserData::class, isCollection: true)]
public function all()
{
    return UserData::collect(User::all());
}
// TypeScript: Array<UserData>

#[TypedRoute]
public function destroy(User $user)
{
    $user->delete();
    return response()->noContent();
}
// TypeScript: void

#[TypedRoute(
    request: CreateUserData::class,
    response: UserData::class,
    errorResponse: CreateUserErrorData::class
)]
public function store(CreateUserData $data)
{
    // ...
}

// GET /api/users?status=active&sort=name
#[TypedRoute(query: UserFilterData::class, response: UserData::class, isPaginated: true)]
public function index(UserFilterData $filters) { }

// POST /api/users (body: { name: "John", email: "[email protected]" })
#[TypedRoute(request: CreateUserData::class, response: UserData::class)]
public function store(CreateUserData $data) { }

// config/trpc.php
return [
    // Output directory for generated files
    'output_path' => resource_path('js/api'),

    // API route prefix filter
    'api_prefix' => 'api',

    // Route collection mode: 'api', 'web', 'all', 'named', 'attributed'
    'route_mode' => 'api',

    // Exclude patterns
    'exclude_patterns' => [
        'debugbar.*',
        'horizon.*',
        'telescope.*',
    ],

    // Output files to generate
    'outputs' => [
        'routes' => true,
        'types' => true,
        'helpers' => true,
        'url-builder' => true,
        'fetch' => true,
        'client' => true,
        'index' => true,
        'readme' => true,
        'grouped-api' => true,
        'inertia' => true,
        'react-query' => false,
        'queries' => false,
        'mutations' => false,
    ],
];

// config/trpc.php
return [
    'preset' => 'spa', // 'inertia', 'api', or 'spa'
];

'postman' => [
    'output_path' => storage_path('app/postman'),
    'collection_name' => env('APP_NAME', 'API').' Collection',
    'base_url' => '{{base_url}}',
    'auth_type' => 'bearer', // 'bearer', 'apikey', or null
    'default_headers' => [],
],

'middleware' => [
    // Exclude middleware from generated output (supports wildcards)
    'exclude' => [
        'Stancl\Tenancy\*',
        'App\Http\Middleware\TrustProxies',
    ],

    // Transform FQCNs to short class names (default: true)
    'short_names' => true,
],

// Define a custom error type
#[TypeScript]
class CreateUserErrorData extends Data
{
    public function __construct(
        public string $message,
        public ?string $email_suggestion,  // Custom field
        public ?array $password_ validation fails, return custom error structure
    if (User::where('email', $data->email)->exists()) {
        return response()->json([
            'message' => 'Email already taken',
            'email_suggestion' => $data->email . '.new',
        ], 422);
    }

    return UserData::from(User::create($data->toArray()));
}

'outputs' => [
    'react-query' => true,  // Core utilities (queryKey, createQueryOptions)
    'queries' => true,      // Resource-based query hooks (usersQueries, etc.)
    'mutations' => true,    // Resource-based mutation hooks (usersMutations, etc.)
],
bash
php artisan vendor:publish --tag=trpc-config
bash
php artisan trpc:generate
typescript
import { routes, type RouteName } from '@/api';

function isAuthRequired(name: RouteName): boolean {
    return routes[name].authenticated;
}

// Use in components
function ApiButton({ route, children }: { route: RouteName; children: React.ReactNode }) {
    const { isAuthenticated } = useAuth();

    if (routes[route].authenticated && !isAuthenticated) {
        return <LoginPrompt />;
    }

    return <button onClick={() => callApi(route)}>{children}</button>;
}
typescript
import type { RequestOf, ResponseOf, ParamsOf, QueryOf, ErrorOf } from '@/api';

// Request body type
type CreateUserPayload = RequestOf<'users.store'>;

// Response type
type UserResponse = ResponseOf<'users.show'>;

// URL parameters type
type UserParams = ParamsOf<'users.show'>; // { user: number }

// Query parameters type
type UserQuery = QueryOf<'users.index'>; // { page?: number, per_page?: number }

// Error type (custom or ValidationError)
type StoreUserError = ErrorOf<'users.store'>;
typescript
import { useInfiniteQuery } from '@tanstack/react-query';
import { queries } from '@/lib/api';

function UserList() {
    const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery(
        queries.users.index({ query: { per_page: 20 } })
    );

    return (
        <div>
            {data?.pages.flatMap(page => page.data).map(user => (
                <div key={user.id}>{user.name}</div>
            ))}
            {hasNextPage && (
                <button
                    onClick={() => fetchNextPage()}
                    disabled={isFetchingNextPage}
                >
                    {isFetchingNextPage ? 'Loading...' : 'Load More'}
                </button>
            )}
        </div>
    );
}
bash
php artisan vendor:publish --tag=trpc-stubs