<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jorge Peraza</title>
    <description>The latest articles on DEV Community by Jorge Peraza (@yorchperaza).</description>
    <link>https://dev.to/yorchperaza</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F523500%2Fe23972d0-c7aa-4f92-93ef-d57affd10e5f.jpeg</url>
      <title>DEV Community: Jorge Peraza</title>
      <link>https://dev.to/yorchperaza</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yorchperaza"/>
    <language>en</language>
    <item>
      <title>Authentication with MonkeysLegion 2.0 + Next.js / React</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Fri, 08 May 2026 02:58:20 +0000</pubDate>
      <link>https://dev.to/yorchperaza/authentication-with-monkeyslegion-20-nextjs-react-2en4</link>
      <guid>https://dev.to/yorchperaza/authentication-with-monkeyslegion-20-nextjs-react-2en4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A complete guide to implementing JWT-based registration, login, session persistence, and token refresh using the MonkeysLegion v2 API with a Next.js (or React) frontend.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;API Endpoints Overview&lt;/li&gt;
&lt;li&gt;Backend Setup (MonkeysLegion)&lt;/li&gt;
&lt;li&gt;Frontend: API Client&lt;/li&gt;
&lt;li&gt;Frontend: Auth Context Provider&lt;/li&gt;
&lt;li&gt;Frontend: Register Page&lt;/li&gt;
&lt;li&gt;Frontend: Login Page&lt;/li&gt;
&lt;li&gt;Frontend: Protected Routes&lt;/li&gt;
&lt;li&gt;Token Refresh Flow&lt;/li&gt;
&lt;li&gt;Common Issues &amp;amp; Solutions&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  API Endpoints Overview
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion v2 exposes the following auth endpoints under the &lt;code&gt;/api/v2/auth&lt;/code&gt; prefix:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Auth&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/auth/register&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Create a new user + company&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/auth/login&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Authenticate and get JWT tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/auth/refresh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Exchange refresh token for new access token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/auth/me&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Get authenticated user profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/auth/logout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Invalidate tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/auth/forgot-password&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Request password reset&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Response Shapes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Login Response&lt;/strong&gt; — &lt;code&gt;POST /auth/login&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJ0eXAiOiJKV1Q..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJ0eXAiOiJKV1Q..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"full_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Register Response&lt;/strong&gt; — &lt;code&gt;POST /auth/register&lt;/code&gt; (HTTP 201)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Registration successful"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"full_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme Inc."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJ0eXAiOiJKV1Q..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJ0eXAiOiJKV1Q..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Me Response&lt;/strong&gt; — &lt;code&gt;GET /auth/me&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"full_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"verified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"two_factor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-08T01:25:23+00:00"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Backend Setup (MonkeysLegion)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install the Framework
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require monkeyscloud/monkeyslegion:^2.0.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Version &lt;code&gt;2.0.8+&lt;/code&gt; is required. Earlier versions have a bug in &lt;code&gt;DatabaseUserProvider&lt;/code&gt; where typed properties (&lt;code&gt;DateTimeImmutable&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;) throw &lt;code&gt;TypeError&lt;/code&gt; during hydration from raw PDO strings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Entity — &lt;code&gt;app/Entity/User.php&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Auth\Contract\AuthenticatableInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;MonkeysLegion\Query\Attribute\&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Timestamps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Hidden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Fillable&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="na"&gt;#[Entity(table: 'users')]&lt;/span&gt;
&lt;span class="na"&gt;#[Timestamps]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;AuthenticatableInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;#[Id]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="na"&gt;#[Field(type: 'string', length: 255, unique: true)]&lt;/span&gt;
    &lt;span class="na"&gt;#[Fillable]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="na"&gt;#[Field(type: 'string', length: 255)]&lt;/span&gt;
    &lt;span class="na"&gt;#[Fillable]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$full_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="na"&gt;#[Field(type: 'string', length: 255)]&lt;/span&gt;
    &lt;span class="na"&gt;#[Hidden]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$password_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="na"&gt;#[Field(type: 'integer', default: 1)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$token_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="na"&gt;#[Field(type: 'datetime')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;\DateTimeImmutable&lt;/span&gt; &lt;span class="nv"&gt;$created_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="na"&gt;#[Field(type: 'datetime')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;\DateTimeImmutable&lt;/span&gt; &lt;span class="nv"&gt;$updated_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ── AuthenticatableInterface ──&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getAuthIdentifier&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getAuthPassword&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Controller — &lt;code&gt;app/Controller/Api/AuthController.php&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Controller\Api&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Service\AuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;MonkeysLegion\Http\Attribute\&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Throttle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Http\Message\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Http\Message\ServerRequestInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;#[Prefix('/api/v2/auth')]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;AuthService&lt;/span&gt; &lt;span class="nv"&gt;$auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="na"&gt;#[Route('POST', '/login', name: 'auth.login')]&lt;/span&gt;
    &lt;span class="na"&gt;#[Throttle(max: 5, per: 60)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ServerRequestInterface&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Missing credentials'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Invalid credentials'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'access_token'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'refresh_token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'refresh_token'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'token_type'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'expires_in'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expires_in'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'user'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'id'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'email'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'full_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="na"&gt;#[Route('POST', '/register', name: 'auth.register')]&lt;/span&gt;
    &lt;span class="na"&gt;#[Throttle(max: 3, per: 60)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ServerRequestInterface&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Missing required fields'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Check for duplicate email&lt;/span&gt;
        &lt;span class="nv"&gt;$existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;findByEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$existing&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'error'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Validation failed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'details'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Email already registered'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'message'&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Registration successful'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'user'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'id'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'email'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'full_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'access_token'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'refresh_token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'refresh_token'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'token_type'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'expires_in'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expires_in'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="na"&gt;#[Route('GET', '/me', name: 'auth.me')]&lt;/span&gt;
    &lt;span class="na"&gt;#[Authenticated]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ServerRequestInterface&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// NOTE: The attribute is 'auth.user', NOT 'user'&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'auth.user'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'id'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'email'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'full_name'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'status'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always use &lt;code&gt;$request-&amp;gt;getAttribute('auth.user')&lt;/code&gt; — the &lt;code&gt;AuthenticationMiddleware&lt;/code&gt; sets the attribute as &lt;code&gt;auth.user&lt;/code&gt;, not &lt;code&gt;user&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Frontend: API Client
&lt;/h2&gt;

&lt;p&gt;Create a reusable API client that handles JWT tokens, automatic refresh, and typed requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/lib/api.ts&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_API_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:8088&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ApiOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;method&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;token&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApiOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Auto-refresh on 401&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refreshed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refreshed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/auth/refresh`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;clearTokens&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ── Auth Methods ──────────────────────────────────────────&lt;/span&gt;
  &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/auth/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;companyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/auth/register&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;companyName&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Frontend: Auth Context Provider
&lt;/h2&gt;

&lt;p&gt;Wrap your app with &lt;code&gt;AuthProvider&lt;/code&gt; to share auth state across all components.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/lib/auth.tsx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;register&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;companyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AuthContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthState&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// On mount: validate stored token by calling /me&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;api&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/auth/me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Map full_name → name for frontend consistency&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full_name&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;companyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;companyName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full_name&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mm_refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;useAuth must be used within AuthProvider&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The API returns &lt;code&gt;full_name&lt;/code&gt; but we map it to &lt;code&gt;name&lt;/code&gt; in the frontend &lt;code&gt;User&lt;/code&gt; interface for simplicity. This mapping happens in three places: &lt;code&gt;login&lt;/code&gt;, &lt;code&gt;register&lt;/code&gt;, and the &lt;code&gt;/me&lt;/code&gt; response handler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Wire Into Layout — &lt;code&gt;src/app/layout.tsx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Frontend: Register Page
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/app/(auth)/register/page.tsx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RegisterPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;companyName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCompanyName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;companyName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Redirect to login after registration&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Registration failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Create Account&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;     &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;        &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Full name"&lt;/span&gt;    &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;       &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;       &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;        &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;    &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;    &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;     &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;     &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;companyName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCompanyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Company name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Creating...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create Account&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Already have an account? &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign in&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Frontend: Login Page
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/app/(auth)/login/page.tsx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LoginPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Redirect to dashboard&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome Back&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;    &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;    &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;    &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Signing in...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sign in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Don't have an account? &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/register"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Create one&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Frontend: Protected Routes
&lt;/h2&gt;

&lt;p&gt;Wrap dashboard pages with a layout that redirects unauthenticated users.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/app/(dashboard)/layout.tsx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the User in Components
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProfileCard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign Out&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Token Refresh Flow
&lt;/h2&gt;

&lt;p&gt;The API client handles token refresh &lt;strong&gt;automatically&lt;/strong&gt;. Here's the sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser makes a request (e.g. &lt;code&gt;GET /api/v2/messages&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;API Client attaches the stored &lt;code&gt;Bearer&lt;/code&gt; token&lt;/li&gt;
&lt;li&gt;If the API returns &lt;code&gt;401 Unauthorized&lt;/code&gt; (token expired):

&lt;ul&gt;
&lt;li&gt;Client sends &lt;code&gt;POST /api/v2/auth/refresh&lt;/code&gt; with the refresh token&lt;/li&gt;
&lt;li&gt;On success: stores the new tokens and &lt;strong&gt;retries the original request&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;On failure: clears all tokens and redirects to &lt;code&gt;/login&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Token Lifetimes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token&lt;/th&gt;
&lt;th&gt;Lifetime&lt;/th&gt;
&lt;th&gt;Storage Key&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Access Token&lt;/td&gt;
&lt;td&gt;30 minutes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;localStorage("mm_token")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refresh Token&lt;/td&gt;
&lt;td&gt;7 days&lt;/td&gt;
&lt;td&gt;&lt;code&gt;localStorage("mm_refresh")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; If both tokens are expired (user inactive for 7+ days), the client clears storage and redirects to &lt;code&gt;/login&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Common Issues &amp;amp; Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;TypeError: Cannot assign string to property ... of type DateTimeImmutable&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; &lt;code&gt;DatabaseUserProvider&lt;/code&gt; in MonkeysLegion ≤ 2.0.7 does raw assignment from PDO strings to typed properties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Upgrade to &lt;code&gt;monkeyscloud/monkeyslegion:^2.0.8&lt;/code&gt; which includes &lt;code&gt;castValue()&lt;/code&gt; type coercion in the hydrator.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. &lt;code&gt;/me&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt; user — &lt;code&gt;$request-&amp;gt;getAttribute('user')&lt;/code&gt; is null
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The &lt;code&gt;AuthenticationMiddleware&lt;/code&gt; sets the attribute as &lt;code&gt;auth.user&lt;/code&gt;, not &lt;code&gt;user&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- $user = $request-&amp;gt;getAttribute('user');
&lt;/span&gt;&lt;span class="gi"&gt;+ $user = $request-&amp;gt;getAttribute('auth.user');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. &lt;code&gt;422 Unprocessable Content&lt;/code&gt; on register
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The email is already registered. The API returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validation failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email already registered"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use a different email, or parse the error details in the frontend to show a specific message.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. &lt;code&gt;full_name&lt;/code&gt; vs &lt;code&gt;name&lt;/code&gt; mismatch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The API returns &lt;code&gt;full_name&lt;/code&gt; but your frontend &lt;code&gt;User&lt;/code&gt; interface expects &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Map the field in every place you read user data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full_name&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. CORS errors from &lt;code&gt;localhost:3000&lt;/code&gt; → &lt;code&gt;localhost:8088&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The API needs CORS headers for cross-origin requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add CORS middleware in your MonkeysLegion middleware stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/middleware.mlc&lt;/span&gt;
&lt;span class="nc"&gt;MonkeysLegion\Http\Middleware\CorsMiddleware&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or set the environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CORS_ORIGINS=http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6. Page redirects to &lt;code&gt;/login&lt;/code&gt; on refresh despite being logged in
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The &lt;code&gt;/me&lt;/code&gt; endpoint crashes (often the &lt;code&gt;DateTimeImmutable&lt;/code&gt; bug), so the auth provider clears tokens and sets &lt;code&gt;user = null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Upgrade MonkeysLegion to &lt;code&gt;2.0.8+&lt;/code&gt; and verify &lt;code&gt;/me&lt;/code&gt; works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8088/api/v2/auth/me &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Register a user via curl&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8088/api/v2/auth/register &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "John Doe",
    "email": "john@example.com",
    "password": "securepass123",
    "company_name": "Acme Inc."
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Login&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8088/api/v2/auth/login &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"email": "john@example.com", "password": "securepass123"}'&lt;/span&gt;

&lt;span class="c"&gt;# Get current user&lt;/span&gt;
curl http://localhost:8088/api/v2/auth/me &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;access_token&amp;gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Refresh token&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8088/api/v2/auth/refresh &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"refresh_token": "&amp;lt;refresh_token&amp;gt;"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://monkeyslegion.com/" rel="noopener noreferrer"&gt;MonkeysLegion&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>symfony</category>
      <category>react</category>
    </item>
    <item>
      <title>MonkeysLegion 2.0 Is Released — Introducing Apex AI and a Faster PHP Runtime</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Tue, 28 Apr 2026 22:43:51 +0000</pubDate>
      <link>https://dev.to/yorchperaza/monkeyslegion-20-is-released-introducing-apex-ai-and-a-faster-php-runtime-1m57</link>
      <guid>https://dev.to/yorchperaza/monkeyslegion-20-is-released-introducing-apex-ai-and-a-faster-php-runtime-1m57</guid>
      <description>&lt;p&gt;MonkeysLegion 2.0 is officially released.&lt;/p&gt;

&lt;p&gt;This is more than a framework update. It is a full architectural step forward for the MonkeysLegion ecosystem: a faster runtime, a cleaner PHP 8.4+ foundation, a stronger modular package structure, and a new AI orchestration layer built directly for modern PHP applications.&lt;/p&gt;

&lt;p&gt;PHP has always been one of the most productive languages on the web.&lt;/p&gt;

&lt;p&gt;It powers SaaS platforms, APIs, internal tools, content systems, enterprise applications, dashboards, automation workflows, and products used by millions of people every day.&lt;/p&gt;

&lt;p&gt;But modern teams are building in a different environment now.&lt;/p&gt;

&lt;p&gt;They need faster boot times.&lt;br&gt;&lt;br&gt;
They need clean architecture.&lt;br&gt;&lt;br&gt;
They need APIs by default.&lt;br&gt;&lt;br&gt;
They need cloud-native deployment.&lt;br&gt;&lt;br&gt;
They need observability.&lt;br&gt;&lt;br&gt;
They need DevOps-friendly workflows.&lt;br&gt;&lt;br&gt;
They need AI-ready applications.&lt;br&gt;&lt;br&gt;
They need developer experience without hidden runtime magic.  &lt;/p&gt;

&lt;p&gt;That is why we are releasing &lt;strong&gt;MonkeysLegion 2.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;MonkeysLegion 2.0 is built for developers and teams who want the clarity of raw PHP, the structure of a serious framework, the productivity of a modular ecosystem, and the performance profile of a runtime that gets out of the way.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why MonkeysLegion 2.0 Matters
&lt;/h2&gt;

&lt;p&gt;Laravel built an incredible ecosystem.&lt;/p&gt;

&lt;p&gt;Forge, Vapor, Nova, Cashier, queues, jobs, billing tools, starter kits, and one of the largest PHP communities in the world. If you need a batteries-included SaaS starter, Laravel is hard to beat.&lt;/p&gt;

&lt;p&gt;Symfony powers some of the most mission-critical enterprise systems on earth.&lt;/p&gt;

&lt;p&gt;Its profiler, debug toolbar, reusable components, release discipline, and enterprise credibility are best-in-class.&lt;/p&gt;

&lt;p&gt;MonkeysLegion is built for a different moment.&lt;/p&gt;

&lt;p&gt;A moment where teams want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raw throughput&lt;/li&gt;
&lt;li&gt;PHP 8.4+ architecture&lt;/li&gt;
&lt;li&gt;Attribute-first development&lt;/li&gt;
&lt;li&gt;Minimal runtime overhead&lt;/li&gt;
&lt;li&gt;Clean dependency injection&lt;/li&gt;
&lt;li&gt;Modular packages&lt;/li&gt;
&lt;li&gt;API-first structure&lt;/li&gt;
&lt;li&gt;DevOps automation&lt;/li&gt;
&lt;li&gt;Cloud-native deployment&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;AI orchestration&lt;/li&gt;
&lt;li&gt;Enterprise-ready foundations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No unnecessary heavy boot process.&lt;br&gt;&lt;br&gt;
No runtime proxies.&lt;br&gt;&lt;br&gt;
No hidden magic.&lt;br&gt;&lt;br&gt;
No fighting the framework.&lt;/p&gt;

&lt;p&gt;Just your code, clean architecture, and a framework designed to move fast from local development to production.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Changed in 2.0
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion 2.0 is a full architectural pass across the framework and package ecosystem.&lt;/p&gt;

&lt;p&gt;The release focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP 8.4+ native architecture&lt;/li&gt;
&lt;li&gt;Attribute-first routing and configuration&lt;/li&gt;
&lt;li&gt;Compiled dependency injection&lt;/li&gt;
&lt;li&gt;Typed MLC configuration&lt;/li&gt;
&lt;li&gt;PSR-compliant HTTP and middleware layers&lt;/li&gt;
&lt;li&gt;Production-ready security middleware&lt;/li&gt;
&lt;li&gt;OpenAPI-first API development&lt;/li&gt;
&lt;li&gt;A modular package ecosystem&lt;/li&gt;
&lt;li&gt;Better developer tooling&lt;/li&gt;
&lt;li&gt;Faster runtime behavior&lt;/li&gt;
&lt;li&gt;AI orchestration with Apex AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make PHP feel modern again — fast, clean, modular, typed, observable, deployable, and ready for AI-native applications.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  PHP 8.4+ Native Architecture
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion 2.0 is designed around modern PHP instead of legacy conventions.&lt;/p&gt;

&lt;p&gt;The framework is moving deeper into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strict types&lt;/li&gt;
&lt;li&gt;PHP attributes&lt;/li&gt;
&lt;li&gt;Property hooks&lt;/li&gt;
&lt;li&gt;Constructor injection&lt;/li&gt;
&lt;li&gt;Typed configuration&lt;/li&gt;
&lt;li&gt;Compiled containers&lt;/li&gt;
&lt;li&gt;PSR standards&lt;/li&gt;
&lt;li&gt;Clean service boundaries&lt;/li&gt;
&lt;li&gt;Lightweight runtime behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main idea is that your application should be readable from the code itself.&lt;/p&gt;

&lt;p&gt;Routes should be visible.&lt;br&gt;&lt;br&gt;
Services should be explicit.&lt;br&gt;&lt;br&gt;
Configuration should be understandable.&lt;br&gt;&lt;br&gt;
Controllers should be clean.&lt;br&gt;&lt;br&gt;
Database logic should be predictable.&lt;br&gt;&lt;br&gt;
Deployment should not require guessing.&lt;/p&gt;

&lt;p&gt;MonkeysLegion 2.0 keeps the code close to the developer.&lt;/p&gt;


&lt;h2&gt;
  
  
  Attribute-First Development
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion 2.0 uses PHP attributes as a first-class architecture pattern.&lt;/p&gt;

&lt;p&gt;Routes, validation rules, service providers, commands, middleware, and AI tools can be declared close to the code they affect.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Http\Attributes\Route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Http\Message\ResponseInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProjectController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;#[Route('/api/projects', methods: ['GET'])]&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;ResponseInterface&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes applications easier to understand.&lt;/p&gt;

&lt;p&gt;You open a controller and immediately see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The endpoint&lt;/li&gt;
&lt;li&gt;The HTTP method&lt;/li&gt;
&lt;li&gt;The handler&lt;/li&gt;
&lt;li&gt;The dependencies&lt;/li&gt;
&lt;li&gt;The response flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams building APIs, dashboards, SaaS platforms, internal tools, CMS platforms, or microservices, this matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Compiled Dependency Injection
&lt;/h2&gt;

&lt;p&gt;Dependency injection should help developers, not confuse them.&lt;/p&gt;

&lt;p&gt;MonkeysLegion 2.0 continues the direction of a clean, predictable container that favors explicit services, typed dependencies, and production compilation.&lt;/p&gt;

&lt;p&gt;The goal is not to create a magical container that hides everything.&lt;/p&gt;

&lt;p&gt;The goal is to make dependency management simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Log\LoggerInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProjectService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;ProjectRepository&lt;/span&gt; &lt;span class="nv"&gt;$projects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;LoggerInterface&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your services are clear.&lt;br&gt;&lt;br&gt;
Your dependencies are explicit.&lt;br&gt;&lt;br&gt;
Your application is easier to test.&lt;br&gt;&lt;br&gt;
Your architecture is easier to reason about.&lt;/p&gt;

&lt;p&gt;In production, the framework can compile container definitions so applications avoid unnecessary runtime reflection on the hot path.&lt;/p&gt;

&lt;p&gt;That is one of the core performance principles behind MonkeysLegion 2.0.&lt;/p&gt;


&lt;h2&gt;
  
  
  Performance Is a Core Feature
&lt;/h2&gt;

&lt;p&gt;Performance is not a marketing layer in MonkeysLegion 2.0.&lt;/p&gt;

&lt;p&gt;It is part of the architecture.&lt;/p&gt;

&lt;p&gt;The framework is designed around zero-magic PHP objects, compiled routing, compiled dependency injection, attribute discovery at cache/build time, and a lean PSR middleware pipeline.&lt;/p&gt;

&lt;p&gt;The performance direction is based on several key principles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero-magic architecture&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Entities, DTOs, resources, and enums should behave like plain PHP objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No proxy-heavy runtime model&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The framework avoids forcing every application through runtime proxy layers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compiled routing and DI&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Discovery happens before production runtime where possible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lean middleware pipeline&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Middleware should be powerful, but the request path should remain predictable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PHP 8.4+ features&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The framework embraces modern PHP language improvements instead of simulating them with legacy patterns.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the current MonkeysLegion benchmarks, the framework is positioned around major improvements over heavier runtime approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entity creation: around &lt;strong&gt;140× faster than Laravel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;DTO construction: around &lt;strong&gt;60× faster than Laravel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enum operations: around &lt;strong&gt;25× faster than Laravel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Resource serialization: around &lt;strong&gt;5.5× faster than Laravel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;HTTP throughput: around &lt;strong&gt;12.5K requests/sec vs Laravel’s ~2.1K requests/sec&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Cold boot memory: significantly lower than heavier full-stack boot processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These benchmark numbers should be understood in context: benchmarks depend on hardware, PHP version, configuration, JIT, opcache, test harness, and workload.&lt;/p&gt;

&lt;p&gt;But the architectural direction is clear:&lt;/p&gt;

&lt;p&gt;MonkeysLegion 2.0 is designed to reduce runtime overhead and make high-performance PHP applications easier to build.&lt;/p&gt;


&lt;h2&gt;
  
  
  API-First by Default
&lt;/h2&gt;

&lt;p&gt;Modern applications are API-driven.&lt;/p&gt;

&lt;p&gt;A backend is rarely just a backend anymore. It may serve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A web app&lt;/li&gt;
&lt;li&gt;A mobile app&lt;/li&gt;
&lt;li&gt;A dashboard&lt;/li&gt;
&lt;li&gt;A CMS&lt;/li&gt;
&lt;li&gt;A CLI&lt;/li&gt;
&lt;li&gt;A partner integration&lt;/li&gt;
&lt;li&gt;An AI workflow&lt;/li&gt;
&lt;li&gt;A microservice&lt;/li&gt;
&lt;li&gt;A third-party API consumer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MonkeysLegion 2.0 is designed with APIs as a first-class concern.&lt;/p&gt;

&lt;p&gt;The framework and skeleton are moving toward a complete API foundation with support for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PSR-7 and PSR-15 HTTP handling&lt;/li&gt;
&lt;li&gt;Middleware pipelines&lt;/li&gt;
&lt;li&gt;JWT authentication&lt;/li&gt;
&lt;li&gt;RBAC&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;DTO binding&lt;/li&gt;
&lt;li&gt;OpenAPI documentation&lt;/li&gt;
&lt;li&gt;Swagger UI&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Security headers&lt;/li&gt;
&lt;li&gt;Request IDs&lt;/li&gt;
&lt;li&gt;CORS&lt;/li&gt;
&lt;li&gt;Trusted proxies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means teams can build real production APIs without stitching together half a dozen disconnected tools.&lt;/p&gt;


&lt;h2&gt;
  
  
  Introducing Apex AI
&lt;/h2&gt;

&lt;p&gt;The biggest new direction in the MonkeysLegion ecosystem is &lt;strong&gt;Apex AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Apex AI is an AI orchestration package for PHP 8.4+.&lt;/p&gt;

&lt;p&gt;It is not just another OpenAI wrapper.&lt;/p&gt;

&lt;p&gt;Apex AI is designed to bring production-ready AI infrastructure into PHP with a clean, framework-native developer experience.&lt;/p&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build AI-powered applications in PHP without stitching together multiple disconnected tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Apex AI introduces a unified way to work with multiple AI providers, route requests intelligently, build pipelines, manage cost, use tools, stream responses, and create real AI workflows inside PHP applications.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Apex AI Brings to PHP
&lt;/h2&gt;

&lt;p&gt;Apex AI is designed around the needs of real production applications.&lt;/p&gt;

&lt;p&gt;It focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unified AI provider interface&lt;/li&gt;
&lt;li&gt;Multi-provider routing&lt;/li&gt;
&lt;li&gt;OpenAI support&lt;/li&gt;
&lt;li&gt;Anthropic support&lt;/li&gt;
&lt;li&gt;Google AI Studio support&lt;/li&gt;
&lt;li&gt;Vertex AI support&lt;/li&gt;
&lt;li&gt;Ollama/local model support&lt;/li&gt;
&lt;li&gt;Smart model routing&lt;/li&gt;
&lt;li&gt;Fallback chains&lt;/li&gt;
&lt;li&gt;Declarative AI pipelines&lt;/li&gt;
&lt;li&gt;Guardrails&lt;/li&gt;
&lt;li&gt;Structured outputs&lt;/li&gt;
&lt;li&gt;Tool calling with PHP attributes&lt;/li&gt;
&lt;li&gt;Streaming with Server-Sent Events&lt;/li&gt;
&lt;li&gt;Agent-style workflows&lt;/li&gt;
&lt;li&gt;Multi-agent crews&lt;/li&gt;
&lt;li&gt;Memory strategies&lt;/li&gt;
&lt;li&gt;Cost tracking&lt;/li&gt;
&lt;li&gt;Budget controls&lt;/li&gt;
&lt;li&gt;Model Context Protocol support&lt;/li&gt;
&lt;li&gt;Clean integration with MonkeysLegion applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because AI development is moving beyond simple prompt calls.&lt;/p&gt;

&lt;p&gt;Modern AI applications need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Fallbacks&lt;/li&gt;
&lt;li&gt;Guardrails&lt;/li&gt;
&lt;li&gt;Tool execution&lt;/li&gt;
&lt;li&gt;Structured responses&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;Context management&lt;/li&gt;
&lt;li&gt;Cost controls&lt;/li&gt;
&lt;li&gt;Streaming&lt;/li&gt;
&lt;li&gt;Evaluation&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apex AI brings these ideas into PHP as part of the MonkeysLegion ecosystem.&lt;/p&gt;


&lt;h2&gt;
  
  
  AI-Native PHP Applications
&lt;/h2&gt;

&lt;p&gt;PHP developers should not need to leave PHP to build serious AI workflows.&lt;/p&gt;

&lt;p&gt;Many companies already have PHP backends, PHP teams, PHP infrastructure, PHP CMS platforms, PHP APIs, and PHP business logic.&lt;/p&gt;

&lt;p&gt;The question is not whether PHP can be used for AI applications.&lt;/p&gt;

&lt;p&gt;The question is whether the ecosystem gives PHP developers the right tools.&lt;/p&gt;

&lt;p&gt;Apex AI is built around that belief.&lt;/p&gt;

&lt;p&gt;With Apex AI, a PHP application can become:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AI-powered CMS&lt;/li&gt;
&lt;li&gt;An AI-assisted dashboard&lt;/li&gt;
&lt;li&gt;A document processing system&lt;/li&gt;
&lt;li&gt;A content generation platform&lt;/li&gt;
&lt;li&gt;A support automation tool&lt;/li&gt;
&lt;li&gt;An internal agent workflow&lt;/li&gt;
&lt;li&gt;A SaaS product with AI features&lt;/li&gt;
&lt;li&gt;A developer platform with AI orchestration&lt;/li&gt;
&lt;li&gt;A backend that routes between cloud and local models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where MonkeysLegion 2.0 becomes more than a framework release.&lt;/p&gt;

&lt;p&gt;It becomes the foundation for AI-native PHP development.&lt;/p&gt;


&lt;h2&gt;
  
  
  Example: Tool Calling with Attributes
&lt;/h2&gt;

&lt;p&gt;Apex AI is designed to feel natural inside modern PHP.&lt;/p&gt;

&lt;p&gt;Instead of forcing AI workflows into disconnected configuration files, tools can be represented as PHP code.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Apex\Attributes\Tool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProjectTools&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'find_project'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Find a project by ID and return its current status.'&lt;/span&gt;
  &lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;findProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;findStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps AI tools close to the domain logic of the application.&lt;/p&gt;

&lt;p&gt;The result is easier to read, easier to test, and easier to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Built for SaaS, Internal Tools, and Enterprise Platforms
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion 2.0 is not only for small apps.&lt;/p&gt;

&lt;p&gt;It is being designed for companies building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SaaS platforms&lt;/li&gt;
&lt;li&gt;Internal business tools&lt;/li&gt;
&lt;li&gt;Developer portals&lt;/li&gt;
&lt;li&gt;Admin dashboards&lt;/li&gt;
&lt;li&gt;API platforms&lt;/li&gt;
&lt;li&gt;Automation systems&lt;/li&gt;
&lt;li&gt;AI-enabled products&lt;/li&gt;
&lt;li&gt;Cloud-native services&lt;/li&gt;
&lt;li&gt;CMS-driven websites&lt;/li&gt;
&lt;li&gt;Enterprise applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many companies do not need another heavy framework that assumes one way of working.&lt;/p&gt;

&lt;p&gt;They need a framework that lets them move fast while keeping the codebase clean.&lt;/p&gt;

&lt;p&gt;MonkeysLegion gives teams a structured foundation without forcing unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Apex AI extends that foundation into AI-powered product development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Modular Ecosystem
&lt;/h2&gt;

&lt;p&gt;One of the most important ideas behind MonkeysLegion is modularity.&lt;/p&gt;

&lt;p&gt;You should not need to install a giant framework if you only need one part of it.&lt;/p&gt;

&lt;p&gt;MonkeysLegion is moving toward a package ecosystem where teams can use the pieces they need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core&lt;/li&gt;
&lt;li&gt;Router&lt;/li&gt;
&lt;li&gt;Dependency Injection&lt;/li&gt;
&lt;li&gt;HTTP&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;Query Builder&lt;/li&gt;
&lt;li&gt;Migration&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Internationalization&lt;/li&gt;
&lt;li&gt;Telemetry&lt;/li&gt;
&lt;li&gt;DevTools&lt;/li&gt;
&lt;li&gt;CLI&lt;/li&gt;
&lt;li&gt;GraphQL&lt;/li&gt;
&lt;li&gt;Apex AI&lt;/li&gt;
&lt;li&gt;Cloud tooling&lt;/li&gt;
&lt;li&gt;Testing utilities&lt;/li&gt;
&lt;li&gt;CMS-related packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This package-first approach makes the framework flexible.&lt;/p&gt;

&lt;p&gt;Small projects can stay small.&lt;br&gt;&lt;br&gt;
Large projects can grow with structure.&lt;br&gt;&lt;br&gt;
Enterprise teams can standardize around the packages that matter to them.&lt;/p&gt;


&lt;h2&gt;
  
  
  Telemetry and Observability
&lt;/h2&gt;

&lt;p&gt;Modern applications need visibility.&lt;/p&gt;

&lt;p&gt;It is not enough for a framework to run code. Teams need to understand what is happening in production.&lt;/p&gt;

&lt;p&gt;MonkeysLegion 2.0 treats observability as part of the developer platform, not as an afterthought.&lt;/p&gt;

&lt;p&gt;That includes visibility into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request performance&lt;/li&gt;
&lt;li&gt;Application health&lt;/li&gt;
&lt;li&gt;Security events&lt;/li&gt;
&lt;li&gt;API usage&lt;/li&gt;
&lt;li&gt;Background processes&lt;/li&gt;
&lt;li&gt;AI requests&lt;/li&gt;
&lt;li&gt;Model routing&lt;/li&gt;
&lt;li&gt;Provider fallbacks&lt;/li&gt;
&lt;li&gt;Cost tracking&lt;/li&gt;
&lt;li&gt;Latency&lt;/li&gt;
&lt;li&gt;Error rates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This becomes even more important with Apex AI.&lt;/p&gt;

&lt;p&gt;AI applications need observability because model behavior, token usage, latency, provider availability, and costs all affect production reliability.&lt;/p&gt;


&lt;h2&gt;
  
  
  Cloud-Native From the Start
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion is not being designed only for local development.&lt;/p&gt;

&lt;p&gt;It is being built with deployment in mind.&lt;/p&gt;

&lt;p&gt;Modern teams need applications that can move from local development to containers, servers, CI/CD pipelines, and cloud environments without massive rewrites.&lt;/p&gt;

&lt;p&gt;That matters because software delivery is no longer just about writing PHP classes.&lt;/p&gt;

&lt;p&gt;It is about the full lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build&lt;/li&gt;
&lt;li&gt;Test&lt;/li&gt;
&lt;li&gt;Package&lt;/li&gt;
&lt;li&gt;Deploy&lt;/li&gt;
&lt;li&gt;Monitor&lt;/li&gt;
&lt;li&gt;Scale&lt;/li&gt;
&lt;li&gt;Improve&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;MonkeysLegion 2.0 is being shaped around that complete workflow.&lt;/p&gt;


&lt;h2&gt;
  
  
  A Better Developer Experience Without Heavy Runtime Cost
&lt;/h2&gt;

&lt;p&gt;Developer experience matters.&lt;/p&gt;

&lt;p&gt;But developer experience should not require hiding everything behind magic.&lt;/p&gt;

&lt;p&gt;MonkeysLegion 2.0 is focused on a balanced philosophy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give developers speed, structure, and useful defaults — without making the runtime heavy or the code difficult to understand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear file structure&lt;/li&gt;
&lt;li&gt;Fast boot process&lt;/li&gt;
&lt;li&gt;Explicit dependencies&lt;/li&gt;
&lt;li&gt;Attribute-based configuration&lt;/li&gt;
&lt;li&gt;Useful CLI commands&lt;/li&gt;
&lt;li&gt;Strong defaults&lt;/li&gt;
&lt;li&gt;Modular packages&lt;/li&gt;
&lt;li&gt;Production-ready skeleton&lt;/li&gt;
&lt;li&gt;Easy local setup&lt;/li&gt;
&lt;li&gt;Cloud-ready conventions&lt;/li&gt;
&lt;li&gt;AI-ready workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The framework should feel productive without becoming opaque.&lt;/p&gt;


&lt;h2&gt;
  
  
  Inspired by the Best, Built With a Different Philosophy
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion is not trying to replace Laravel or Symfony.&lt;/p&gt;

&lt;p&gt;They are excellent frameworks with massive ecosystems and proven track records.&lt;/p&gt;

&lt;p&gt;But not every team wants the same tradeoffs.&lt;/p&gt;

&lt;p&gt;Laravel is great when you want a large ecosystem and rapid application scaffolding.&lt;/p&gt;

&lt;p&gt;Symfony is great when you want enterprise components, stability, and deep configurability.&lt;/p&gt;

&lt;p&gt;MonkeysLegion is for teams that want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A lighter framework&lt;/li&gt;
&lt;li&gt;Less magic&lt;/li&gt;
&lt;li&gt;More direct control&lt;/li&gt;
&lt;li&gt;Modern PHP-first architecture&lt;/li&gt;
&lt;li&gt;Fast APIs&lt;/li&gt;
&lt;li&gt;Modular packages&lt;/li&gt;
&lt;li&gt;Cloud-native defaults&lt;/li&gt;
&lt;li&gt;Strong developer tooling&lt;/li&gt;
&lt;li&gt;Clean enterprise structure&lt;/li&gt;
&lt;li&gt;AI orchestration built into the ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is not about copying the old model.&lt;/p&gt;

&lt;p&gt;It is about building a framework for where PHP is going next.&lt;/p&gt;


&lt;h2&gt;
  
  
  Built as an Ecosystem, Not Just a Framework
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion 2.0 is part of a bigger vision.&lt;/p&gt;

&lt;p&gt;The framework is the foundation, but the ecosystem is the real goal.&lt;/p&gt;

&lt;p&gt;That ecosystem includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MonkeysLegion Framework&lt;/li&gt;
&lt;li&gt;MonkeysLegion Skeleton&lt;/li&gt;
&lt;li&gt;MonkeysLegion DevTools&lt;/li&gt;
&lt;li&gt;MonkeysLegion GraphQL&lt;/li&gt;
&lt;li&gt;MonkeysLegion Telemetry&lt;/li&gt;
&lt;li&gt;MonkeysLegion Apex AI&lt;/li&gt;
&lt;li&gt;MonkeysCMS&lt;/li&gt;
&lt;li&gt;Cloud deployment tooling&lt;/li&gt;
&lt;li&gt;AI-ready developer workflows&lt;/li&gt;
&lt;li&gt;Enterprise package roadmap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because modern teams do not only need a router or container.&lt;/p&gt;

&lt;p&gt;They need a complete development platform.&lt;/p&gt;

&lt;p&gt;MonkeysLegion is moving toward that full-stack ecosystem while keeping the core lightweight and modular.&lt;/p&gt;


&lt;h2&gt;
  
  
  MonkeysCMS and the Future of AI-Ready Content Platforms
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion also powers the direction behind MonkeysCMS.&lt;/p&gt;

&lt;p&gt;MonkeysCMS is being designed as a modern, code-first CMS built on top of the MonkeysLegion framework.&lt;/p&gt;

&lt;p&gt;This is one of the strongest examples of why MonkeysLegion matters.&lt;/p&gt;

&lt;p&gt;A framework is not only valuable when it can serve a JSON response.&lt;/p&gt;

&lt;p&gt;It becomes powerful when it can support real products:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CMS platforms&lt;/li&gt;
&lt;li&gt;Admin systems&lt;/li&gt;
&lt;li&gt;API products&lt;/li&gt;
&lt;li&gt;AI workflows&lt;/li&gt;
&lt;li&gt;Enterprise dashboards&lt;/li&gt;
&lt;li&gt;Developer platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Apex AI, the CMS direction becomes even stronger.&lt;/p&gt;

&lt;p&gt;Content systems are one of the clearest places where AI can help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate drafts&lt;/li&gt;
&lt;li&gt;Improve content&lt;/li&gt;
&lt;li&gt;Critique content&lt;/li&gt;
&lt;li&gt;Translate content&lt;/li&gt;
&lt;li&gt;Classify content&lt;/li&gt;
&lt;li&gt;Extract metadata&lt;/li&gt;
&lt;li&gt;Build summaries&lt;/li&gt;
&lt;li&gt;Power editorial assistants&lt;/li&gt;
&lt;li&gt;Automate workflow tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MonkeysLegion 2.0 is the foundation for that future.&lt;/p&gt;


&lt;h2&gt;
  
  
  Open Source and Community-Driven
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion is open source.&lt;/p&gt;

&lt;p&gt;The project started from a simple belief:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Modern PHP projects deserve a framework that is fast, cloud-native, AI-ready, and clear to work with.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Version 2.0 is a call to developers, agencies, founders, and engineering teams:&lt;/p&gt;

&lt;p&gt;Come test it.&lt;br&gt;&lt;br&gt;
Break it.&lt;br&gt;&lt;br&gt;
Improve it.&lt;br&gt;&lt;br&gt;
Build with it.&lt;br&gt;&lt;br&gt;
Open issues.&lt;br&gt;&lt;br&gt;
Submit pull requests.&lt;br&gt;&lt;br&gt;
Share ideas.&lt;br&gt;&lt;br&gt;
Help shape the next generation of PHP tooling.&lt;/p&gt;


&lt;h2&gt;
  
  
  Who MonkeysLegion 2.0 Is For
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion 2.0 is for developers and teams who want to build serious software with PHP.&lt;/p&gt;

&lt;p&gt;It is for the solo founder building a SaaS product.&lt;/p&gt;

&lt;p&gt;It is for the agency building multiple client platforms.&lt;/p&gt;

&lt;p&gt;It is for the backend engineer building APIs.&lt;/p&gt;

&lt;p&gt;It is for the enterprise team that needs maintainable internal tools.&lt;/p&gt;

&lt;p&gt;It is for the startup that wants speed without technical debt.&lt;/p&gt;

&lt;p&gt;It is for the developer who loves PHP but wants a framework that feels modern, clean, fast, and ready for AI-powered products.&lt;/p&gt;

&lt;p&gt;It is for builders who want control.&lt;/p&gt;


&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The easiest way to start is with the official skeleton.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project monkeyscloud/monkeyslegion-skeleton my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
composer serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, you can begin building APIs, services, dashboards, internal tools, SaaS applications, AI workflows, CMS platforms, or custom products using the MonkeysLegion architecture.&lt;/p&gt;

&lt;p&gt;Useful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://monkeyslegion.com" rel="noopener noreferrer"&gt;monkeyslegion.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Release article: &lt;a href="https://monkeyslegion.com/news/2026-04/monkeyslegion-2-announcing" rel="noopener noreferrer"&gt;monkeyslegion.com/news/2026-04/monkeyslegion-2-announcing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Framework: &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion" rel="noopener noreferrer"&gt;github.com/MonkeysCloud/MonkeysLegion&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Skeleton: &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion-Skeleton" rel="noopener noreferrer"&gt;github.com/MonkeysCloud/MonkeysLegion-Skeleton&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion 2.0 is built with a clear belief:&lt;/p&gt;

&lt;p&gt;PHP is not done.&lt;/p&gt;

&lt;p&gt;PHP is still one of the most important languages on the web. But the ecosystem needs new ideas, new tooling, and new frameworks that match how software is built today.&lt;/p&gt;

&lt;p&gt;MonkeysLegion is our answer.&lt;/p&gt;

&lt;p&gt;A feather-light framework.&lt;br&gt;&lt;br&gt;
A modular ecosystem.&lt;br&gt;&lt;br&gt;
A cloud-native foundation.&lt;br&gt;&lt;br&gt;
A clean architecture for modern PHP.&lt;br&gt;&lt;br&gt;
A faster runtime path.&lt;br&gt;&lt;br&gt;
A developer platform built for speed, clarity, and scale.&lt;br&gt;&lt;br&gt;
An AI orchestration layer for the next generation of PHP applications.&lt;/p&gt;

&lt;p&gt;Version 2.0 is not the finish line.&lt;/p&gt;

&lt;p&gt;It is the foundation.&lt;/p&gt;

&lt;p&gt;Welcome to MonkeysLegion 2.0.&lt;/p&gt;

&lt;p&gt;Welcome to Apex AI.&lt;/p&gt;

&lt;p&gt;Let’s build the next era of PHP.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>symfony</category>
      <category>ai</category>
    </item>
    <item>
      <title>I Replaced GitHub + Jira + Vercel + PlanetScale + Datadog With One Free Platform</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Mon, 06 Apr 2026 15:07:29 +0000</pubDate>
      <link>https://dev.to/yorchperaza/i-replaced-github-jira-vercel-planetscale-datadog-with-one-free-platform-5gkl</link>
      <guid>https://dev.to/yorchperaza/i-replaced-github-jira-vercel-planetscale-datadog-with-one-free-platform-5gkl</guid>
      <description>&lt;p&gt;Every month I was paying for five tools that didn't talk to each other.&lt;/p&gt;

&lt;p&gt;GitHub for code ($20/mo for the team). Jira for tasks ($8/user/mo). Vercel for some deploys ($20/user/mo). A database host ($40/mo). Datadog for monitoring ($75/mo). Plus the Slack tax of context-switching between all of them 50 times a day.&lt;/p&gt;

&lt;p&gt;Total: &lt;strong&gt;~$400/month.&lt;/strong&gt; Five logins. Five billing dashboards. Five permission systems. Zero integration between them.&lt;/p&gt;

&lt;p&gt;When someone on my team merged a PR, I had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check GitHub to see the diff&lt;/li&gt;
&lt;li&gt;Switch to Jira to update the ticket&lt;/li&gt;
&lt;li&gt;Switch to Vercel to see if the deploy succeeded&lt;/li&gt;
&lt;li&gt;Switch to Datadog to check if metrics look healthy&lt;/li&gt;
&lt;li&gt;Go back to Jira to move the ticket to Done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's five context switches for one merge. Multiply by 10 merges a day. That's 50 tab switches. Research says each context switch costs 20-30 minutes of deep focus. We were bleeding productivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Built One Platform
&lt;/h2&gt;

&lt;p&gt;I'm a developer. The answer was obvious: build one tool that does all of it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://monkeys.cloud" rel="noopener noreferrer"&gt;MonkeysCloud&lt;/a&gt; is what I built. It's a single platform with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git hosting&lt;/strong&gt; — repos, PRs, branches, merge queues, branch protection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task management&lt;/strong&gt; — Kanban boards, Scrum sprints, time tracking, labels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting&lt;/strong&gt; — 32 stacks, auto-detect, auto-build, auto-deploy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databases&lt;/strong&gt; — MySQL, PostgreSQL, Redis, MongoDB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI&lt;/strong&gt; — code review on every PR, build failure analysis, deploy risk scoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt; — wiki, knowledge base, API docs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt; — metrics, logs, uptime checks, alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here's the part that matters: &lt;strong&gt;it's free.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Free" Actually Means
&lt;/h2&gt;

&lt;p&gt;Not a 14-day trial. Not "free until you hit 100 builds." Actually free:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;Free tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Compute instances per project&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2 free&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database instances per project&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2 free&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team members&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unlimited&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Projects&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unlimited&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git repositories&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unlimited&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom domains + SSL&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI code review&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kanban boards + sprints&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring + logs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation wiki&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Part That Changed My Workflow
&lt;/h2&gt;

&lt;p&gt;The killer feature isn't any single thing — it's the integration.&lt;/p&gt;

&lt;p&gt;When a developer creates a branch named &lt;code&gt;feature/PROJ-42-auth&lt;/code&gt;, the platform automatically links that branch, every commit on it, the PR, the build, the preview environment, and the deploy &lt;strong&gt;back to task PROJ-42&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The task timeline shows:&lt;/p&gt;

&lt;p&gt;Task created by Sarah&lt;br&gt;
Branch feature/PROJ-42-auth created&lt;br&gt;
4 commits pushed&lt;br&gt;
PR #17 opened&lt;br&gt;
AI review: 2 suggestions (resolved)&lt;br&gt;
Build #84 passed&lt;br&gt;
PR merged to main&lt;br&gt;
Deploy #91 to production ✓&lt;br&gt;
Task auto-moved to Done&lt;/p&gt;

&lt;p&gt;Nobody manually updated the task. Nobody copy-pasted a Jira ticket number. Nobody checked three tabs to verify the deploy. It just happened.&lt;/p&gt;

&lt;p&gt;My project manager sees the full lifecycle of every feature without asking anyone for a status update. The board moves itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Actually Helps
&lt;/h2&gt;

&lt;p&gt;I've been burned by "AI-powered" tools that are just ChatGPT wrappers. MonkeysAI is different because it has context — it accesses the actual codebase, task board, build logs, deploy history, and monitoring data.&lt;/p&gt;

&lt;p&gt;When a build fails, it doesn't say "check your configuration." It says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build failed: TypeScript type mismatch on line 42 of Auth.tsx. The function handleLogin expects &lt;code&gt;string&lt;/code&gt; but receives &lt;code&gt;string | undefined&lt;/code&gt;. Introduced in commit abc123. Fix: add a null check or update the type signature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I'm about to deploy to production, it gives a risk score:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deploy risk: MEDIUM. Changes 3 files in the payment module. Test coverage for changed code: 62% (below project average of 78%). Recommendation: verify checkout flow in preview before promoting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are not generic suggestions. They reference my actual files, my actual test coverage, my actual deploy history.&lt;/p&gt;

&lt;h2&gt;
  
  
  32 Stacks — Zero Config
&lt;/h2&gt;

&lt;p&gt;I push code. MonkeysCloud reads &lt;code&gt;composer.json&lt;/code&gt;, &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;go.mod&lt;/code&gt;, &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;Gemfile&lt;/code&gt;, &lt;code&gt;Cargo.toml&lt;/code&gt;, or whatever — and generates the entire build config automatically.&lt;/p&gt;

&lt;p&gt;Stacks I've deployed without writing a single line of configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PHP:&lt;/strong&gt; Laravel, Symfony, WordPress, Drupal, my own MonkeysLegion framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript:&lt;/strong&gt; Next.js, Nuxt, Remix, SvelteKit, Astro, Express, NestJS, React, Vue, Angular&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python:&lt;/strong&gt; Django, FastAPI, Flask, Streamlit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruby:&lt;/strong&gt; Rails&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go:&lt;/strong&gt; any Go project (compiles to binary, 30-second deploys)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust:&lt;/strong&gt; Actix, Axum, Rocket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java:&lt;/strong&gt; Spring Boot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.NET:&lt;/strong&gt; ASP.NET Core&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elixir:&lt;/strong&gt; Phoenix&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom:&lt;/strong&gt; Dockerfile, Docker Compose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can override everything, but for 90% of projects, you push code and it just works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Costs (For Real)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Solo developer, side project:&lt;/strong&gt; $0/month. Two free servers + two free databases. Custom domain. AI code review. Task board.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Going to production:&lt;/strong&gt; $19/month. One always-on Starter instance. Keep your free instances for dev and staging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Startup, 5 devs, 2 projects:&lt;/strong&gt; ~$177/month. Team plan ($59) + Pro production ($79) + Plus database ($39). Everything else free. Compare to $400+ across five separate tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agency, 12 clients:&lt;/strong&gt; ~$135/month. Most clients on free instances. A few on Starter ($19 each). Team plan ($59). All client stakeholders invited free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Push your code and get a live URL in 60 seconds: &lt;a href="https://monkeys.cloud" rel="noopener noreferrer"&gt;monkeys.cloud&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built this because I was tired of context-switching between five tools that cost $400/month combined. Now my entire workflow — code, tasks, deploys, databases, AI, docs, monitoring — lives in one place.&lt;/p&gt;

&lt;p&gt;If you've felt the same frustration, give it 5 minutes. The worst that happens is you deploy a project for free.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Jorge, the founder of MonkeysCloud. I'm building this in public and shipping fast. Follow along or roast my work — either way, I'd love to hear what you think in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>We're Giving Away Free Servers With Every Project — App, Database, Cache, Worker. No Trial. No Credit Card.</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Sun, 05 Apr 2026 21:16:28 +0000</pubDate>
      <link>https://dev.to/yorchperaza/were-giving-away-free-servers-with-every-project-app-database-cache-worker-no-trial-no-1ocf</link>
      <guid>https://dev.to/yorchperaza/were-giving-away-free-servers-with-every-project-app-database-cache-worker-no-trial-no-1ocf</guid>
      <description>&lt;p&gt;Today we're launching something that no other developer platform offers: &lt;strong&gt;every project you create on MonkeysCloud gets free compute instances and free database instances.&lt;/strong&gt; Not a 30-day trial. Not $5 in credits. Real servers that run your code and your database — for as long as you need them.&lt;/p&gt;

&lt;p&gt;Here's what that means in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Get — Per Project, No Strings
&lt;/h2&gt;

&lt;p&gt;Every project on MonkeysCloud includes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instance&lt;/th&gt;
&lt;th&gt;What it runs&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free server 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your application (any of 32 stacks)&lt;/td&gt;
&lt;td&gt;1 GB RAM&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free server 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A worker, second service, or another app&lt;/td&gt;
&lt;td&gt;1 GB RAM&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free database 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MySQL 8.4, PostgreSQL 16, or MongoDB 7&lt;/td&gt;
&lt;td&gt;1 GB RAM&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free database 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Redis 7 cache, or a second database&lt;/td&gt;
&lt;td&gt;1 GB RAM&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's &lt;strong&gt;4 free instances per project.&lt;/strong&gt; Each one can run any role — app server, database, cache, queue worker, scheduler, or a custom Docker container. You choose.&lt;/p&gt;

&lt;p&gt;No credit card required. No time limit. No usage cap. You sign up, create a project, select your stack, push code, and you have a running application with a database and a cache at a live URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Exactly Runs on These Free Instances?
&lt;/h2&gt;

&lt;p&gt;Let me make this concrete. Here's what you can deploy right now for $0:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A complete Laravel stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instance 1: Laravel app (FrankenPHP, PHP 8.4)&lt;/li&gt;
&lt;li&gt;Instance 2: Queue worker (processing jobs)&lt;/li&gt;
&lt;li&gt;Database 1: MySQL 8.4 (your primary database)&lt;/li&gt;
&lt;li&gt;Database 2: Redis 7 (sessions + cache)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total cost: &lt;strong&gt;$0.&lt;/strong&gt; That's a production-capable Laravel application with a relational database, a cache layer, and background job processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Next.js full-stack app:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instance 1: Next.js (Node 22, SSR)&lt;/li&gt;
&lt;li&gt;Database 1: PostgreSQL 16&lt;/li&gt;
&lt;li&gt;Database 2: Redis 7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total cost: &lt;strong&gt;$0.&lt;/strong&gt; Three instances. Full-stack JavaScript with a real database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Django + Celery setup:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instance 1: Django (Gunicorn)&lt;/li&gt;
&lt;li&gt;Instance 2: Celery worker&lt;/li&gt;
&lt;li&gt;Database 1: PostgreSQL 16&lt;/li&gt;
&lt;li&gt;Database 2: Redis 7 (Celery broker)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total cost: &lt;strong&gt;$0.&lt;/strong&gt; Background tasks, a real database, and a message broker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A microservices project:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instance 1: Go API&lt;/li&gt;
&lt;li&gt;Instance 2: Node.js service&lt;/li&gt;
&lt;li&gt;Database 1: PostgreSQL 16&lt;/li&gt;
&lt;li&gt;Database 2: MongoDB 7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total cost: &lt;strong&gt;$0.&lt;/strong&gt; Two services and two different database engines.&lt;/p&gt;

&lt;p&gt;Every project. Any stack. App + database + cache + worker. Free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fine Print (There's Not Much)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sleep after 30 minutes.&lt;/strong&gt; Free instances sleep after 30 minutes with no incoming traffic. When the next request arrives, the instance wakes in 3 to 5 seconds. Your data on disk — database files, uploaded files, application state — is preserved while sleeping. This is fine for development, staging, demos, internal tools, client previews, and sites with intermittent traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom domains and SSL work.&lt;/strong&gt; Every instance — including free ones — supports custom domains with auto-provisioned SSL via Let's Encrypt. You can point &lt;code&gt;myapp.com&lt;/code&gt; at a free instance and it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two sizes of free.&lt;/strong&gt; There are actually two free sizes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;CPU&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;SSD&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared 0.25 vCPU&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Starter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared 0.5 vCPU&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;td&gt;10 GB&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both are free. Both sleep after 30 minutes. Both count toward your free instance quota. Pick whichever fits your workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 + 2 is the starting point.&lt;/strong&gt; A free account gets 2 compute instances + 2 database instances per project. But that quota grows as you add paid instances (more on that below).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Platform Is Free Too
&lt;/h2&gt;

&lt;p&gt;The free instances are just the infrastructure. The platform that wraps them is also free:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git hosting.&lt;/strong&gt; Unlimited private repositories with Smart HTTP and SSH access. Or mirror from GitHub, GitLab, or Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull requests.&lt;/strong&gt; Create PRs, leave inline reviews, run merge queues, enforce branch protection. Three merge strategies: merge commit, squash, rebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI code review.&lt;/strong&gt; Every PR gets an automated review in seconds — security vulnerabilities, performance issues, bugs, and best practices. Inline comments with suggested fixes. Before your team even opens the PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task management.&lt;/strong&gt; Kanban boards and Scrum sprints. Tasks auto-link to branches, PRs, builds, and deploys through branch naming. The board updates itself — nobody manually moves tickets to "Done."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring.&lt;/strong&gt; Response times, error rates, CPU, memory, bandwidth — real-time dashboards with zero setup. Structured log viewer with search and live streaming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation.&lt;/strong&gt; Built-in Markdown wiki with version history, search, and collaborative editing. API docs, runbooks, onboarding guides — next to your code and tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unlimited team members.&lt;/strong&gt; Invite your entire team — developers, designers, product managers, clients, contractors. Everyone gets full access. No per-seat pricing. No "viewer" seats. Free.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Scales: The Tier System
&lt;/h2&gt;

&lt;p&gt;Here's the part I'm most proud of. When you add a paid instance (Plus at $39/mo or above) to any project, your &lt;strong&gt;entire organization&lt;/strong&gt; gets upgraded:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Paid Instances in Org&lt;/th&gt;
&lt;th&gt;Org Tier&lt;/th&gt;
&lt;th&gt;Max Projects&lt;/th&gt;
&lt;th&gt;Free Instances per Project&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2 + 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Starter&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3 + 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2–3&lt;/td&gt;
&lt;td&gt;Growth&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;4 + 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4–7&lt;/td&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;5 + 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8–15&lt;/td&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;6 + 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16–30&lt;/td&gt;
&lt;td&gt;Scale&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;7 + 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31+&lt;/td&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;7 + 7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;One paid instance upgrades every project.&lt;/strong&gt; Add a single Plus ($39/mo) to Project A, and Projects B, C, D, and E all get 3 free compute + 3 free database instances each. The paid instance benefits your entire org, not just the project it lives in.&lt;/p&gt;

&lt;p&gt;And here's the kicker: &lt;strong&gt;Plus, Pro, and Business include a 90-day free trial.&lt;/strong&gt; You add a Plus instance, pay $0 for 90 days, and immediately get Starter tier benefits across your entire organization. Five projects. Three free instances each. Before paying a single dollar.&lt;/p&gt;

&lt;h2&gt;
  
  
  32 Stacks — You Pick, We Build
&lt;/h2&gt;

&lt;p&gt;When you create a project, you select your stack from 32 options. MonkeysCloud generates the complete build configuration — base image, install commands, build steps, start command — based on your selection. Push code. We handle the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PHP:&lt;/strong&gt; Laravel, Symfony, WordPress, Drupal, MonkeysLegion, PHP Generic&lt;br&gt;
&lt;strong&gt;JavaScript/TypeScript:&lt;/strong&gt; Next.js, Nuxt, Remix, SvelteKit, Astro, Express, NestJS, React SPA, Vue SPA, Angular&lt;br&gt;
&lt;strong&gt;Python:&lt;/strong&gt; Django, FastAPI, Flask, Streamlit, Python Generic&lt;br&gt;
&lt;strong&gt;Ruby:&lt;/strong&gt; Rails, Ruby Generic&lt;br&gt;
&lt;strong&gt;Go&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Rust&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Java:&lt;/strong&gt; Spring Boot, Java Generic&lt;br&gt;
&lt;strong&gt;.NET&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Elixir:&lt;/strong&gt; Phoenix&lt;br&gt;
&lt;strong&gt;Other:&lt;/strong&gt; Static Site, Docker, Docker Compose&lt;/p&gt;

&lt;p&gt;You pick the stack. We configure the build pipeline. You push code. It deploys.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Costs When You Need More
&lt;/h2&gt;

&lt;p&gt;The free tier handles development, staging, demos, and low-traffic production. When you need always-on instances with more compute for real production traffic:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;SSD&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Always-On&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;No (sleeps)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starter&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;td&gt;10 GB&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;No (sleeps)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plus&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;25 GB&lt;/td&gt;
&lt;td&gt;$39/mo &lt;em&gt;(90 days free)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;60 GB&lt;/td&gt;
&lt;td&gt;$79/mo &lt;em&gt;(90 days free)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;td&gt;120 GB&lt;/td&gt;
&lt;td&gt;$149/mo &lt;em&gt;(90 days free)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scale&lt;/td&gt;
&lt;td&gt;32 GB&lt;/td&gt;
&lt;td&gt;250 GB&lt;/td&gt;
&lt;td&gt;$299/mo&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power&lt;/td&gt;
&lt;td&gt;64 GB&lt;/td&gt;
&lt;td&gt;500 GB&lt;/td&gt;
&lt;td&gt;$599/mo&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HC-32 → HC-64&lt;/td&gt;
&lt;td&gt;128–256 GB&lt;/td&gt;
&lt;td&gt;1–2 TB&lt;/td&gt;
&lt;td&gt;$1,199–$2,399/mo&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The jump from free to paid is &lt;strong&gt;Plus at $39/month&lt;/strong&gt; — with 90 days free. That gives you 4 GB RAM, 25 GB SSD, always-on, and immediately upgrades your org tier so every project gets more free instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Solo developers.&lt;/strong&gt; Deploy your side project, portfolio, or MVP with a database. Free. Custom domain. SSL. AI code review. Push to deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Startups.&lt;/strong&gt; Run your development and staging environments on free instances. Add a Pro ($79, 90 days free) for production. Your 5-person team uses the platform for free — Git, tasks, deploys, monitoring, docs. Compare to $400/month across GitHub + Jira + Vercel + database host + Datadog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agencies.&lt;/strong&gt; Create a project per client. Most client sites run on free instances with a custom domain. Larger clients upgrade to Plus or Pro for always-on. Invite client stakeholders to their project board for free. Fifteen developers, twelve clients — all free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open-source projects.&lt;/strong&gt; Host your project, track issues, deploy documentation sites, and give contributors AI-reviewed PRs. Free. Apply for our Open Source Partner program for additional instance credits and a free Team plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Students and educators.&lt;/strong&gt; Learn real-world dev workflows — Git, pull requests, CI/CD, databases, monitoring — on a platform that doesn't expire after 30 days. Apply for our Education Partner program for free Team plans and instance credits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Right Now
&lt;/h2&gt;

&lt;p&gt;No pitch. No "schedule a demo." No "talk to sales."&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://monkeys.cloud" rel="noopener noreferrer"&gt;monkeys.cloud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign up (GitHub or Google, 10 seconds)&lt;/li&gt;
&lt;li&gt;Create a project, select your stack&lt;/li&gt;
&lt;li&gt;Push your code&lt;/li&gt;
&lt;li&gt;You have a live URL with a database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The worst that happens is you deploy a project for free and decide it's not for you.&lt;/p&gt;

&lt;p&gt;The best that happens is you replace five tools with one and never context-switch again.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://monkeys.cloud" rel="noopener noreferrer"&gt;monkeys.cloud&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ for the Skeptics
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"How do you make money if the servers are free?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The free instances are small (1–2 GB) and sleep when idle. They cost us very little to run on shared infrastructure. When your project needs always-on production hosting with dedicated compute and more resources, you add paid instances starting at $39/month with 65–79% gross margins. The free tier is the acquisition funnel. The paid tier is the business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What's the catch?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Free instances sleep after 30 minutes. That means a 3–5 second wake time on the first request after sleeping. For production sites where every millisecond matters, you upgrade to a paid always-on instance. For everything else — dev, staging, demos, internal tools, side projects, client previews — the sleep/wake cycle is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Will the free tier disappear?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We reserve the right to modify free tier limits with 30 days' notice (see our Terms). We have no plans to remove the free tier — it's our primary acquisition channel and the core of our growth model. But we're honest: we don't use the phrase "free forever" because we can't predict the future. What we can say is that the free tier is fundamental to how MonkeysCloud works, and removing it would require fundamentally changing the business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How is this different from Railway/Render/Heroku?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Railway gives you a 30-day trial with $5 credit — then you pay per minute. Render's free tier sleeps after 15 minutes with 750 hours/month. Heroku eliminated free dynos entirely. None of them include Git hosting, task management, AI code review, documentation, or monitoring. MonkeysCloud gives you all of that — plus 4 free instances per project — in one integrated platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How is this different from Vercel/Netlify?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vercel and Netlify are frontend-first. They deploy React, Next.js, and static sites. They don't host PHP, Python, Go, Ruby, Java, or Rust backends. They don't run MySQL, PostgreSQL, or Redis. They don't have task management or AI code review. MonkeysCloud is full-stack — 32 frameworks, real databases, real backend hosting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Can I actually run a production site on the free tier?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your site gets intermittent traffic and a 3–5 second wake time is acceptable, yes. Many blogs, portfolios, documentation sites, internal tools, and low-traffic client sites run fine on free instances with a custom domain and SSL. For high-traffic production with instant response, upgrade to Plus ($39/mo, 90 days free).&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Jorge, the founder of MonkeysCloud. I built this because I was tired of paying $400/month for five tools that didn't talk to each other. Now I'm giving away free servers to every developer who wants to ship faster. Questions, feedback, or roasts — drop them in the comments. I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>showdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Stop Bidding Against 200 People for $5 Projects — There's a Better Way</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Tue, 10 Mar 2026 03:08:13 +0000</pubDate>
      <link>https://dev.to/yorchperaza/stop-bidding-against-200-people-for-5-projects-theres-a-better-way-3e0n</link>
      <guid>https://dev.to/yorchperaza/stop-bidding-against-200-people-for-5-projects-theres-a-better-way-3e0n</guid>
      <description>&lt;p&gt;If you've spent any time on traditional freelance platforms, you know the drill.&lt;/p&gt;

&lt;p&gt;You find a project that fits your skills perfectly. You spend 30 minutes crafting a thoughtful proposal. You hit submit — and immediately see you're &lt;strong&gt;proposal #47 out of 50 slots&lt;/strong&gt;. Half of them are undercutting you by 80%. The client ghosts everyone and reposts the job two weeks later.&lt;/p&gt;

&lt;p&gt;It's exhausting. And it doesn't have to be this way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing MonkeysWorks — An AI-Powered Freelance Marketplace Built for Developers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://monkeysworks.com" rel="noopener noreferrer"&gt;MonkeysWorks&lt;/a&gt; is a new kind of freelance platform launching soon, and we're opening the doors early for freelancers who want to get in before the crowd.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://monkeysworks.com/join/freelancers" rel="noopener noreferrer"&gt;👉 Join the Prelaunch Waitlist as a Freelancer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes MonkeysWorks Different?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🤖 AI Does the Heavy Lifting
&lt;/h3&gt;

&lt;p&gt;MonkeysWorks uses AI at every step of the process — not as a gimmick, but to genuinely save you time and surface the right opportunities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smart job matching&lt;/strong&gt; that learns your skills, stack, and preferences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-assisted proposals&lt;/strong&gt; so you spend time doing the work, not selling it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated project scoping&lt;/strong&gt; to help clients define what they actually need before they post&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No more sifting through 300 irrelevant listings looking for the one real opportunity.&lt;/p&gt;

&lt;h3&gt;
  
  
  💻 Built by Developers, for Developers
&lt;/h3&gt;

&lt;p&gt;MonkeysWorks isn't a generic gig marketplace. It's designed for technical freelancers — developers, DevOps engineers, architects, and technical writers — who build real things and deserve to be compensated accordingly.&lt;/p&gt;

&lt;p&gt;The platform supports the full modern stack and understands the difference between "build me a website" and "I need a Next.js frontend with SSR, integrated with a headless CMS and deployed on GCP."&lt;/p&gt;

&lt;h3&gt;
  
  
  💰 Transparent, Fair Pricing
&lt;/h3&gt;

&lt;p&gt;No race to the bottom. MonkeysWorks is designed to reward quality and expertise, not whoever submits the cheapest bid in the first 10 minutes.&lt;/p&gt;

&lt;p&gt;Clients on the platform are vetted and come ready to pay fair rates for serious work.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Get In Early — Early Freelancers Win
&lt;/h3&gt;

&lt;p&gt;Platforms reward early adopters. Getting in before launch means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Higher visibility&lt;/strong&gt; when clients start posting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority access&lt;/strong&gt; to the first wave of projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Influence&lt;/strong&gt; over platform features — we genuinely listen to early members&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founding member status&lt;/strong&gt; with perks we're still cooking up&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Who Should Join?
&lt;/h2&gt;

&lt;p&gt;MonkeysWorks is a great fit if you are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;full-stack or backend developer&lt;/strong&gt; (PHP, Node, Python, Go, you name it)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;frontend developer&lt;/strong&gt; working with React, Vue, or Next.js&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;DevOps / cloud engineer&lt;/strong&gt; comfortable with GCP, AWS, or containerized workloads&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;technical freelancer&lt;/strong&gt; tired of platforms that treat you like a commodity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're serious about your craft and want to work with clients who respect that — this is for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Join
&lt;/h2&gt;

&lt;p&gt;It takes less than a minute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://monkeysworks.com/join/freelancers" rel="noopener noreferrer"&gt;→ Sign up at monkeysworks.com/join/freelancers&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Leave your info, tell us a bit about your stack, and we'll reach out before the public launch with early access details.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;The freelance market isn't broken because there isn't enough work. It's broken because the platforms that connect freelancers to clients were built to maximize transaction volume — not freelancer success.&lt;/p&gt;

&lt;p&gt;MonkeysWorks is being built differently. AI-first, developer-focused, and designed so that the best person for the job actually gets it.&lt;/p&gt;

&lt;p&gt;Come build with us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://monkeysworks.com/join/freelancers" rel="noopener noreferrer"&gt;Join the MonkeysWorks Prelaunch →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions or want to share feedback? Drop a comment below — the team reads everything.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>MonkeysAI — Live Playground (Development Preview)</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Fri, 13 Feb 2026 04:24:10 +0000</pubDate>
      <link>https://dev.to/yorchperaza/monkeysai-live-playground-development-preview-4152</link>
      <guid>https://dev.to/yorchperaza/monkeysai-live-playground-development-preview-4152</guid>
      <description>&lt;p&gt;We just launched something we've been building quietly inside the Monkeys ecosystem:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://monkeyscms.com/#ai-playground" rel="noopener noreferrer"&gt;MonkeysAI — Live Playground&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This isn't a wrapper around a third-party API.&lt;/p&gt;

&lt;p&gt;There are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No OpenAI tokens&lt;/li&gt;
&lt;li&gt;No external proxy layer&lt;/li&gt;
&lt;li&gt;No hidden API dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It runs on our own infrastructure.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is MonkeysAI?
&lt;/h2&gt;

&lt;p&gt;MonkeysAI is the AI layer for &lt;a href="https://monkeyscms.com" rel="noopener noreferrer"&gt;MonkeysCMS&lt;/a&gt; and MonkeysLegion — designed to integrate AI directly into content workflows at the architecture level.&lt;/p&gt;

&lt;p&gt;Not as a plugin. Not as a SaaS add-on. &lt;strong&gt;As core infrastructure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Live Playground exposes the API directly in the browser so developers can test behavior before integrating it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Can Do in the Playground
&lt;/h2&gt;

&lt;p&gt;🧠 &lt;strong&gt;Generate&lt;/strong&gt;&lt;br&gt;
Create new content from a short brief.&lt;/p&gt;

&lt;p&gt;✨ &lt;strong&gt;Improve&lt;/strong&gt;&lt;br&gt;
Enhance clarity, tone, structure, or formatting of existing content.&lt;/p&gt;

&lt;p&gt;🔎 &lt;strong&gt;Critique &amp;amp; Refine&lt;/strong&gt;&lt;br&gt;
Analyze content and automatically improve weak sections.&lt;/p&gt;




&lt;h2&gt;
  
  
  Smart Model Routing
&lt;/h2&gt;

&lt;p&gt;One of the key pieces we built is &lt;strong&gt;dynamic model selection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of forcing users to choose a model manually, we route intelligently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Fast Mode&lt;/strong&gt; → Optimized for speed and lightweight generation&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Reasoning Mode&lt;/strong&gt; → Used for deeper analysis and structured logic&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Auto Mode&lt;/strong&gt; → Automatically selects the best model for the task&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lower cost per request&lt;/li&gt;
&lt;li&gt;Faster response times when complexity isn't required&lt;/li&gt;
&lt;li&gt;Heavy reasoning only when necessary&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Build It This Way?
&lt;/h2&gt;

&lt;p&gt;Most AI integrations today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proxy requests to external APIs&lt;/li&gt;
&lt;li&gt;Add latency&lt;/li&gt;
&lt;li&gt;Lock you into vendor pricing&lt;/li&gt;
&lt;li&gt;Break when token limits change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure control&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Predictable performance&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost transparency&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full integration into CMS-level workflows&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom orchestration&lt;/li&gt;
&lt;li&gt;Model routing logic&lt;/li&gt;
&lt;li&gt;Dedicated servers&lt;/li&gt;
&lt;li&gt;Continuous dataset crawling &amp;amp; validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's slower to build — but architecturally cleaner.&lt;/p&gt;




&lt;h2&gt;
  
  
  Development Preview Notice
&lt;/h2&gt;

&lt;p&gt;This is an early public preview.&lt;/p&gt;

&lt;p&gt;We are actively improving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Response quality&lt;/li&gt;
&lt;li&gt;Crawling verification accuracy&lt;/li&gt;
&lt;li&gt;Performance optimization&lt;/li&gt;
&lt;li&gt;Output stability under load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It's not polished. But it's real.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The Playground is just step one.&lt;/p&gt;

&lt;p&gt;The goal is deeper AI-native CMS features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline multilingual optimization (25+ languages)&lt;/li&gt;
&lt;li&gt;Structured content reasoning&lt;/li&gt;
&lt;li&gt;SEO-aware generation&lt;/li&gt;
&lt;li&gt;AI-assisted migrations&lt;/li&gt;
&lt;li&gt;Editorial workflow automation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;If you're building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CMS&lt;/li&gt;
&lt;li&gt;An AI product&lt;/li&gt;
&lt;li&gt;A content engine&lt;/li&gt;
&lt;li&gt;Or your own LLM infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd genuinely love feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it. Stress it. Break it. Tell me what to improve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://monkeyscms.com/#ai-playground" rel="noopener noreferrer"&gt;monkeyscms.com/#ai-playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Jorge&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>php</category>
      <category>opensource</category>
    </item>
    <item>
      <title>MonkeysLegion GraphQL 2.0 — A Complete Rewrite, Built for the Future</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Wed, 11 Feb 2026 22:30:05 +0000</pubDate>
      <link>https://dev.to/yorchperaza/monkeyslegion-graphql-20-a-complete-rewrite-built-for-the-future-56ea</link>
      <guid>https://dev.to/yorchperaza/monkeyslegion-graphql-20-a-complete-rewrite-built-for-the-future-56ea</guid>
      <description>&lt;p&gt;When I started working on MonkeysLegion GraphQL, the goal was simple:&lt;br&gt;
&lt;strong&gt;Make GraphQL feel native to modern PHP.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Version 2.0 is not an upgrade.&lt;br&gt;
It's a &lt;strong&gt;complete architectural rewrite&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 Repo: &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion-GraphQL" rel="noopener noreferrer"&gt;MonkeysLegion-GraphQL&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why a Complete Rewrite?
&lt;/h2&gt;

&lt;p&gt;GraphQL in PHP often feels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too coupled to webonyx&lt;/li&gt;
&lt;li&gt;Configuration-heavy&lt;/li&gt;
&lt;li&gt;Reflection-expensive&lt;/li&gt;
&lt;li&gt;Hard to secure properly&lt;/li&gt;
&lt;li&gt;Difficult to scale for real production workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of patching the old system… &lt;strong&gt;I rebuilt it from scratch.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What's New in 2.0
&lt;/h2&gt;
&lt;h3&gt;
  
  
  🧩 Attribute-Driven Schema Definition
&lt;/h3&gt;

&lt;p&gt;No more schema arrays. No more manual wiring.&lt;br&gt;
Everything is powered by &lt;strong&gt;PHP 8.4 attributes&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[Type]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserType&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;#[Field]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="na"&gt;#[Field]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="na"&gt;#[Query]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserQuery&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;#[Field]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supported attributes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#[Type]&lt;/code&gt; · &lt;code&gt;#[Field]&lt;/code&gt; · &lt;code&gt;#[Query]&lt;/code&gt; · &lt;code&gt;#[Mutation]&lt;/code&gt; · &lt;code&gt;#[Subscription]&lt;/code&gt; · &lt;code&gt;#[Arg]&lt;/code&gt; · &lt;code&gt;#[InputType]&lt;/code&gt; · &lt;code&gt;#[Enum]&lt;/code&gt; · &lt;code&gt;#[InterfaceType]&lt;/code&gt; · &lt;code&gt;#[UnionType]&lt;/code&gt; · &lt;code&gt;#[Middleware]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Clean. Typed. Explicit.&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚙️ New Architecture (Builder Pipeline)
&lt;/h3&gt;

&lt;p&gt;The entire schema generation system is now modular:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SchemaBuilder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TypeBuilder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FieldBuilder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InputTypeBuilder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EnumBuilder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArgumentBuilder&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No magic factory classes. No static schema definitions. Just a &lt;strong&gt;clean build pipeline&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 GraphQLContext (Immutable Execution Context)
&lt;/h3&gt;

&lt;p&gt;Every request now gets an immutable execution context — HTTP request, authenticated user, DI container, and DataLoaderRegistry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphQLContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Predictable. Safe. Testable.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Performance: DataLoader + Batch Execution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;DataLoader&lt;/strong&gt; — per-request caching + batching. No more N+1 query nightmares.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;batchLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$keys&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Per-request caching&lt;/li&gt;
&lt;li&gt;Batch loading&lt;/li&gt;
&lt;li&gt;Registry-based resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;BatchExecutor&lt;/strong&gt; — supports multiple operations in one request. Useful for complex clients, microfrontend architectures, and optimized API gateways.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔒 Security Built-In
&lt;/h3&gt;

&lt;p&gt;Security is not optional anymore. MonkeysLegion GraphQL 2.0 includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DepthLimiter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ComplexityAnalyzer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IntrospectionControl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PersistedQueries&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RateLimiter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production-ready from day one.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🧱 Custom Scalars Included
&lt;/h3&gt;

&lt;p&gt;Out of the box support for: &lt;code&gt;DateTime&lt;/code&gt; · &lt;code&gt;JSON&lt;/code&gt; · &lt;code&gt;Email&lt;/code&gt; · &lt;code&gt;URL&lt;/code&gt; · &lt;code&gt;Upload&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;No need to reimplement common scalars.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔄 Subscriptions (Modern Protocol)
&lt;/h3&gt;

&lt;p&gt;WebSocket layer completely rewritten.&lt;/p&gt;

&lt;p&gt;❌ Removed &lt;code&gt;subscriptions-transport-ws&lt;/code&gt;&lt;br&gt;
✅ Now using &lt;code&gt;graphql-ws&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;New components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SubscriptionServer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SubscriptionManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PubSubInterface&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;InMemoryPubSub&lt;/code&gt; / &lt;code&gt;RedisPubSub&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WsAuthenticator&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Removed heavy dependencies like Ratchet/React. &lt;strong&gt;Cleaner. Faster. Modern.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  🗂 Schema Caching (PSR-16)
&lt;/h3&gt;

&lt;p&gt;Reflection is expensive. So we cache it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SchemaCache&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SchemaCacheWarmer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CLI commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php mlc graphql:cache:warm
php mlc graphql:cache:clear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Zero-runtime schema rebuilds in production.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🧾 Input Validation System
&lt;/h3&gt;

&lt;p&gt;Built-in validation layer with &lt;code&gt;InputValidator&lt;/code&gt; and &lt;code&gt;RuleSet&lt;/code&gt;. Structured validation errors via &lt;code&gt;ValidationError&lt;/code&gt; and &lt;code&gt;AuthorizationError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No need to plug external validation unless you want to.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔌 PSR-15 Middleware Support
&lt;/h3&gt;

&lt;p&gt;Fully PSR-15 compatible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GraphQLMiddleware&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GraphiQLMiddleware&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UploadMiddleware&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop into any modern PHP stack.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧬 Entity Integration
&lt;/h3&gt;

&lt;p&gt;Native support for mapping domain entities via &lt;code&gt;EntityTypeMapper&lt;/code&gt; and &lt;code&gt;EntityResolver&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Makes MonkeysLegion's entity system work &lt;strong&gt;seamlessly&lt;/strong&gt; with GraphQL.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔗 Relay Pagination Support
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ConnectionType&lt;/code&gt; · &lt;code&gt;EdgeType&lt;/code&gt; · &lt;code&gt;PageInfoType&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ready for modern frontend clients.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧰 CLI Tooling
&lt;/h3&gt;

&lt;p&gt;Built-in CLI commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;graphql:schema:dump&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;graphql:schema:validate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;graphql:cache:warm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;graphql:cache:clear&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;graphql:introspect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GraphQL now feels like a &lt;strong&gt;first-class framework citizen&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ Breaking Changes
&lt;/h2&gt;

&lt;p&gt;This is version 2.0 for a reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changed:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Before → After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Minimum PHP version&lt;/td&gt;
&lt;td&gt;→ &lt;strong&gt;8.4&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket protocol&lt;/td&gt;
&lt;td&gt;→ &lt;strong&gt;graphql-ws&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration&lt;/td&gt;
&lt;td&gt;→ &lt;strong&gt;.mlc files&lt;/strong&gt; (typed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Types&lt;/td&gt;
&lt;td&gt;No longer extend webonyx base classes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Removed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Schema\SchemaFactory&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Support\Scanner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Execution\Executor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WebSocket\WsHandler&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ratchet/React dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything replaced with cleaner architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;This release is about &lt;strong&gt;performance&lt;/strong&gt;, &lt;strong&gt;security&lt;/strong&gt;, &lt;strong&gt;modern PHP design&lt;/strong&gt;, &lt;strong&gt;clean architecture&lt;/strong&gt;, and &lt;strong&gt;production-readiness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's not just "GraphQL working in PHP."&lt;br&gt;
It's GraphQL &lt;strong&gt;designed for serious applications&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Federation support&lt;/li&gt;
&lt;li&gt;Advanced query cost modeling&lt;/li&gt;
&lt;li&gt;Observability hooks&lt;/li&gt;
&lt;li&gt;Native OpenTelemetry integration&lt;/li&gt;
&lt;li&gt;Extended subscription drivers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion GraphQL 2.0 is the foundation for everything I'm building next — AI-native CMS (MonkeysCMS), scalable SaaS APIs, real-time systems, and high-performance microservices.&lt;/p&gt;

&lt;p&gt;Rewriting it was painful. But worth it.&lt;/p&gt;

&lt;p&gt;If you're building serious PHP applications and want modern GraphQL done right:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion-GraphQL" rel="noopener noreferrer"&gt;MonkeysLegion-GraphQL on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>graphql</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>MonkeysLegion: Ship Production-Ready PHP in Minutes, Not Days 🚀</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Wed, 28 Jan 2026 23:51:09 +0000</pubDate>
      <link>https://dev.to/yorchperaza/monkeyslegion-ship-production-ready-php-in-minutes-not-days-45jh</link>
      <guid>https://dev.to/yorchperaza/monkeyslegion-ship-production-ready-php-in-minutes-not-days-45jh</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; MonkeysLegion is a lightweight, modular PHP framework that bundles everything you need—blazing-fast router, DI container, CLI tools, and zero-config Docker—so you can focus on features instead of plumbing. It's MIT-licensed, open-source, and we're looking for collaborators!&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Another PHP Framework?
&lt;/h2&gt;

&lt;p&gt;Let's be honest: setting up a new PHP project can feel like assembling IKEA furniture without instructions. You need routing, dependency injection, database migrations, authentication, validation, caching... and by the time you've wired it all together, you've lost the motivation to build the actual thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MonkeysLegion changes that.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We built it because we were tired of the boilerplate. We wanted something that's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Fast to start&lt;/strong&gt; — One composer command, and you're coding&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;Modular&lt;/strong&gt; — Use only what you need&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Production-ready&lt;/strong&gt; — Built-in auth, validation, caching, and observability&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Well-documented&lt;/strong&gt; — Because code without docs is just legacy waiting to happen&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get Started in 60 Seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project monkeyscloud/monkeyslegion-skeleton my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
php ml key:generate
composer serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://127.0.0.1:8000&lt;/code&gt; — you're live. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the Box?
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion isn't just another micro-framework. It's a complete ecosystem:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;What You Get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP Stack&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PSR-7/15 compliant, middleware pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Routing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Attribute-based, auto-discovery, caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query Builder, Micro-ORM, Migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JWT, RBAC, 2FA, OAuth, API Keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DTO binding with attribute constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;File, Redis, Memcached drivers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Migrations, scaffolding, Tinker REPL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Telemetry&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prometheus metrics, distributed tracing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Code That Speaks for Itself
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Attribute-Based Routing
&lt;/h3&gt;

&lt;p&gt;No more giant route files. Define routes right where they belong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Router\Attributes\Route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Router\Attributes\RoutePrefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Router\Attributes\Middleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;#[RoutePrefix('/api/users')]&lt;/span&gt;
&lt;span class="na"&gt;#[Middleware(['cors', 'throttle'])]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;#[Route('GET', '/', name: 'users.index')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="na"&gt;#[Route('GET', '/{id:\d+}', name: 'users.show')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="na"&gt;#[Route('POST', '/', name: 'users.create')]&lt;/span&gt;
    &lt;span class="na"&gt;#[Middleware('auth')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CreateUserRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Validated DTO is automatically injected&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validation with DTOs
&lt;/h3&gt;

&lt;p&gt;Type-safe request validation using PHP attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Validation\Attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateUserRequest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="na"&gt;#[Assert\NotBlank]&lt;/span&gt;
        &lt;span class="na"&gt;#[Assert\Email]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;#[Assert\NotBlank]&lt;/span&gt;
        &lt;span class="na"&gt;#[Assert\Length(min: 8, max: 64)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;#[Assert\Url]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invalid requests automatically return structured errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Value must be a valid e-mail."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Minimum length is 8 characters."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fluent Query Builder
&lt;/h3&gt;

&lt;p&gt;Write readable database queries without raw SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$qb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;leftJoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'profiles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'p.user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'users.id'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'users.*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'p.avatar'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DESC'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;perPage&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Built-in Authentication
&lt;/h3&gt;

&lt;p&gt;JWT auth with 2FA support, ready to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$auth&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;requires2FA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'requires_2fa'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'challenge'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;challengeToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'access_token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'refresh_token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Powerful CLI Tools
&lt;/h3&gt;

&lt;p&gt;Scaffold your app without leaving the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php ml make:entity User          &lt;span class="c"&gt;# Generate Entity&lt;/span&gt;
php ml make:controller User      &lt;span class="c"&gt;# Generate Controller&lt;/span&gt;
php ml make:migration            &lt;span class="c"&gt;# Generate migration from entity diff&lt;/span&gt;
php ml migrate                   &lt;span class="c"&gt;# Run migrations&lt;/span&gt;
php ml tinker                    &lt;span class="c"&gt;# Interactive REPL&lt;/span&gt;
php ml openapi:export            &lt;span class="c"&gt;# Export OpenAPI spec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Package Ecosystem
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion is built as independent packages. Use the full stack or pick what you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-core&lt;/code&gt; — Kernel, events, helpers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-router&lt;/code&gt; — Full-featured routing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-auth&lt;/code&gt; — JWT, RBAC, 2FA, OAuth&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-query&lt;/code&gt; — Query Builder &amp;amp; ORM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-cache&lt;/code&gt; — PSR-16 multi-driver cache&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-files&lt;/code&gt; — File storage, chunked uploads, S3&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-mail&lt;/code&gt; — SMTP, templates, DKIM, queues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-i18n&lt;/code&gt; — Internationalization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-telemetry&lt;/code&gt; — Metrics &amp;amp; tracing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-validation&lt;/code&gt; — DTO validation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-template&lt;/code&gt; — MLView templating engine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PHP 8.4+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL 8.4&lt;/strong&gt; (recommended)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composer 2.x&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  We Need You! 🤝
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion is open-source and MIT-licensed. We're actively looking for collaborators who want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐛 Report bugs and suggest features&lt;/li&gt;
&lt;li&gt;📝 Improve documentation&lt;/li&gt;
&lt;li&gt;🧪 Write tests&lt;/li&gt;
&lt;li&gt;💡 Build new packages&lt;/li&gt;
&lt;li&gt;🌍 Help with translations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No contribution is too small.&lt;/strong&gt; Whether it's fixing a typo or implementing a new feature, we'd love to have you on board.&lt;/p&gt;

&lt;h2&gt;
  
  
  Join Our Community
&lt;/h2&gt;

&lt;p&gt;We've set up a Slack workspace where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask questions and get help&lt;/li&gt;
&lt;li&gt;Discuss features and architecture&lt;/li&gt;
&lt;li&gt;Share what you're building&lt;/li&gt;
&lt;li&gt;Connect with other developers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://join.slack.com/t/monkeyslegion/shared_invite/zt-36jut3kqo-WCwOabVrVrhHBln4xhMATA" rel="noopener noreferrer"&gt;Join MonkeysLegion on Slack&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://monkeyslegion.com" rel="noopener noreferrer"&gt;monkeyslegion.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://monkeyslegion.com/docs/starter" rel="noopener noreferrer"&gt;monkeyslegion.com/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion-Skeleton" rel="noopener noreferrer"&gt;MonkeysCloud/MonkeysLegion-Skeleton&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Packagist:&lt;/strong&gt; &lt;a href="https://packagist.org/packages/monkeyscloud/monkeyslegion-skeleton" rel="noopener noreferrer"&gt;monkeyscloud/monkeyslegion-skeleton&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get Started Now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project monkeyscloud/monkeyslegion-skeleton my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give it a ⭐ on GitHub if you like what you see!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with 💚 by developers who believe PHP deserves better tooling.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What do you think?&lt;/strong&gt; Have you tried MonkeysLegion? What features would you like to see? Drop a comment below or join us on Slack! 👇&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>ai</category>
      <category>laravel</category>
    </item>
    <item>
      <title>🧠 MonkeysCMS: An AI-Native CMS Built on MonkeysLegion — Rethinking Content Platforms from the Core</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Wed, 07 Jan 2026 02:12:47 +0000</pubDate>
      <link>https://dev.to/yorchperaza/monkeyscms-an-ai-native-cms-built-on-monkeyslegion-rethinking-content-platforms-from-the-core-3dpj</link>
      <guid>https://dev.to/yorchperaza/monkeyscms-an-ai-native-cms-built-on-monkeyslegion-rethinking-content-platforms-from-the-core-3dpj</guid>
      <description>&lt;p&gt;Most CMS platforms are built on top of frameworks they don't control.&lt;/p&gt;

&lt;p&gt;That single fact explains why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extensibility feels bolted-on&lt;/li&gt;
&lt;li&gt;Performance tuning is painful&lt;/li&gt;
&lt;li&gt;AI integration feels awkward&lt;/li&gt;
&lt;li&gt;Developer experience degrades over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MonkeysCMS exists because we chose a different path.&lt;/p&gt;

&lt;p&gt;Instead of adapting a CMS to a framework,&lt;br&gt;
we built a framework specifically to power a modern, AI-native CMS.&lt;/p&gt;

&lt;p&gt;That framework is &lt;strong&gt;MonkeysLegion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 CMS repo: &lt;a href="https://github.com/MonkeysCloud/MonkeysCMS" rel="noopener noreferrer"&gt;https://github.com/MonkeysCloud/MonkeysCMS&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 Framework ecosystem: &lt;a href="https://github.com/MonkeysCloud" rel="noopener noreferrer"&gt;https://github.com/MonkeysCloud&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🚨 Why Existing CMS Architectures Hit a Ceiling
&lt;/h2&gt;

&lt;p&gt;Modern content systems must handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Headless &amp;amp; hybrid rendering&lt;/li&gt;
&lt;li&gt;API-first delivery&lt;/li&gt;
&lt;li&gt;Multi-channel publishing&lt;/li&gt;
&lt;li&gt;Automation-heavy workflows&lt;/li&gt;
&lt;li&gt;AI-assisted content creation&lt;/li&gt;
&lt;li&gt;Long-term maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yet most CMS platforms fall into one of these traps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ Legacy CMS (Drupal, WordPress, etc.)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Powerful but heavy&lt;/li&gt;
&lt;li&gt;Tight coupling everywhere&lt;/li&gt;
&lt;li&gt;AI added as plugins&lt;/li&gt;
&lt;li&gt;Framework constraints leak into CMS logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❌ Modern SaaS Headless CMS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Closed source&lt;/li&gt;
&lt;li&gt;Usage-based pricing explosions&lt;/li&gt;
&lt;li&gt;Limited extensibility&lt;/li&gt;
&lt;li&gt;No control over core behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The missing piece?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A CMS built on a framework designed explicitly for content systems and automation.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🐒 Enter MonkeysLegion: The Foundation
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion is a modern, modular backend framework built with one goal:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give developers full control over architecture without sacrificing productivity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightweight&lt;/li&gt;
&lt;li&gt;Explicit&lt;/li&gt;
&lt;li&gt;Modular&lt;/li&gt;
&lt;li&gt;Framework-agnostic&lt;/li&gt;
&lt;li&gt;Built for APIs, automation, and extensibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MonkeysCMS is not using MonkeysLegion accidentally —&lt;br&gt;
it is a direct expression of its design philosophy.&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ Why Build a CMS on Its Own Framework?
&lt;/h2&gt;

&lt;p&gt;Because CMSs are not just CRUD apps.&lt;/p&gt;

&lt;p&gt;They need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong domain modeling&lt;/li&gt;
&lt;li&gt;Pluggable logic layers&lt;/li&gt;
&lt;li&gt;Predictable extension points&lt;/li&gt;
&lt;li&gt;Performance control&lt;/li&gt;
&lt;li&gt;AI integration at the core&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MonkeysLegion provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧱 Modular packages (Router, Auth, Cache, Query, CLI, etc.)&lt;/li&gt;
&lt;li&gt;🔌 Clean dependency boundaries&lt;/li&gt;
&lt;li&gt;🧠 Explicit data flow (no magic globals)&lt;/li&gt;
&lt;li&gt;🧪 Testable components by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lets MonkeysCMS stay:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean&lt;/li&gt;
&lt;li&gt;Maintainable&lt;/li&gt;
&lt;li&gt;Extensible without hacks&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🧩 Architecture Overview: CMS + Framework + AI
&lt;/h2&gt;

&lt;p&gt;Think of the system like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐
│   Frontend  │  (Next.js, Astro, Svelte, Apps)
└─────▲───────┘
      │ API
┌─────┴───────┐
│  MonkeysCMS │  Content, Schemas, Workflows
└─────▲───────┘
      │ Core Services
┌─────┴───────┐
│ MonkeysLegion│  Routing, Auth, Storage, CLI
└─────▲───────┘
      │ Intelligence
┌─────┴───────┐
│  MonkeysAI  │  AI workflows &amp;amp; automation
└─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer has one responsibility — and nothing leaks.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 MonkeysAI: AI Where It Actually Belongs
&lt;/h2&gt;

&lt;p&gt;Because MonkeysCMS is built on MonkeysLegion, AI isn't a hack.&lt;/p&gt;

&lt;p&gt;MonkeysAI integrates cleanly into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content pipelines&lt;/li&gt;
&lt;li&gt;Workflow automation&lt;/li&gt;
&lt;li&gt;Schema interpretation&lt;/li&gt;
&lt;li&gt;Semantic enrichment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context-aware content suggestions&lt;/li&gt;
&lt;li&gt;AI-assisted editing (not replacement)&lt;/li&gt;
&lt;li&gt;Automated content operations&lt;/li&gt;
&lt;li&gt;Future schema-aware generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI becomes a system capability, not a plugin checkbox.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Developer Experience Is a First-Class Feature
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion enforces discipline that benefits MonkeysCMS developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No hidden container magic&lt;/li&gt;
&lt;li&gt;No global state abuse&lt;/li&gt;
&lt;li&gt;Clear service contracts&lt;/li&gt;
&lt;li&gt;Predictable lifecycle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a contributor, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can understand modules in isolation&lt;/li&gt;
&lt;li&gt;You don't need to learn "CMS magic"&lt;/li&gt;
&lt;li&gt;Your changes don't break unrelated systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I hate fighting the framework just to add a feature"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This stack was built for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Modular by Nature (Not by Marketing)
&lt;/h2&gt;

&lt;p&gt;Because MonkeysCMS inherits MonkeysLegion's modularity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every CMS feature is a module&lt;/li&gt;
&lt;li&gt;AI capabilities are pluggable&lt;/li&gt;
&lt;li&gt;APIs are versionable&lt;/li&gt;
&lt;li&gt;Integrations are isolated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom deployments easy&lt;/li&gt;
&lt;li&gt;Enterprise use viable&lt;/li&gt;
&lt;li&gt;Long-term maintenance realistic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No forks. No hacks. No rewrites.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;This architecture is ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-driven content platforms&lt;/li&gt;
&lt;li&gt;SaaS documentation hubs&lt;/li&gt;
&lt;li&gt;Multi-brand editorial systems&lt;/li&gt;
&lt;li&gt;Investor/startup platforms&lt;/li&gt;
&lt;li&gt;Knowledge bases &amp;amp; research portals&lt;/li&gt;
&lt;li&gt;CMS + automation hybrids&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anywhere content needs to be &lt;strong&gt;structured, automated, and intelligent.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🧑‍💻 Contributing: CMS, Framework, or AI
&lt;/h2&gt;

&lt;p&gt;You don't need to contribute to everything.&lt;/p&gt;

&lt;p&gt;You can help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MonkeysCMS features&lt;/li&gt;
&lt;li&gt;MonkeysLegion core packages&lt;/li&gt;
&lt;li&gt;MonkeysAI workflows&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;DX improvements&lt;/li&gt;
&lt;li&gt;Integration examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is an ecosystem — not a monolith.&lt;/p&gt;

&lt;p&gt;👉 Start here: &lt;a href="https://github.com/MonkeysCloud/MonkeysCMS" rel="noopener noreferrer"&gt;https://github.com/MonkeysCloud/MonkeysCMS&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛣️ What's Ahead
&lt;/h2&gt;

&lt;p&gt;Roadmap highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual schema builders&lt;/li&gt;
&lt;li&gt;AI-powered content ops&lt;/li&gt;
&lt;li&gt;Plugin marketplace&lt;/li&gt;
&lt;li&gt;Advanced workflow automation&lt;/li&gt;
&lt;li&gt;Multi-tenant support&lt;/li&gt;
&lt;li&gt;Performance-focused deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All driven by MonkeysLegion's stability and AI-native design.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Final Thought
&lt;/h2&gt;

&lt;p&gt;MonkeysCMS is not "just another CMS".&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CMS powered by its own framework&lt;/li&gt;
&lt;li&gt;An AI-native content platform&lt;/li&gt;
&lt;li&gt;A long-term open-source ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Framework first. CMS second. AI everywhere it makes sense.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If this vision resonates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ Star the repo&lt;/li&gt;
&lt;li&gt;🧠 Join the discussions&lt;/li&gt;
&lt;li&gt;🧑‍💻 Build with us&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's build the CMS — and the foundation beneath it — the right way 🐒🚀&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>symfony</category>
      <category>opensource</category>
    </item>
    <item>
      <title>MonkeysCMS + MonkeysAI: Building the First AI-Native Open CMS 🚀</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Sat, 03 Jan 2026 02:58:47 +0000</pubDate>
      <link>https://dev.to/yorchperaza/monkeyscms-monkeysai-building-the-first-ai-native-open-cms-p23</link>
      <guid>https://dev.to/yorchperaza/monkeyscms-monkeysai-building-the-first-ai-native-open-cms-p23</guid>
      <description>&lt;p&gt;A few weeks ago, I shared an article about &lt;a href="https://dev.to/yorchperaza/build-the-cms-we-always-wanted-monkeyscms-contributors-wanted-4oed"&gt;MonkeysCMS&lt;/a&gt; and the idea of building the CMS we always wanted. &lt;br&gt;
The response was clear: many of us feel the same frustration with today's CMS landscape.&lt;/p&gt;

&lt;p&gt;Now, the journey is taking its next step.&lt;/p&gt;

&lt;p&gt;We're actively developing MonkeysAI, and it's not a side project. It's becoming a core part of MonkeysCMS.&lt;/p&gt;

&lt;p&gt;This document is an invitation — not just to use another CMS, but to help define the first truly AI-native, &lt;br&gt;
developer-first open CMS.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why AI Inside the CMS
&lt;/h2&gt;

&lt;p&gt;Most CMS platforms treat AI as a plugin, an external API call, or a paid add-on.&lt;br&gt;
We're doing the opposite.&lt;/p&gt;

&lt;p&gt;MonkeysAI is designed as part of the CMS architecture, so AI works with your content model, not around it.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI that understands your content types&lt;/li&gt;
&lt;li&gt;Context-aware generation&lt;/li&gt;
&lt;li&gt;SEO optimization inside the editor&lt;/li&gt;
&lt;li&gt;Full control for teams&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What MonkeysAI Brings
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI-assisted content creation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Draft generation from structured fields&lt;/li&gt;
&lt;li&gt;Rewrite, expand, summarize, localize&lt;/li&gt;
&lt;li&gt;Consistent tone and brand voice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SEO as a first-class citizen:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Title and meta generation&lt;/li&gt;
&lt;li&gt;Semantic keyword suggestions&lt;/li&gt;
&lt;li&gt;Readability and structure feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Structured, not magical:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respects schemas and taxonomies&lt;/li&gt;
&lt;li&gt;Developers decide where AI is allowed&lt;/li&gt;
&lt;li&gt;Editors stay in control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Open and extensible:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No vendor lock-in&lt;/li&gt;
&lt;li&gt;Pluggable providers&lt;/li&gt;
&lt;li&gt;Production-ready design&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Build in the Open
&lt;/h2&gt;

&lt;p&gt;MonkeysCMS is not another WordPress clone or a closed SaaS.&lt;br&gt;
It's a framework-driven CMS built for developers who care about architecture and clean APIs.&lt;/p&gt;

&lt;p&gt;AI should enhance control, not replace it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Should Join
&lt;/h2&gt;

&lt;p&gt;We welcome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend and frontend developers&lt;/li&gt;
&lt;li&gt;AI engineers&lt;/li&gt;
&lt;li&gt;CMS architects&lt;/li&gt;
&lt;li&gt;SEO specialists and writers&lt;/li&gt;
&lt;li&gt;Open-source contributors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need to know everything — just care about building something better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Early Stage, Real Impact
&lt;/h2&gt;

&lt;p&gt;MonkeysCMS + MonkeysAI is still being shaped.&lt;br&gt;
Early contributors will influence architecture and define how AI fits real CMS workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Join the Journey
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/MonkeysCloud/MonkeysCMS" rel="noopener noreferrer"&gt;https://github.com/MonkeysCloud/MonkeysCMS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're building the next generation of content systems — open, intelligent, and human-centric.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Built on:&lt;/strong&gt; &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion-Skeleton" rel="noopener noreferrer"&gt;MonkeysLegion Framework&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions? Drop a comment below or open an issue on GitHub. Let's build something great together.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Slack: &lt;a href="https://join.slack.com/t/monkeyslegion/shared_invite/zt-36jut3kqo-WCwOabVrVrhHBln4xhMATA" rel="noopener noreferrer"&gt;https://join.slack.com/t/monkeyslegion/shared_invite/zt-36jut3kqo-WCwOabVrVrhHBln4xhMATA&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>opensource</category>
      <category>php</category>
    </item>
    <item>
      <title>Build the CMS we always wanted: MonkeysCMS (contributors wanted)</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Sun, 21 Dec 2025 22:57:57 +0000</pubDate>
      <link>https://dev.to/yorchperaza/build-the-cms-we-always-wanted-monkeyscms-contributors-wanted-4oed</link>
      <guid>https://dev.to/yorchperaza/build-the-cms-we-always-wanted-monkeyscms-contributors-wanted-4oed</guid>
      <description>&lt;p&gt;There are two kinds of CMS projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ones that are &lt;em&gt;easy&lt;/em&gt; until you need real structure, relationships, permissions, and performance.&lt;/li&gt;
&lt;li&gt;The ones that are &lt;em&gt;powerful&lt;/em&gt; but come with a heavy config surface, fragile update paths, and a lot of "CMS ceremony".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MonkeysCMS is my attempt to take the best ideas from Drupal and WordPress and rebuild them in a modern, code-first way—on top of the &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion-Skeleton" rel="noopener noreferrer"&gt;MonkeysLegion framework&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you like building systems (entities, schema, permissions, content modeling, tooling), I'd love your help shaping this into a real community CMS.&lt;/p&gt;




&lt;h2&gt;
  
  
  What MonkeysCMS is (in plain terms)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MonkeysCMS is a modern CMS where content types are PHP classes.&lt;/strong&gt; No YAML stacks, no UI-first configuration as your source of truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  The core ideas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code-first entities with PHP attributes&lt;/strong&gt;: define content types as typed PHP classes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-sync schema&lt;/strong&gt;: enable a module → tables are created immediately (no migrations or extra CLI steps for schema updates).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True modularity&lt;/strong&gt;: isolated modules with namespaces, dependencies, and lifecycle hooks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Normalized relational database&lt;/strong&gt; (with proper relationships), avoiding the classic EAV pain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API-first&lt;/strong&gt;: REST endpoints for CMS operations (content, users, permissions, taxonomy, menus, settings, modules…).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern PHP (8.4+)&lt;/strong&gt; with strict types and modern patterns.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Current progress (what's already in the repo)
&lt;/h2&gt;

&lt;p&gt;This isn't a blank README—the repository already documents and scaffolds several major CMS building blocks:&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Modules + schema sync
&lt;/h3&gt;

&lt;p&gt;Enabling a module triggers discovery of entity classes, reads their attributes, generates SQL, and executes schema creation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Entity system (attributes + field types + relations)
&lt;/h3&gt;

&lt;p&gt;There's an attribute-based model for &lt;code&gt;ContentType&lt;/code&gt;, &lt;code&gt;Field&lt;/code&gt;, &lt;code&gt;Relation&lt;/code&gt;, and &lt;code&gt;Id&lt;/code&gt;, plus a mapped set of field types.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) RBAC inspired by Drupal (but simplified)
&lt;/h3&gt;

&lt;p&gt;System roles and permission patterns are defined and documented, including auto-registration of entity permissions on module enable.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Taxonomy system (Drupal-like)
&lt;/h3&gt;

&lt;p&gt;Vocabularies, hierarchical terms, and entity-term assignment are part of the documented design.&lt;/p&gt;

&lt;h3&gt;
  
  
  5) Menus, settings, CLI tooling, caching, themes
&lt;/h3&gt;

&lt;p&gt;The README outlines endpoints and CLI commands (&lt;code&gt;./monkeys ...&lt;/code&gt;), caching via MonkeysLegion-Cache, and a theme system driven by &lt;code&gt;.mlc&lt;/code&gt; configuration.&lt;/p&gt;

&lt;p&gt;And yes: it's early-stage—right now the GitHub repo shows &lt;strong&gt;0 issues and 0 PRs&lt;/strong&gt;, which means contributors can still heavily influence the architecture and conventions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I'm inviting contributors &lt;em&gt;now&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Because this is the sweet spot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The direction is clear (code-first, modular, relational, API-first).&lt;/li&gt;
&lt;li&gt;The foundation exists.&lt;/li&gt;
&lt;li&gt;The project hasn't ossified into "that's how it's always been".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever wished you could build a CMS with the &lt;em&gt;structure&lt;/em&gt; of Drupal but the &lt;em&gt;developer experience&lt;/em&gt; of modern frameworks, this is that moment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where you can contribute (high-impact areas)
&lt;/h2&gt;

&lt;p&gt;Here are contribution tracks that would immediately move MonkeysCMS forward:&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend / Core
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Harden the schema generator (indexes, constraints, diffs, safe updates)&lt;/li&gt;
&lt;li&gt;Entity hydration/serialization edge cases&lt;/li&gt;
&lt;li&gt;Relations: many-to-many helpers, cascade rules, eager loading patterns&lt;/li&gt;
&lt;li&gt;Permissions middleware + auditability (deny reasons, permission tracing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Admin API &amp;amp; DX
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Consistency: error formats, pagination conventions, filtering/sorting spec&lt;/li&gt;
&lt;li&gt;API docs (OpenAPI), example clients, Postman collections&lt;/li&gt;
&lt;li&gt;CLI improvements around module lifecycle and schema preview&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Content modeling features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Revisions, publishing workflows, moderation states (the README already hints at publishable/revisionable concepts via attributes)&lt;/li&gt;
&lt;li&gt;Media handling (image/file/gallery field types)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Themes &amp;amp; rendering
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Improve the theme manager + template engine integration&lt;/li&gt;
&lt;li&gt;Starter themes and real-world example templates&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Unit + integration coverage (schema generation, permissions checks, taxonomy trees, CRUD flows)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick start (run it locally)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Requirements:&lt;/strong&gt; PHP 8.4+, MySQL 8+ or SQLite, Composer 2.x.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/MonkeysCloud/MonkeysCMS.git
&lt;span class="nb"&gt;cd &lt;/span&gt;monkeyscms
composer &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env
composer serve
&lt;span class="c"&gt;# or&lt;/span&gt;
./monkeys serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How to contribute
&lt;/h2&gt;

&lt;p&gt;The current contribution flow is simple: fork → branch → PR.&lt;/p&gt;

&lt;p&gt;If you want something easy to start with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick one small improvement in docs or tests&lt;/li&gt;
&lt;li&gt;Open a PR&lt;/li&gt;
&lt;li&gt;We'll shape conventions together as we go&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The vision (what we're building toward)
&lt;/h2&gt;

&lt;p&gt;A CMS that feels like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You define content models like you define application code.&lt;/li&gt;
&lt;li&gt;Enabling a module makes the system real immediately (schema + permissions + API).&lt;/li&gt;
&lt;li&gt;You get "CMS power" (content types, taxonomy, menus, blocks, roles) without legacy complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that resonates, jump in.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/MonkeysCloud/MonkeysCMS" rel="noopener noreferrer"&gt;MonkeysCloud/MonkeysCMS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built on:&lt;/strong&gt; &lt;a href="https://github.com/MonkeysCloud/MonkeysLegion-Skeleton" rel="noopener noreferrer"&gt;MonkeysLegion Framework&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions? Drop a comment below or open an issue on GitHub. Let's build something great together.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Slack: &lt;a href="https://join.slack.com/t/monkeyslegion/shared_invite/zt-36jut3kqo-WCwOabVrVrhHBln4xhMATA" rel="noopener noreferrer"&gt;https://join.slack.com/t/monkeyslegion/shared_invite/zt-36jut3kqo-WCwOabVrVrhHBln4xhMATA&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>opensource</category>
      <category>cms</category>
      <category>webdev</category>
    </item>
    <item>
      <title>MonkeysLegion 1.0.8: From Side Project to Production-Ready PHP Ecosystem</title>
      <dc:creator>Jorge Peraza</dc:creator>
      <pubDate>Sun, 23 Nov 2025 20:28:52 +0000</pubDate>
      <link>https://dev.to/yorchperaza/monkeyslegion-108-from-side-project-to-production-ready-php-ecosystem-27j5</link>
      <guid>https://dev.to/yorchperaza/monkeyslegion-108-from-side-project-to-production-ready-php-ecosystem-27j5</guid>
      <description>&lt;p&gt;When I first introduced MonkeysLegion on the internet, it was “just” a new PHP 8 framework with a bold promise:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let modern teams move from commit to cloud without the boilerplate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Back then, that promise was mostly about vision. The core worked, but a lot was still in motion: APIs were evolving, packages were experimental, and I was shipping fast to prove the ideas.&lt;/p&gt;

&lt;p&gt;Fast-forward to v1.0.8 of the framework (and beyond into 1.0.x), and &lt;a href="https://monkeyslegion.com/" rel="noopener noreferrer"&gt;MonkeysLegion&lt;/a&gt; has grown into a mature, modular ecosystem: a set of focused packages, a production-ready skeleton, batteries-included tooling, and opinionated patterns that have already been battle-tested in real projects. &lt;/p&gt;

&lt;p&gt;This article is about that journey:&lt;/p&gt;

&lt;p&gt;What MonkeysLegion is in practice&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How it helps you ship production code faster&lt;/li&gt;
&lt;li&gt;What changed from 1.0.0 to the 1.0.8+ line&lt;/li&gt;
&lt;li&gt;Why I believe it’s now a solid choice for real apps&lt;/li&gt;
&lt;li&gt;How you can contribute and help shape where it goes next&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is MonkeysLegion, really?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://monkeyslegion.com/" rel="noopener noreferrer"&gt;MonkeysLegion&lt;/a&gt; is a feather-light, modular PHP 8+ framework designed to bridge the gap between “micro-frameworks that make you build everything yourself” and “mega-frameworks that feel like a monolith.”&lt;/p&gt;

&lt;p&gt;The official homepage describes it as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A lightweight, modular framework that lets modern teams move from commit to cloud without the boilerplate.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practical terms, a MonkeysLegion application is built on top of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PSR-11 DI container with configuration-first definitions&lt;/li&gt;
&lt;li&gt;PSR-7/15 HTTP stack (requests, responses, middleware, emitter)&lt;/li&gt;
&lt;li&gt;An attribute-based router with auto-discovery (no giant route files)&lt;/li&gt;
&lt;li&gt;Validation layer with DTO binding + attribute constraints&lt;/li&gt;
&lt;li&gt;MLView component templating (Blade-style but framework-native)&lt;/li&gt;
&lt;li&gt;CLI toolbox for migrations, cache, key-gen, scaffolding&lt;/li&gt;
&lt;li&gt;An Entity → Migration SQL diff generator for schema evolution &lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that, the ecosystem includes first-party packages for auth, query builder &amp;amp; micro-ORM, files &amp;amp; storage, Stripe payments, logging, dev server with hot-reload, telemetry, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why another PHP framework?
&lt;/h2&gt;

&lt;p&gt;This is the question I answered when announcing v1.0.0 on dev.to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MonkeysLegion was built for teams who want lightning-fast performance without the heavyweight abstractions of legacy stacks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The goal isn’t to “beat” Laravel or Symfony. The goal is to serve teams that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want fast startup with production-ready defaults&lt;/li&gt;
&lt;li&gt;Prefer explicit configuration over magical conventions&lt;/li&gt;
&lt;li&gt;Need cloud-native patterns (observability, rate-limiting, OpenAPI) baked in&lt;/li&gt;
&lt;li&gt;Care about maintainable, typed code and clean separation of boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MonkeysLegion wants you to move quickly without paying for that speed later in unmaintainable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  From v1.0.0 to v1.0.8+: what actually matured?
&lt;/h2&gt;

&lt;p&gt;Version numbers can be misleading. “1.0.0” is technically stable, but in most OSS projects 1.0.0 is when the real testing begins. That was true for MonkeysLegion as well.&lt;/p&gt;

&lt;p&gt;Between 1.0.0 and 1.0.8+, a lot changed:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A real ecosystem of first-party packages
&lt;/h3&gt;

&lt;p&gt;What started as a single repo has evolved into a set of focused, versioned packages, all published on Packagist under the monkeyscloud/monkeyslegion-* namespace.&lt;/p&gt;

&lt;p&gt;Some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-core&lt;/code&gt; – kernel, events, core helpers &lt;/li&gt;
&lt;li&gt;Libraries.io&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-http&lt;/code&gt; &amp;amp; &lt;code&gt;monkeyslegion-router&lt;/code&gt; – PSR-7/15 stack + attribute router&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-query&lt;/code&gt; – Query Builder &amp;amp; Micro-ORM, PDO-based, with advanced queries, transactions, repositories, pagination, streaming, and support for MySQL, PostgreSQL, SQLite &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-auth&lt;/code&gt; – drop-in JWT-based auth &amp;amp; authorization layer &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-files&lt;/code&gt; – file storage &amp;amp; uploads abstraction (local/S3/GCS) &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-dev-server&lt;/code&gt; – hot-reload dev server built on PHP’s native web server &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-logger&lt;/code&gt; – advanced logger using PSR-3 &amp;amp; Monolog, fully typed &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monkeyslegion-stripe&lt;/code&gt; – first-class Stripe integration for payments and subscriptions &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This modularization is a big part of “maturity”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can opt into only what you need.&lt;/li&gt;
&lt;li&gt;Each package has its own versioning &amp;amp; changelog.&lt;/li&gt;
&lt;li&gt;Bugs can be fixed and features shipped without destabilizing the framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. A production-ready skeleton
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;MonkeysLegion Skeleton&lt;/strong&gt; repo is now the canonical way to start a project. It’s a “batteries-included” starter that wires together the DI container, HTTP stack, router, validation, rate-limiter, MLView templating, CLI, and swagger/OpenAPI out of the box. &lt;br&gt;
GitHub&lt;/p&gt;

&lt;p&gt;That means you can go from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer create-project "monkeyscloud/monkeyslegion-skeleton" my-app
cd my-app
php vendor/bin/ml serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…to a running app with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; → home route&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/docs&lt;/code&gt; → live Swagger UI&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/openapi.json&lt;/code&gt; → generated OpenAPI 3.1 spec&lt;/li&gt;
&lt;li&gt;Ready-to-extend config files for DI, routes, and environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t have to “assemble a framework” yourself; you start from something that has already been battle-tested in real projects, then customize.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dev experience: the hot-reload dev server
&lt;/h3&gt;

&lt;p&gt;One of the biggest jumps in DX has been the MonkeysLegion Dev Server (&lt;code&gt;monkeyslegion-dev-server&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Instead of manually running php -S and restarting when files change, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start, stop, restart, status commands&lt;/li&gt;
&lt;li&gt;Zero configuration for standard MonkeysLegion apps&lt;/li&gt;
&lt;li&gt;Two hot-reload modes:

&lt;ul&gt;
&lt;li&gt;entr (recommended) for efficient file watching&lt;/li&gt;
&lt;li&gt;Built-in PHP watcher for environments where entr isn’t available&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;It’s a small thing, but it turns the framework into something that feels modern:&lt;br&gt;
save → server restarts → refresh → done.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Database layer that grew up
&lt;/h3&gt;

&lt;p&gt;The Query Builder &amp;amp; Micro-ORM (monkeyslegion-query) has probably seen the most action. Originally a simple fluent wrapper over PDO, it has matured into a package that can stand on its own: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fluent, chainable API (&lt;code&gt;select&lt;/code&gt;, &lt;code&gt;join&lt;/code&gt;, &lt;code&gt;where&lt;/code&gt;, &lt;code&gt;groupBy&lt;/code&gt;, &lt;code&gt;having&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic parameter binding&lt;/strong&gt; to prevent SQL injection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction support&lt;/strong&gt; including savepoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced queries&lt;/strong&gt;: joins, subqueries, unions, CTEs&lt;/li&gt;
&lt;li&gt;Built-in &lt;strong&gt;Repository pattern&lt;/strong&gt; with typed entities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming &amp;amp; chunking&lt;/strong&gt; for large datasets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pagination&lt;/strong&gt; helpers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve ever bounced between “raw PDO” and “too heavy ORM”, this is meant to fit the sweet spot: expressive yet predictable.&lt;/p&gt;
&lt;h2&gt;
  
  
  How MonkeysLegion helps you develop faster
&lt;/h2&gt;

&lt;p&gt;Let’s turn all this into concrete benefits for day-to-day development.&lt;/p&gt;
&lt;h3&gt;
  
  
  . 1. Clear separation of concerns
&lt;/h3&gt;

&lt;p&gt;Because the framework is organized around PSR standards and explicit boundaries, your code tends to naturally fall into clean layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actions / Controllers handle HTTP&lt;/li&gt;
&lt;li&gt;Services encapsulate business logic&lt;/li&gt;
&lt;li&gt;Repositories handle persistence via &lt;code&gt;monkeyslegion-query&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DTOs + Validators guard your input and output&lt;/li&gt;
&lt;li&gt;Templates (MLView) render views&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes it easier to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write unit tests for each layer&lt;/li&gt;
&lt;li&gt;Swap implementations (e.g., DB driver, storage backend)&lt;/li&gt;
&lt;li&gt;Onboard new team members (“here’s where we put X”)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. A router that stays out of your way
&lt;/h3&gt;

&lt;p&gt;The attribute-based router lets you define routes right next to your controllers, with typed signatures and middleware attributes.&lt;/p&gt;

&lt;p&gt;A typical action might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MonkeysLegion\Http\Attributes\Route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Http\Message\ResponseInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Http\Message\ServerRequestInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloAction&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;#[Route('GET', '/hello')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ServerRequestInterface&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;ResponseInterface&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getQueryParams&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ml_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No massive routing file. No “route caching dance.” Just attributes, auto-discovery, and type-safe handlers.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Built-in OpenAPI &amp;amp; documentation
&lt;/h3&gt;

&lt;p&gt;Because the skeleton comes with OpenAPI 3.1 generation and Swagger UI, documenting your APIs isn’t an afterthought.&lt;/p&gt;

&lt;p&gt;As you annotate your handlers and DTOs, the framework can produce machine-readable API specs, which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better collaboration with frontend teams&lt;/li&gt;
&lt;li&gt;Easier client generation&lt;/li&gt;
&lt;li&gt;Less outdated docs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Cloud-native mindset from day one
&lt;/h3&gt;

&lt;p&gt;From rate-limiting to telemetry, MonkeysLegion pushes you toward cloud-ready defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sliding-window rate limiter (IP + user buckets) baked into the skeleton &lt;/li&gt;
&lt;li&gt;Clear HTTP and middleware boundaries (easy to integrate with gateways)&lt;/li&gt;
&lt;li&gt;Logging &amp;amp; telemetry packages that play nicely with modern observability stacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result: your app is more prepared for real production traffic without heroic refactors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 1.0.8+ feels “mature”
&lt;/h2&gt;

&lt;p&gt;So, what does “maturity” mean in this context?&lt;/p&gt;

&lt;p&gt;For MonkeysLegion, maturity at 1.0.8+ looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stable public APIs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Core contracts and namespaces are no longer moving targets.&lt;/li&gt;
&lt;li&gt;SemVer is respected: breaking changes are either postponed or properly versioned.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Package ecosystem instead of a monolith&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Auth, Stripe, Files, Query, Dev Server, Logger, Telemetry, etc. all live in their own repos and versions. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Used in real projects&lt;/strong&gt; (not just demos)

&lt;ul&gt;
&lt;li&gt;The skeleton and core packages are already running production apps, which has driven fixes around performance, DX, and edge cases. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Tooling that feels cohesive&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Dev server, CLI, migrations, OpenAPI, auth, logging — all share the same design language and conventions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;MonkeysLegion has moved from “cool framework on paper” to “this is something you can actually bet a project on.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started in a few minutes
&lt;/h2&gt;

&lt;p&gt;If you want to try it out, here’s the quickest path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer create-project "monkeyscloud/monkeyslegion-skeleton" my-app
cd my-app

# Start dev server (hot-reload if `entr` is installed)
composer serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;http://127.0.0.1:8000&lt;/code&gt; in your browser, and you should see the default skeleton app with docs and health routes ready to go&lt;/p&gt;

&lt;h2&gt;
  
  
  How to contribute (and why I’d love your help)
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion is MIT licensed and lives on GitHub at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;github.com/MonkeysCloud/MonkeysLegion-Skeleton&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
There are lots of ways to help, even if you don’t consider yourself a “framework author”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use it on a side project and open issues when you hit rough edges.&lt;/li&gt;
&lt;li&gt;Contribute to docs, examples, and guides — these matter as much as code.&lt;/li&gt;
&lt;li&gt;Help improve integrations (Stripe, queues, 3rd-party APIs). &lt;/li&gt;
&lt;li&gt;Add starter templates: API-only, multi-tenant SaaS, headless CMS, etc.&lt;/li&gt;
&lt;li&gt;Help with benchmarks, performance profiling, and comparisons.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve ever wanted to influence how a modern PHP framework feels — especially one that cares about &lt;strong&gt;both DX and cloud-native patterns&lt;/strong&gt; — this is your chance to help shape it early, while it’s already stable enough to be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;MonkeysLegion started as an experiment: what if a PHP framework could feel as modern and cloud-aware as the stacks we see in Node, Go, and modern JVM land — without giving up PHP’s strengths?&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;1.0.8+&lt;/strong&gt;, it’s no longer just an experiment. It’s a &lt;strong&gt;practical, production-ready toolkit&lt;/strong&gt; for teams who want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit architecture&lt;/li&gt;
&lt;li&gt;Strong defaults&lt;/li&gt;
&lt;li&gt;Real-world performance&lt;/li&gt;
&lt;li&gt;And a framework that stays out of the way when it should&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that resonates with you, spin up the skeleton, break things, open issues, and send PRs. I’d love to see what you build with it — and how your feedback can push the 1.0.x line into something that serves the community even better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://join.slack.com/t/monkeyslegion/shared_invite/zt-36jut3kqo-WCwOabVrVrhHBln4xhMATA" rel="noopener noreferrer"&gt;Slack support&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>symfony</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
