The goal of the project is to create a template for development on Laravel and Nuxt with maximum API performance, ready-made authorization methods, image uploading with optimization and ready-made user roles.
- Laravel 13 and Nuxt 4
- Laravel Octane supercharges your application's performance by serving your application using high-powered application servers.
- Laravel Socialite OAuth providers
- Laravel Sail Light-weight command-line interface for interacting with Laravel's default Docker development environment.
- Spatie Laravel Permissions This package allows you to manage user permissions and roles in a database.
- Pest Elegant testing framework with expressive syntax and zero-config Laravel integration.
- UI library Nuxt UI 4 based on TailwindCSS 4 and Reka UI.
- Pinia The intuitive store for Vue.js
- Integrated pages: login, registration, password recovery, email confirmation, account information update, password change.
- Temporary uploads with cropping and optimization of images.
- Device management
- Enhanced Fetch Wrappers : Utilizes
$httpanduseHttp, which extend the capabilities of Nuxt's standard$fetchanduseFetch. - Monorepo layout (
apps/api+apps/web) withjusttask runner.
- PHP 8.4+ / Node 20+ (or Bun)
- Redis is required for the Throttling with Redis feature
- For Docker setup: Docker and just task runner
Show standalone instructions
cd apps/api && composer install && cd ../web && bun install && cd ../..cp apps/api/.env.example apps/api/.envcd apps/api && php artisan key:generate && php artisan storage:linkphp artisan migrate && php artisan db:seedphp artisan octane:install --server=swoole && php artisan octane:start --host=127.0.0.1 --port=8000- In another terminal:
cd apps/web && bun run dev
Single docker-compose.yml: API runs on Laravel Sail with Octane in watch mode, web runs on oven/bun:1, plus a redis:8-alpine service for cache / queue / session / throttling. Orchestrated via just. just sail ... wraps the upstream vendor/bin/sail for those who want it.
brew install just # macOS
apt install just # Debian / Ubuntu
winget install --id Casey.Just --exact # WindowsOther systems: see the full list of packages.
just up -d # start full app (api + web) — :8000 / :3000
just prod -d # production mode (Octane no-watch, web .output/)
just api -d # api only — :8000
just web # web foreground, ephemeral — :3000
just stop # pause containers
just down # remove containersQuick start:
just init # copies .env files, composer install, builds api image, bun install, key:generate, storage:link
just up -d
just a migrate --seedCommon commands:
just # show all recipes
just build # rebuild the api image
just a migrate # `php artisan migrate` in ephemeral container
just sail ... # invoke Laravel Sail directly (e.g. `just sail tinker`)
just composer require ...
just bun add @vueuse/core
just pint # PHP linter
just test # Pest
just down -v # stop and remove volumesTo integrate with the API, enhanced $http and useHttp wrappers are used, expanding the functionality of Nuxt's standard $fetch and useFetch. The $http wrapper includes custom interceptors to replace the originals:
onFetchinstead ofonRequestonFetchErrorinstead ofonRequestErroronFetchResponseinstead ofonResponseonFetchResponseErrorinstead ofonResponseError
Additionally, $http predefines a base url, authorization headers, and proxy IP for convenient API work in SSR mode.
For example, the code for authorizing a user by email and password:
<script lang="ts" setup>
const router = useRouter();
const auth = useAuthStore();
const form = templateRef("form");
const state = reactive({
email: "",
password: "",
remember: false,
});
const { refresh: onSubmit, status } = useHttp("login", {
method: "POST",
body: state,
immediate: false,
watch: false,
async onFetchResponse({ response }) {
if (response?.status === 422) {
form.value.setErrors(response._data?.errors);
} else if (response._data?.ok) {
await auth.login();
await router.push("/");
}
}
});
const loading = computed(() => status.value === "pending");
</script>
<template>
<UForm ref="form" :state="state" @submit="onSubmit" class="space-y-4">
<UFormField label="Email" name="email" required>
<UInput
v-model="state.email"
placeholder="you@example.com"
icon="i-heroicons-envelope"
trailing
type="email"
autofocus
/>
</UFormField>
<UFormField label="Password" name="password" required>
<UInput v-model="state.password" class="w-full" type="password" />
</UFormField>
<UTooltip :delay-duration="0" text="for 1 month" :content="{ side: 'right' }">
<UCheckbox v-model="state.remember" class="w-full" label="Remember me" />
</UTooltip>
<div class="flex items-center justify-end space-x-4">
<NuxtLink class="text-sm" to="/auth/forgot">Forgot your password?</NuxtLink>
<UButton type="submit" label="Login" :loading="loading" />
</div>
</UForm>
</template>In this example, a POST request will be made to the url "/api/v1/login"
useAuthStore() has everything you need to work with authorization.
Data returned by useAuthStore:
logged: Boolean, whether the user is authorizeduser: User object, user stored in pinia storefetchCsrf: Function, fetch csrf tokenfetchUser: Function, fetch user datalogin: Function, login userlogout: Function, remove local data and call API to remove sessionhasRole: Function, checks the role
The following middleware is supported:
guest: unauthorized usersauth: authorized usersverified: users who have confirmed their emailrole-user: users with the 'user' rolerole-admin: users with the 'admin' role
All built-in middleware from Laravel + middleware based on roles Spatie Laravel Permissions Middleware