Skip to content

API Gateway Routes

Section: 15 — API
Last Updated: 2026-06-01
Scope: YARP route table, cluster configuration, CAE type, auth


Overview

Microtec ERP uses YARP (Yet Another Reverse Proxy) within the Gateway.Yarp project (Platforms/Src/Gateway/Gateway.Yarp/) as the API gateway. The gateway is the only internet-facing entry point for all API traffic.


Route Table

Routes are defined in appsettings.json under the ReverseProxy.Routes section. The gateway matches requests by path prefix and forwards to the corresponding cluster.

Route IDPath PatternClusterDownstream ServiceLocal Dev Port
erp-apis/erp-apis/{**catch-all}erp-clusterAppsPortal.Apis:2005
inventory-apis/inventory-apis/{**catch-all}inventory-clusterInventory.Apis:2008
business-owners-apis/business-owners-apis/{**catch-all}business-owners-clusterBusinessOwners.Apis:2003
admin-portal-apis/admin-portal-apis/{**catch-all}admin-portal-clusterBusinessOwners.AdminPortal:2007
attachments-apis/attachments-apis/api/{**catch-all}attachments-clusterAttachment.Apis:2030
notifications-apis/notifications-apis/{**catch-all}notifications-clusterNotification.Apis:2031
integration-apis/integration-apis/{**catch-all}integration-clusterIntegration.Apis:2033
workflow-apis/workflow-apis/{**catch-all}workflow-clusterWorkflows.Apis:2035
hr-apis/hr-apis/{**catch-all}hr-clusterHr.Personnel.Apis:2046
template-apis/template-apis/{**catch-all}template-clusterTemplate.Blazor:2036

No Separate Finance / Sales / Purchase Routes

Finance, Sales, and Purchase modules are all served by AppsPortal.Apis (erp-cluster). There are no separate gateway clusters or route entries for those modules.

Special Routes

In addition to the main routes above, several specific path prefixes have dedicated route entries that also point to the same clusters:

Route IDPath PatternCluster
business-owners-sidemenu/business-owners-apis/SideMenu/{**catch-all}business-owners-cluster
business-owners-translation/business-owners-apis/Translation/{**catch-all}business-owners-cluster
erp-hrgeneralsetting/erp-apis/HrGeneralSetting/{**catch-all}erp-cluster
erp-sidemenu/erp-apis/SideMenu/{**catch-all}erp-cluster
erp-translation/erp-apis/Translation/{**catch-all}erp-cluster
erp-swagger/erp-apis/swagger/{**catch-all}erp-cluster
inventory-sidemenu/inventory-apis/SideMenu/{**catch-all}inventory-cluster
inventory-translation/inventory-apis/Translation/{**catch-all}inventory-cluster
inventory-swagger/inventory-apis/swagger/{**catch-all}inventory-cluster
attachments-swagger/attachments-apis/swagger/{**catch-all}attachments-cluster
notifications-swagger/notifications-apis/swagger/{**catch-all}notifications-cluster
integration-swagger/integration-apis/swagger/{**catch-all}integration-cluster

YARP Configuration

The gateway is configured via appsettings.json using YARP's declarative configuration:

json
{
  "ReverseProxy": {
    "Routes": {
      "erp-apis": {
        "ClusterId": "erp-cluster",
        "Match": {
          "Path": "/erp-apis/{**catch-all}"
        }
      },
      "inventory-apis": {
        "ClusterId": "inventory-cluster",
        "Match": {
          "Path": "/inventory-apis/{**catch-all}"
        }
      },
      "business-owners-apis": {
        "ClusterId": "business-owners-cluster",
        "Match": {
          "Path": "/business-owners-apis/{**catch-all}"
        }
      },
      "admin-portal-apis": {
        "ClusterId": "admin-portal-cluster",
        "Match": {
          "Path": "/admin-portal-apis/{**catch-all}"
        }
      },
      "attachments-apis": {
        "ClusterId": "attachments-cluster",
        "Match": {
          "Path": "/attachments-apis/api/{**catch-all}"
        }
      },
      "notifications-apis": {
        "ClusterId": "notifications-cluster",
        "Match": {
          "Path": "/notifications-apis/{**catch-all}"
        }
      },
      "integration-apis": {
        "ClusterId": "integration-cluster",
        "Match": {
          "Path": "/integration-apis/{**catch-all}"
        }
      },
      "workflow-apis": {
        "ClusterId": "workflow-cluster",
        "Match": {
          "Path": "/workflow-apis/{**catch-all}"
        }
      },
      "hr-apis": {
        "ClusterId": "hr-cluster",
        "Match": {
          "Path": "/hr-apis/{**catch-all}"
        }
      },
      "template-apis": {
        "ClusterId": "template-cluster",
        "Match": {
          "Path": "/template-apis/{**catch-all}"
        }
      }
    },
    "Clusters": {
      "erp-cluster": {
        "Destinations": {
          "erp-cluster/destination1": {
            "Address": "https://mic-erp-be-appsportal.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "inventory-cluster": {
        "Destinations": {
          "inventory-cluster/destination1": {
            "Address": "https://mic-erp-be-inventory.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "business-owners-cluster": {
        "Destinations": {
          "business-owners-cluster/destination1": {
            "Address": "https://mic-erp-be-businessowners.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "admin-portal-cluster": {
        "Destinations": {
          "admin-portal-cluster/destination1": {
            "Address": "https://mic-erp-be-adminportal.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "hr-cluster": {
        "Destinations": {
          "hr-cluster/destination1": {
            "Address": "https://mic-erp-be-hr.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "integration-cluster": {
        "Destinations": {
          "integration-cluster/destination1": {
            "Address": "https://mic-erp-be-integration.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "attachments-cluster": {
        "Destinations": {
          "attachments-cluster/destination1": {
            "Address": "https://mic-erp-be-attachments.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "notifications-cluster": {
        "Destinations": {
          "notifications-cluster/destination1": {
            "Address": "https://mic-erp-be-notifications.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "workflow-cluster": {
        "Destinations": {
          "workflow-cluster/destination1": {
            "Address": "https://mic-erp-be-workflows.internal.<env>.azurecontainerapps.io/"
          }
        }
      },
      "template-cluster": {
        "Destinations": {
          "template-cluster/destination1": {
            "Address": "https://mic-erp-be-template.internal.<env>.azurecontainerapps.io/"
          }
        }
      }
    }
  }
}

Replace <env> with the Container App environment suffix (e.g., dev, stage, prod).


Authentication Pipeline

The gateway validates the JWT signature using Keycloak's public key (fetched from the OIDC discovery endpoint at startup). It does not decode or interpret claims — claim-based authorization is enforced by each downstream service individually.

json
// appsettings.json (Gateway)
{
  "Authentication": {
    "Authority": "https://<keycloak-host>/realms/microtec",
    "Audience": "account",
    "ValidateAudience": true,
    "ValidateIssuer": true
  }
}

Downstream services receive the original Authorization: Bearer <token> header unchanged.


Health Checks

The gateway exposes health endpoints:

EndpointAuthPurpose
GET /healthNoLiveness / readiness probe
json
// GET /health
{
  "status": "Healthy",
  "entries": {
    "erp-cluster":              { "status": "Healthy" },
    "inventory-cluster":        { "status": "Healthy" },
    "business-owners-cluster":  { "status": "Healthy" },
    "admin-portal-cluster":     { "status": "Healthy" },
    "hr-cluster":               { "status": "Healthy" },
    "integration-cluster":      { "status": "Healthy" },
    "attachments-cluster":      { "status": "Healthy" },
    "notifications-cluster":    { "status": "Healthy" },
    "workflow-cluster":         { "status": "Healthy" },
    "template-cluster":         { "status": "Healthy" }
  }
}

Rate Limiting

Rate limiting is enforced upstream by Azure Front Door WAF (default: 10,000 requests per 5 minutes per client IP). The Gateway itself does not apply an additional rate limiting layer.


Ocelot (Legacy Reference)

Earlier versions of the gateway used Ocelot. If you see Ocelot-style configuration files (with UpstreamPathTemplate / DownstreamPathTemplate), these are from the legacy gateway and should not be used for new service configurations.

Migration to YARP was completed; the current source is at Platforms/Src/Gateway/Gateway.Yarp/.


Troubleshooting

502 Bad Gateway from gateway

bash
# Check downstream service health via ACA internal name
curl -k https://mic-erp-be-hr.internal.dev.azurecontainerapps.io/health

# Check gateway logs
az containerapp logs show --name mic-erp-be-dev-gateway --resource-group mic-erp-be-dev-compute-rg

Route not matched (404 at gateway)

bash
# Verify the route is in YARP config
grep -r "hr-apis\|erp-apis" Gateway.Yarp/appsettings*.json

# Test path matching
curl -v https://gateway.microtec-test.com/erp-apis/Invoices \
     -H "Authorization: Bearer <token>"

Internal Documentation — Microtec Platform Team