{"id":366,"date":"2025-08-27T20:38:20","date_gmt":"2025-08-27T20:38:20","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=366"},"modified":"2025-08-27T20:38:44","modified_gmt":"2025-08-27T20:38:44","slug":"building-a-mobile-app-backend-with-laravel-12-api","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/building-a-mobile-app-backend-with-laravel-12-api\/","title":{"rendered":"Building a Mobile App Backend with Laravel 12 API"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Building a Mobile App Backend with Laravel 12 API<\/strong><\/h2>\n\n\n\n<p>A mobile app needs a reliable backend to handle authentication, serve JSON data, sync user content, and enforce security. Laravel 12 provides everything you need to build a <strong>RESTful API<\/strong> that mobile apps (iOS\/Android) can consume easily. In this tutorial you\u2019ll create authentication endpoints, version your API, format JSON consistently, apply rate limiting, and prepare for push notifications.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 &#8211; Set Up Sanctum for Mobile Token Auth<\/strong><\/h2>\n\n\n\n<p>Mobile apps typically use <strong>token-based auth<\/strong>. Sanctum is lighter than Passport and perfect for mobile-first APIs.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">composer require laravel\/sanctum\n\nphp artisan vendor:publish --provider=<span class=\"hljs-string\">\"Laravel\\Sanctum\\SanctumServiceProvider\"<\/span>\n\nphp artisan migrate<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Publishing Sanctum migrations adds <code>personal_access_tokens<\/code>. Tokens issued here are tied to users, making mobile logins stateless and secure.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 &#8211; Auth Controller for Mobile<\/strong><\/h2>\n\n\n\n<p>Create endpoints for login, logout, and fetching the current user. These return JSON only (no views).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/Api\/MobileAuthController.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Api<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Controller<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Request<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Auth<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MobileAuthController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">login<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $credentials = $request-&gt;validate(&#91;\n            <span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-string\">'required|email'<\/span>,\n            <span class=\"hljs-string\">'password'<\/span> =&gt; <span class=\"hljs-string\">'required'<\/span>\n        ]);\n\n        <span class=\"hljs-keyword\">if<\/span> (! $token = Auth::attempt($credentials)) {\n            <span class=\"hljs-keyword\">return<\/span> response()-&gt;json(&#91;<span class=\"hljs-string\">'message'<\/span> =&gt; <span class=\"hljs-string\">'Invalid credentials'<\/span>], <span class=\"hljs-number\">401<\/span>);\n        }\n\n        $user = Auth::user();\n        $token = $user-&gt;createToken(<span class=\"hljs-string\">'mobile'<\/span>)-&gt;plainTextToken;\n\n        <span class=\"hljs-keyword\">return<\/span> response()-&gt;json(&#91;\n            <span class=\"hljs-string\">'token'<\/span> =&gt; $token,\n            <span class=\"hljs-string\">'user'<\/span>  =&gt; $user,\n        ]);\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">me<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> response()-&gt;json($request-&gt;user());\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">logout<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $request-&gt;user()-&gt;currentAccessToken()-&gt;delete();\n        <span class=\"hljs-keyword\">return<\/span> response()-&gt;json(&#91;<span class=\"hljs-string\">'message'<\/span> =&gt; <span class=\"hljs-string\">'Logged out'<\/span>]);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>login<\/code> validates credentials and issues a personal access token. The mobile client stores this token securely (Keychain\/Keystore). <code>me<\/code> lets apps fetch the logged-in user, while <code>logout<\/code> revokes the token.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 &#8211; API Versioning<\/strong><\/h2>\n\n\n\n<p>Versioning avoids breaking old mobile clients. Use URI prefixes like <code>\/api\/v1\/<\/code> and separate controllers by namespace.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/api.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Api<\/span>\\<span class=\"hljs-title\">MobileAuthController<\/span>;\n\nRoute::prefix(<span class=\"hljs-string\">'v1'<\/span>)-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    Route::post(<span class=\"hljs-string\">'\/login'<\/span>,  &#91;MobileAuthController::class, <span class=\"hljs-string\">'login'<\/span>]);\n    Route::post(<span class=\"hljs-string\">'\/logout'<\/span>, &#91;MobileAuthController::class, <span class=\"hljs-string\">'logout'<\/span>])-&gt;middleware(<span class=\"hljs-string\">'auth:sanctum'<\/span>);\n    Route::get(<span class=\"hljs-string\">'\/me'<\/span>,      &#91;MobileAuthController::class, <span class=\"hljs-string\">'me'<\/span>])-&gt;middleware(<span class=\"hljs-string\">'auth:sanctum'<\/span>);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Prefixing routes with <code>v1<\/code> ensures older apps keep working when you release <code>v2<\/code> with changes. Each version can have its own controllers\/resources.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 &#8211; Consistent JSON Responses<\/strong><\/h2>\n\n\n\n<p>Always return consistent shapes, so mobile devs can code against stable schemas. Wrap responses in a common format.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Resources\/UserResource.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Resources<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Resources<\/span>\\<span class=\"hljs-title\">Json<\/span>\\<span class=\"hljs-title\">JsonResource<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UserResource<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">JsonResource<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">toArray<\/span><span class=\"hljs-params\">($request)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> &#91;\n            <span class=\"hljs-string\">'id'<\/span>    =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;id,\n            <span class=\"hljs-string\">'name'<\/span>  =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;name,\n            <span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;email,\n        ];\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>UserResource<\/code> ensures consistent user JSON regardless of DB schema changes. Use resources for all entities returned to mobile clients.<\/p>\n\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Example in controller<\/span>\n<span class=\"hljs-keyword\">return<\/span> response()-&gt;json(&#91;\n    <span class=\"hljs-string\">'status'<\/span> =&gt; <span class=\"hljs-string\">'ok'<\/span>,\n    <span class=\"hljs-string\">'data'<\/span>   =&gt; <span class=\"hljs-keyword\">new<\/span> UserResource($request-&gt;user())\n]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Wrapping every response with <code>status<\/code> + <code>data<\/code> (and optionally <code>error<\/code>) gives mobile developers a predictable structure across endpoints.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 &#8211; Rate Limiting &amp; Security<\/strong><\/h2>\n\n\n\n<p>Mobile APIs are public-facing. Apply strict <code>throttle<\/code> limits to prevent brute force and abuse.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/api.php (snippet)<\/span>\nRoute::middleware(<span class=\"hljs-string\">'throttle:60,1'<\/span>)-&gt;prefix(<span class=\"hljs-string\">'v1'<\/span>)-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ all endpoints here are limited to 60 per minute per IP<\/span>\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Use the <code>throttle<\/code> middleware globally or per route group. Adjust per your scale. Combine with <code>auth:sanctum<\/code> so only authenticated clients can access sensitive endpoints.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 &#8211; Push Notifications Hook (Optional)<\/strong><\/h2>\n\n\n\n<p>Mobile apps often expect notifications. While Laravel doesn\u2019t send push directly, you can store device tokens and call FCM (Firebase) or APNs (Apple) APIs.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/migrations\/...create_device_tokens.php<\/span>\nSchema::create(<span class=\"hljs-string\">'device_tokens'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n    $table-&gt;id();\n    $table-&gt;foreignId(<span class=\"hljs-string\">'user_id'<\/span>)-&gt;constrained()-&gt;cascadeOnDelete();\n    $table-&gt;string(<span class=\"hljs-string\">'token'<\/span>)-&gt;unique(); <span class=\"hljs-comment\">\/\/ FCM\/APNs token<\/span>\n    $table-&gt;string(<span class=\"hljs-string\">'platform'<\/span>); <span class=\"hljs-comment\">\/\/ ios \/ android<\/span>\n    $table-&gt;timestamps();\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Saving device tokens per user allows you to target them with push via FCM\/APNs when certain backend events fire (new message, order shipped, etc.).<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>7 &#8211; Example Mobile Response<\/strong><\/h2>\n\n\n\n<p>Here\u2019s a sample JSON payload mobile clients would consume from a protected endpoint:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-attr\">\"status\"<\/span>: <span class=\"hljs-string\">\"ok\"<\/span>,\n  <span class=\"hljs-attr\">\"data\"<\/span>: {\n    <span class=\"hljs-attr\">\"user\"<\/span>: {\n      <span class=\"hljs-attr\">\"id\"<\/span>: <span class=\"hljs-number\">1<\/span>,\n      <span class=\"hljs-attr\">\"name\"<\/span>: <span class=\"hljs-string\">\"Alice\"<\/span>,\n      <span class=\"hljs-attr\">\"email\"<\/span>: <span class=\"hljs-string\">\"alice@example.com\"<\/span>\n    },\n    <span class=\"hljs-attr\">\"posts\"<\/span>: &#91;\n      {\n        <span class=\"hljs-attr\">\"id\"<\/span>: <span class=\"hljs-number\">101<\/span>,\n        <span class=\"hljs-attr\">\"title\"<\/span>: <span class=\"hljs-string\">\"Hello World\"<\/span>,\n        <span class=\"hljs-attr\">\"body\"<\/span>: <span class=\"hljs-string\">\"This is the first post...\"<\/span>\n      }\n    ]\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This structure is predictable, easy to parse, and extendable. Even when you add new fields, older mobile clients won\u2019t break if they ignore them.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>You built a mobile-ready backend: token auth with Sanctum, versioned endpoints, resource-based JSON, rate limiting, and optional push integration. This setup ensures your iOS\/Android apps can consume data securely and reliably. Stick to consistent JSON schemas and evolve with versioning when breaking changes are unavoidable.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/integrating-laravel-with-third-party-apis-mail-sms-payment\">Integrating Laravel with Third-Party APIs (Mail, SMS, Payment)<\/a><\/li>\n<li><a href=\"\/blog\/how-to-build-a-secure-file-upload-api-in-laravel\">How to Build a Secure File Upload API in Laravel<\/a><\/li>\n<li><a href=\"\/blog\/using-laravel-passport-for-advanced-api-authentication\">Using Laravel Passport for Advanced API Authentication<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Building a Mobile App Backend with Laravel 12 API A mobile app needs a reliable backend to handle authentication, serve JSON data, sync user content, and enforce security. Laravel 12 provides everything you need to build a RESTful API that mobile apps (iOS\/Android) can consume easily. In this tutorial you\u2019ll create authentication endpoints, version your [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":370,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[25,60,59],"class_list":["post-366","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-api","tag-backend","tag-mobile"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/366","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=366"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/366\/revisions"}],"predecessor-version":[{"id":369,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/366\/revisions\/369"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/370"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=366"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=366"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=366"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}