Appearance
API Standards Overview
Section: 15 — API
Last Updated: 2026-06-21
Scope: Versioning, response envelope, validation, Swagger, gateway routing
Standards at a Glance
All Microtec ERP APIs follow a consistent contract enforced by shared Microtec packages, including Microtec.Web.Core, Microtec.Web.ApiVersioning, and Microtec.Web.Hosting.
| Standard | Value |
|---|---|
| Versioning | URL path versioning: /api/v1/, /api/v2/ |
| Response format | APIResponse<T> JSON envelope |
| Validation | FluentValidation (server-side) |
| Documentation | Swagger / OpenAPI 3.0 at /swagger |
| Auth | JWT Bearer (Authorization: Bearer) |
| Context | ERP Access Token (X-Access-Context) |
| Content type | application/json |
| Date format | ISO 8601 UTC |
See API Versioning for the canonical configuration, controller and minimal-endpoint patterns, Swagger documents, gateway setup, migration, and rollout checklist.
URL Convention
/api/{version}/{domain}/{resource}[/{id}][/{sub-resource}]Examples
GET /api/v1/accounting/invoices
GET /api/v1/accounting/invoices/{id}
POST /api/v1/accounting/invoices
PUT /api/v1/accounting/invoices/{id}
DELETE /api/v1/accounting/invoices/{id}
GET /api/v1/hr/employees
GET /api/v1/hr/employees/{id}/contracts
POST /api/v1/hr/employees/{id}/contracts
GET /api/v1/accounting/invoices?page=1&limit=20&search=INV-001Naming Conventions
| Action | HTTP Method | URL Pattern |
|---|---|---|
| List all | GET | /api/v1/{domain}/{resources} |
| Get by ID | GET | /api/v1/{domain}/{resources}/{id} |
| Create | POST | /api/v1/{domain}/{resources} |
| Update | PUT | /api/v1/{domain}/{resources}/{id} |
| Partial update | PATCH | /api/v1/{domain}/{resources}/{id} |
| Delete | DELETE | /api/v1/{domain}/{resources}/{id} |
| Dropdown / lookup | GET | /api/v1/{domain}/{resources}/dropdown |
| Export | GET | /api/v1/{domain}/{resources}/export |
Handler Naming Convention
Backend CQRS handlers follow a strict naming pattern:
| Operation | Command/Query | Handler Name |
|---|---|---|
| Create | Command | Add{Entity}Command |
| Update | Command | Edit{Entity}Command |
| Delete | Command | Delete{Entity}Command |
| Get by ID | Query | GetById{Entity}Query |
| Get all / list | Query | GetAll{Entity}Query |
| Get dropdown | Query | GetDropdown{Entity}Query |
| Export | Query | Export{Entity}Query |
Response Envelope
See request-response.md for full envelope specification with examples.
All responses are wrapped in APIResponse<T>:
json
{
"success": true,
"data": { },
"message": "Operation successful",
"messageCode": null,
"validationErrors": null,
"meta": null
}HTTP status codes are always meaningful:
200 OK— successful read201 Created— resource created204 No Content— successful delete400 Bad Request— validation error401 Unauthorized— missing/invalid token403 Forbidden— insufficient permissions404 Not Found— resource not found409 Conflict— business rule violation422 Unprocessable Entity— semantic validation failure500 Internal Server Error— unexpected server error
FluentValidation
All command/query input DTOs have a corresponding AbstractValidator<T>:
csharp
public class AddEmployeeCommandValidator : AbstractValidator<AddEmployeeCommand>
{
public AddEmployeeCommandValidator()
{
RuleFor(x => x.FirstName)
.NotEmpty().WithMessage("First name is required")
.MaximumLength(100);
RuleFor(x => x.NationalId)
.NotEmpty()
.Length(10).WithMessage("National ID must be 10 digits")
.Matches(@"^\d{10}$");
RuleFor(x => x.HireDate)
.NotEmpty()
.LessThanOrEqualTo(DateTime.Today)
.WithMessage("Hire date cannot be in the future");
}
}Validation errors are automatically mapped to APIResponse<T>.validationErrors by the MediatR validation pipeline behavior (in Microtec.Web.Core).
Controller Pattern
Every controller action must have:
csharp
using Asp.Versioning;
using Microtec.Web.ApiVersioning;
[ApiController]
[ApiVersion(ApiVersions.V1)]
[Route(MicrotecApiVersioningDefaults.VersionedControllerRoute)]
[Authorize]
public class InvoicesController : ControllerBase
{
private readonly IMediator _mediator;
[HttpGet]
[ProducesResponseType(typeof(APIResponse<PagedResult<InvoiceDto>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(APIResponse<object>), StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> GetAll([FromQuery] GetAllInvoiceQuery query)
=> Ok(await _mediator.Send(query));
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(APIResponse<InvoiceDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(APIResponse<object>), StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetById(Guid id)
=> Ok(await _mediator.Send(new GetByIdInvoiceQuery(id)));
[HttpPost]
[ProducesResponseType(typeof(APIResponse<Guid>), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(APIResponse<object>), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Create([FromBody] AddInvoiceCommand command)
{
var result = await _mediator.Send(command);
return CreatedAtAction(nameof(GetById), new { id = result.Data }, result);
}
}IMPORTANT
[ProducesResponseType] attributes are mandatory on all controller actions. They are required for accurate Swagger documentation and are enforced in code review.
Swagger / OpenAPI
Each API version exposes its own OpenAPI document, such as /swagger/v1/swagger.json and /swagger/v2/swagger.json. Swagger UI is served at /swagger when the root EnableSwagger setting is enabled for the environment.
Do not register Swagger documents or version groups manually in a service. Shared API versioning creates the documents from endpoint metadata. See Swagger / OpenAPI for UI usage and API Versioning for document grouping and route substitution.
Rate Limiting
All public endpoints are rate-limited via the API Gateway:
| Tier | Limit | Window |
|---|---|---|
| Standard (authenticated) | 1000 req | 1 minute |
| Export endpoints | 10 req | 1 minute |
| Auth endpoints | 20 req | 1 minute |
| Unauthenticated | 10 req | 1 minute |
Rate limit responses:
json
HTTP 429 Too Many Requests
Retry-After: 45
{
"success": false,
"data": null,
"message": "Rate limit exceeded. Retry after 45 seconds.",
"messageCode": 4290
}Localization
API responses support Arabic and English:
Accept-Language: ar ← Arabic error messages
Accept-Language: en ← English (default)Error messages in validationErrors are localized based on the Accept-Language header.