<?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: Dean Walters</title>
    <description>The latest articles on DEV Community by Dean Walters (@treelz).</description>
    <link>https://dev.to/treelz</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%2F3623219%2F43457d5f-37cc-4841-a645-a9f66b2bbf8c.jpg</url>
      <title>DEV Community: Dean Walters</title>
      <link>https://dev.to/treelz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/treelz"/>
    <language>en</language>
    <item>
      <title>Building a Unified API Response Architecture (ASP.NET Minimal API + Next.js)</title>
      <dc:creator>Dean Walters</dc:creator>
      <pubDate>Sat, 06 Dec 2025 10:37:27 +0000</pubDate>
      <link>https://dev.to/treelz/building-a-unified-api-response-architecture-aspnet-minimal-api-nextjs-38l7</link>
      <guid>https://dev.to/treelz/building-a-unified-api-response-architecture-aspnet-minimal-api-nextjs-38l7</guid>
      <description>&lt;p&gt;&lt;em&gt;How to eliminate inconsistent errors, simplify validation, and keep backend &amp;amp; frontend perfectly in sync&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If your frontend error-handling is overloaded with conditional logic…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (error.response?.data?.message) { ... }
else if (typeof error === "string") { ... }
else if (error.status === 401) { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…it’s not your fault - it’s your backend being inconsistent.&lt;br&gt;
This article shows how to design a fully unified response architecture using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ASP.NET Minimal API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next.js&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a Response Envelope&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a Rich Error Object pattern&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fully synced C# -&amp;gt; TypeScript enums&lt;br&gt;
The result is predictable, type-safe, frontend-friendly APIs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;The Problem: Inconsistent API Responses&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Most projects begin with “quick” error returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return BadRequest("Email invalid");
throw new Exception("Something went wrong");
return Unauthorized();`
But later you realize your API responds with:
`{ "error": "UserNotFound" }
{ "message": "Invalid email" }
Something went wrong
&amp;lt;html&amp;gt;401 Unauthorized&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;1. Unpredictable API response structures&lt;/strong&gt;&lt;br&gt;
Different shapes break interceptors and fetch wrappers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Frontend cannot centralize error handling&lt;/strong&gt;&lt;br&gt;
You end up parsing strings, objects, arrays, or HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Validation errors are irregular&lt;/strong&gt;&lt;br&gt;
No unified shape, varying formats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. No type syncing&lt;/strong&gt;&lt;br&gt;
A typo in backend enums silently breaks the frontend.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Solution: The Unified Response Envelope&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;single response format&lt;/strong&gt; used by all endpoints — successful or not.&lt;br&gt;
It should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;indicate success/failure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;include a strict error code (enum)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;standardize validation errors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;never leak stack traces&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;be predictable for any client&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fully map to TypeScript&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Defining the ApiResponse Contract
&lt;/h2&gt;

&lt;p&gt;Before diving into middleware, we need a consistent backend response type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ApiResponse&amp;lt;T&amp;gt;
{
    public bool Success { get; set; }

    [JsonConverter(typeof(JsonStringEnumConverter))]
    public ErrorCodes? ErrorCode { get; set; }

    public string? Message { get; set; }

    public T? Data { get; set; }

    public List&amp;lt;ValidationError&amp;gt;? ValidationErrors { get; set; }

    public static ApiResponse&amp;lt;T&amp;gt; Ok(T data, string? message = null) =&amp;gt;
        new() { Success = true, Data = data, Message = message };

    public static ApiResponse&amp;lt;T&amp;gt; Fail(ErrorCodes code, string? message = null) =&amp;gt;
        new() { Success = false, ErrorCode = code, Message = message };

    public static ApiResponse&amp;lt;T&amp;gt; ValidationError(List&amp;lt;ValidationError&amp;gt; errors) =&amp;gt;
        new()
        {
            Success = false,
            ErrorCode = ErrorCodes.ValidationError,
            Message = "Validation failed",
            ValidationErrors = errors
        };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class ensures that &lt;strong&gt;all API responses follow the same structure.&lt;/strong&gt;&lt;br&gt;
Success always returns Data, errors always return ErrorCode and Message.&lt;br&gt;
&lt;strong&gt;Why it matters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One response shape -&amp;gt; simple front-end logic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strong typing between backend &amp;amp; frontend&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No accidental HTML or string leakage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UI becomes fully predictable&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Backend Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Global Exception Middleware&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ErrorMiddleware
{
    private readonly RequestDelegate _next;

    public ErrorMiddleware(RequestDelegate next) =&amp;gt; _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch
        {
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = 500;

            await context.Response.WriteAsJsonAsync(
                ApiResponse&amp;lt;object&amp;gt;.Fail(
                    ErrorCodes.ServerError,
                    "Internal server error")
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This middleware wraps the entire pipeline in a &lt;code&gt;try/catch&lt;/code&gt;.&lt;br&gt;
Any unhandled exception becomes a structured 500 JSON response.&lt;br&gt;
&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No stack traces or raw exceptions escape&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend never returns unhandled errors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend receives the same error shape every time&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. 401 &amp;amp; 403 Override&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ApiAuthorizationHandler : IAuthorizationMiddlewareResultHandler
{
    private readonly AuthorizationMiddlewareResultHandler _default = new();

    public async Task HandleAsync(
        RequestDelegate next,
        HttpContext context,
        AuthorizationPolicy policy,
        PolicyAuthorizationResult result)
    {
        if (result.Challenged)
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsJsonAsync(
                ApiResponse&amp;lt;object&amp;gt;.Fail(ErrorCodes.Unauthorized, "Authentication required")
            );
            return;
        }

        if (result.Forbidden)
        {
            context.Response.StatusCode = 403;
            await context.Response.WriteAsJsonAsync(
                ApiResponse&amp;lt;object&amp;gt;.Fail(ErrorCodes.Forbidden, "Access denied")
            );
            return;
        }

        await _default.HandleAsync(next, context, policy, result);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This handler replaces raw 401/403 status codes with clean JSON envelopes.&lt;br&gt;
&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No more raw status codes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mobile and SPA clients get clean JSON&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auth errors match your global error format&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. API-Scoped 404 Handler&lt;/strong&gt;&lt;br&gt;
ASP.NET returns default HTML for unknown endpoints.&lt;br&gt;
We override it only for &lt;code&gt;/api/**&lt;/code&gt; paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.Use(async (context, next) =&amp;gt;
{
    await next();

    if (context.Request.Path.StartsWithSegments("/api") &amp;amp;&amp;amp;
        context.Response.StatusCode == 404 &amp;amp;&amp;amp;
        !context.Response.HasStarted)
    {
        await context.Response.WriteAsJsonAsync(
            ApiResponse&amp;lt;object&amp;gt;.Fail(ErrorCodes.NotFound, "API endpoint not found")
        );
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No HTML 404 pages for API routes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every API failure is a JSON envelope&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why This Architecture Works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Predictable&lt;/strong&gt;&lt;br&gt;
Every response follows one structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Strongly typed&lt;/strong&gt;&lt;br&gt;
Backend enums → frontend TS types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Easier debugging&lt;/strong&gt;&lt;br&gt;
You always know where things went wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Cleaner UI code&lt;/strong&gt;&lt;br&gt;
One global error handler instead of dozens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- More secure&lt;/strong&gt;&lt;br&gt;
No stack traces or framework-generated HTML.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Frontend Architecture (Next.js)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;apiFetch&lt;/code&gt;: Unified Fetch Wrapper&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function apiFetch&amp;lt;T&amp;gt;(
  url: string,
  options: RequestInit = {}
): Promise&amp;lt;ApiResponse&amp;lt;T&amp;gt;&amp;gt; {
  const res = await fetch(API_URL + url, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      ...(options.headers || {}),
    },
    credentials: "include",
  });

  let json: ApiResponse&amp;lt;T&amp;gt;;

  try {
    json = await res.json();
  } catch {
    return {
      success: false,
      errorCode: "ServerError",
      message: "Invalid JSON from server",
    };
  }

  return json;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wrapper ensures that every request returns an &lt;code&gt;ApiResponse&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;br&gt;
If the server returns invalid JSON, we still wrap the error safely.&lt;br&gt;
&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No front-end crashes from malformed responses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Always returns a predictable structure&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Rich Error Object Pattern (&lt;code&gt;ApiError&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class ApiError extends Error {
  public error: ErrorResponse;

  constructor(response: ApiResponse&amp;lt;any&amp;gt;) {
    super("API Error");
    Object.setPrototypeOf(this, ApiError.prototype);
    this.name = "ApiError";

    this.error = {
      errorCode: response.errorCode!,
      message: response.message,
      validationErrors: response.validationErrors,
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Throwing plain strings or raw fetch errors is unhelpful.&lt;br&gt;
Instead, we throw a &lt;strong&gt;structured, typed error&lt;/strong&gt; object that UIs can use safely.&lt;br&gt;
&lt;strong&gt;Takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;UI receives rich error metadata&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works perfectly with Next.js server/client components&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Makes error boundaries more useful&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Using apiFetch in a Service Module&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const userService = {
  async getUser(id: number) {
    const res = await apiFetch&amp;lt;{ id: number; name: string }&amp;gt;(
      `/api/users/${id}`
    );

    if (!res.success) {
      throw new ApiError(res);
    }

    return res.data;
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this is clean&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All API logic is centralized - components receive only clean data or &lt;code&gt;ApiError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Syncing TypeScript With C# Enums&lt;/strong&gt;&lt;br&gt;
To eliminate typos and keep error codes synchronized between backend &amp;amp; frontend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.MapGet("/ts/error-codes.ts", () =&amp;gt;
{
    var names = Enum.GetNames(typeof(ErrorCodes));
    var lines = names.Select(n =&amp;gt; $"  | \"{n}\"").ToList();
    lines[0] = lines[0].Replace("|", "");

    var ts = "export type ErrorCode =\n" + string.Join("\n", lines) + ";\n";

    return Results.Text(ts, "text/plain");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
You generate the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://localhost:5000/ts/error-codes.ts &amp;gt; lib/errorCodes.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the frontend has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type ErrorCode =
  | "Unauthorized"
  | "Forbidden"
  | "NotFound"
  | "ValidationError"
  | "Conflict"
  | "ServerError";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Frontend Error Map&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const errorMap: Record&amp;lt;ErrorCode, string&amp;gt; = {
  Unauthorized: "Need Authentication",
  Forbidden: "Access Denied",
  NotFound: "Not Found",
  ValidationError: "Validation Error",
  Conflict: "Conflict Detected",
  ServerError: "Server Error",
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt;&lt;br&gt;
TypeScript forces developers to map every error code — or fail the build.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Developer Experience: Before vs After&lt;/strong&gt;
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;inconsistent API formats&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HTML error pages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;unpredictable validation formats&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;lots of repeated try/catch logic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;difficult UI error states&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;one universal response structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;strict typing across stack&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;easy global error handler&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;predictable UX&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;safer exceptions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;more reliable monitoring/logging&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When NOT to Use an Envelope&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This pattern is not ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Public REST APIs (Stripe style)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OpenAPI-first API-first schemas&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strictly RESTful HATEOAS-driven systems&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SPAs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSR apps&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;mobile apps&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;internal tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;B2B dashboards&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;admin panels&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…it’s a massive improvement.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Examples&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;All example code: &lt;a href="https://github.com/Akubet/ResponseExample" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;br&gt;
You can clone that and start developing with this boilerplate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Used in my recent production project: &lt;a href="https://neon-royale.com" rel="noopener noreferrer"&gt;Neon Royale&lt;/a&gt;&lt;br&gt;
And integrating the API with the frontend was a smooth and straightforward process thanks to this architecture.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>fullstack</category>
      <category>api</category>
    </item>
  </channel>
</rss>
