Tourify is a two-tier, cross-platform mobile tourism application. A React Native client built with Expo communicates exclusively over HTTP with a Laravel 13 REST API, which in turn persists data in a MySQL database. A Blade-based admin panel is served by the same Laravel application on the web routes, sharing all models and business logic with the API.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/astrxnomo/tourify/llms.txt
Use this file to discover all available pages before exploring further.
Mobile Client
Expo 54 / React Native 0.81.5 — runs on iOS, Android, and Web from a single codebase.
REST API
Laravel 13 with Sanctum 4.3 — stateless Bearer-token endpoints under
/api/*.Admin Panel
Blade web UI served on
/admin/* routes — session-authenticated, protected by an is_admin middleware.Database
MySQL — Eloquent ORM with polymorphic relationships for images, favorites, and reviews.
Request flow
Mobile app sends HTTP request
The Expo app calls a helper from
services/post.js (e.g. get('/cities')). The helper reads the Bearer token from SecureStore and attaches it as an Authorization header together with Accept: application/json.Laravel routes the request
Requests arriving at
/api/* are handled by routes/api.php. Public routes (city list, place list, etc.) need no token. Protected routes are wrapped in middleware('auth:sanctum'), which validates the Sanctum personal-access token before the controller runs.Controller processes and responds
API controllers under
app/Http/Controllers/ query Eloquent models, apply any business logic, and return JSON responses. Validation errors return a 422 with an errors map; other failures return an appropriate HTTP status with a message key.Admin panel flow
Requests to/admin/* are handled by routes/web.php with the web, auth, and is_admin middleware stack. Authentication uses Laravel’s session/cookie mechanism rather than Sanctum tokens. The Blade views under resources/views/admin/ render server-side HTML — no separate API calls are needed.
Authentication layers
| Context | Mechanism | Guard |
|---|---|---|
| Mobile API | Sanctum personal-access tokens (Bearer) | auth:sanctum |
| Admin panel | Laravel session + cookies | auth (web guard) |
POST /api/auth/login, the API returns a token string. The mobile app persists it via storage.setItem('auth_token', token) (SecureStore on native, localStorage on web). Every subsequent request reads that token and attaches it as Authorization: Bearer <token>.
The admin panel uses POST /admin/login which sets a session cookie. All admin routes require both the auth guard and the is_admin middleware, which checks that the authenticated user’s role is admin.
Push notifications
Request permission
usePushNotifications (called from Router.js) calls Notifications.requestPermissionsAsync(). On Android it also creates a default notification channel with MAX importance.Obtain Expo push token
Notifications.getExpoPushTokenAsync() returns an ExpoToken[...] string unique to the device/user pair.Register token with the API
The hook calls
POST /api/push-token with { token }. The NotificationController stores the value in users.push_token for the authenticated user.Admin triggers notification
From the admin panel (
POST /admin/notifications), an admin selects a recipient and composes a title + message. The AdminNotificationController calls the Expo Push HTTP API with the stored push_token, creates a Notification record in the database, and the device displays the alert.Push token registration is treated as non-critical: if
POST /api/push-token fails (e.g. the device is offline at startup), usePushNotifications silently swallows the error and the rest of the app continues to work normally.Directory structure
API route map
Auth routes (public + protected)
Auth routes (public + protected)
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /api/auth/register | — | Register a new user |
POST | /api/auth/login | — | Obtain a Sanctum token |
POST | /api/auth/forgot-password | — | Send password-reset email |
POST | /api/auth/logout | ✅ | Revoke current token |
GET | /api/auth/me | ✅ | Return authenticated user |
Public resource routes
Public resource routes
| Method | Path | Description |
|---|---|---|
GET | /api/cities | List all cities |
GET | /api/cities/{city} | Single city with places + events |
GET | /api/categories | List all categories |
GET | /api/categories/{category} | Single category with places |
GET | /api/places | List all places |
GET | /api/places/{place} | Single place with reviews + images |
GET | /api/events | List all events |
GET | /api/events/{event} | Single event with reviews + images |
Protected resource routes
Protected resource routes
| Method | Path | Description |
|---|---|---|
POST | /api/push-token | Save Expo push token for authenticated user |
GET | /api/favorites | List user’s favorites |
POST | /api/favorites | Add a favorite (polymorphic) |
DELETE | /api/favorites/{favorite} | Remove by ID |
DELETE | /api/favorites | Remove by morph type + ID |
POST | /api/places/{place}/reviews | Review a place |
POST | /api/events/{event}/reviews | Review an event |
GET | /api/my/registrations | List user’s event registrations |
POST | /api/events/{event}/register | Register for an event |
DELETE | /api/events/{event}/register | Cancel registration |
GET | /api/notifications | List user’s notifications |
PATCH | /api/notifications/read-all | Mark all notifications as read |
PATCH | /api/notifications/{notification}/read | Mark single notification as read |
Dependency versions
| Layer | Package | Version |
|---|---|---|
| Backend runtime | PHP | 8.3 |
| Backend framework | Laravel | 13 |
| API authentication | Laravel Sanctum | 4.3 |
| Mobile framework | Expo | ~54.0.33 |
| Mobile runtime | React Native | 0.81.5 |
| Navigation | React Navigation | 7 |
| Push notifications | expo-notifications | ~0.29.0 |
| Secure storage | expo-secure-store | ~15.0.8 |