API Reference
The real surface of ApiController, req and res: what exists, with its signature. If a member is not here, it does not exist.
ApiController
Base class that every controller inherits from. It gives you the JSON utilities, the results with their status code, logging and the two lifecycle hooks. The JWT is NOT generated here: the middleware issues it when the login answers with X-Auth-Status.
Response helpers
| Property / Method | Type | Description |
Ok(obj) | 200 | The normal result. Serializes the object and answers 200. It is the preferred form, and the one that respects the server's responseformat. |
OkRaw(json) | 200 | The same, but with JSON you already have. It is the one that follows TableToJson(). |
Created(obj, location) | 201 | For what you have just created. location is optional and sets the Location header. |
NoContent() | 204 | It went well and there is nothing to return. Typical of a DELETE. |
BadRequest(msg, code) | 400 | What the client sent is not valid. code is yours and travels in the Problem Details. |
Unauthorized(msg) · Forbidden(msg) | 401 · 403 | Not identified, or identified and not allowed. |
NotFound(msg) | 404 | What was asked for does not exist. |
Fail(status, msg, code) | whatever you pass | Any other error, with the status code you give it. |
newObject(fields) | Object | Creates a VFP object with those properties set to NULL. fields is a comma-separated list. |
ToJson(obj) · ParseJson(txt) | String · Object | Serialize and deserialize, through JSONFox. The inverse of ToJson is called ParseJson: there is no FromJson. |
LogInfo · LogWarning · LogError · LogFromException | Void | They write to the server log. The last one dumps the whole exception, with its line. |
What an error returns
Since 0.9.15, error results are described with Problem Details (RFC 9457), the same format ASP.NET uses, and are served with Content-Type: application/problem+json; charset=utf-8.
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "No existe el artículo ZZZZ",
"instance": "/articulos/ZZZZ",
"code": "not_found"
}
detail is your message, the one you passed to this.NotFound(...); code is yours and travels as an extension member, which is what the RFC allows for what is not its own; title is the code's reason phrase and is not translated, because it is machine readable; instance is set by the engine. A 200 does NOT change: it comes out with no envelope. And if your server declares responseformat: foxserver, that format does not change either.
Lifecycle hooks
| Property / Method | Type | Description |
BeforeEndpoint(req, res) | Boolean | Runs before each endpoint OF THAT CONTROLLER. Returning .F. cuts the request off. This is where you open your tables. |
AfterEndpoint(req, res) | Void | Runs after each endpoint of that controller. This is where you close them. |
FoxServerRequest
Object that encapsulates all information about the incoming HTTP request. Available as the req parameter in all endpoints.
Properties
| Property / Method | Type | Description |
Method | String | GET, POST, PUT, PATCH, DELETE |
uri | Object | An OBJECT, not a string: Scheme, Host, Port, AbsolutePath, PathAndQuery and AbsoluteUri. The path without the query is in uri.AbsolutePath. |
Params | Object | Route parameters. req.Params.id from /{id}, or req.GetParam("id", "") with a default. |
query | Object | Query string. req.query.page from ?page=2, or req.GetQuery("page", 1). |
Headers | Object | The headers. Read with req.GetHeader("Authorization", "") and checked with req.HasHeader(). |
RawBody | String | The unparsed body. Also req.GetRawBody(). This is what to read when the body may not arrive, or when a date must not go through JSONFox. |
json | Object | The already parsed JSON body. req.HasJsonBody() says whether there is one, and req.HasJsonError() / req.JsonError if it could not be parsed. |
ContentType | String | The request's Content-Type. |
AuthenticatedUser | Object | With the token already validated: .id, .name and .role. |
Claims | Object | The token's extra claims. req.GetClaim(name, default), req.HasClaim(name) and req.ClaimNames(). |
MultipartData · FormFields | Object | Uploads and forms: req.file(field), req.files(field), req.HasFiles(), req.GetFormField(field, default). |
req.path, req.body, req.remoteIP and req.scheme do not exist And reading a property that does not exist returns empty without raising an error, which is the worst way to be wrong. The path is in req.uri.AbsolutePath, the body in req.RawBody and the method in req.Method. The client IP is NOT exposed to the controller.
Methods
ABCReading parameters, headers and the JSON body
* Un parametro de ruta, con valor por defecto
lcId = req.GetParam("id", "")
* Una cabecera: GetHeader, no req.headers[...]
IF req.HasHeader("X-Api-Key")
lcKey = req.GetHeader("X-Api-Key", "")
ENDIF
* El JSON del cuerpo, con cuidado: leer una propiedad que no vino
* da error 1734, asi que se comprueba antes o se envuelve en TRY.
IF req.HasJsonBody() AND !ISNULL(req.json)
TRY
lcCiudad = NVL(req.json.direccion.ciudad, "")
CATCH
lcCiudad = ""
ENDTRY
ENDIF
FoxServerResponse
Object to build and send the HTTP response. Available as the res parameter in all endpoints. All methods are chainable (return res).
| Property / Method | Description |
status(code) | Sets the HTTP status code (200, 201, 400, 401, 404, 500...) |
json(jsonStr) | Sends JSON body. Sets Content-Type: application/json automatically. |
send(text) | Sends plain text or HTML as body. |
header(key, value) | Adds or overwrites a response header. Chainable. |
location(url) | Sets the Location header. Use with status(301) or status(302) for redirect. |
sendFile(path, disp) | Sends a file. disp can be "inline" (display in browser) or "attachment" (force download). |
events(tipo) | Declares an SSE event type this endpoint supports. Without declaring it, nobody can subscribe to it. |
emit(tipo, json) | Emits an SSE event from any endpoint. Framing, retry and pings are handled by the middleware. |
Example
ABCResponding by chaining the res methods
* Standard success response
res.status(200).json(THIS.ToJson(loResult))
* Created with location header
res.status(201)
.header("Location", "/api/v1/users/" + lcNewId)
.json(THIS.ToJson(loResult))
* Error response
res.status(400).json('{"error":"Name is required"}')
* File download
res.status(200)
.header("Content-Disposition", 'attachment; filename="report.pdf"')
.sendFile("C:\reports\report.pdf", "attachment")
ApiController: detailed methods
Object and JSON management
| Property / Method | Description |
NewObject(fields) | Creates a VFP object with properties initialized to NULL. fields is a comma-separated list: "status,data,message". |
NewObjectFromCursor(cursor, session) | Creates a VFP object mirroring a cursor structure. Useful for creating templates based on table schemas. |
ToJson(obj) | Serializes a VFP object to a JSON string via JSONFox. |
ParseJson(jsonStr) | Deserializes a JSON string to a VFP object via JSONFox. |
TableToJson(cursor, currentRow) | Converts a cursor to JSON. currentRow=.T. returns the current record as an object; .F. returns all records as an array. |
TableToJsonObject(cursor, currentRow, session) | Like TableToJson but returns a VFP object collection instead of a JSON string. Useful for manipulating data before final serialization. |
MasterToJSON(master, detail, expr, attr) | Generates hierarchical master-detail JSON. expr is the join condition (e.g. "master.id = detail.master_id"), attr is the detail array name in the output JSON. |
ABCMasterToJSON: orders with their lines
* Ejemplo: maestro-detalle con MasterToJSON
SELECT id, fecha, cliente FROM pedidos INTO CURSOR curPedidos
SELECT id, pedido_id, producto, cantidad FROM lineas INTO CURSOR curLineas
lcJson = THIS.MasterToJSON("curPedidos", "curLineas", "curPedidos.id = curLineas.pedido_id", "lineas")
res.status(200).json(lcJson)
Text and formatting utilities
| Property / Method | Description |
Format(template, p0..p10) | Substitutes {0}, {1}, ... placeholders in the template string. Similar to C# String.Format. |
EscapeCharsToJSON(text) | Escapes special characters for safe use in JSON strings. |
EscapeCharsToHTML(text) | Escapes special characters for use in HTML output. |
Content(req, source) | Serializes source (cursor, object or string) to the format requested by the client via Accept header or ?format= (json, xml, csv). |
TableToXml(cursor, currentRow) | Converts a cursor to XML. |
TableToCsv(cursor, currentRow) | Converts a cursor to CSV. |
Date, encoding and identifier utilities
| Property / Method | Type | Description |
NewGuid() | String | Generates a GUID without braces or dashes. Use for all PKs. |
DTOUNX(dt, useMsec) | Number | Converts a VFP DateTime to a Unix timestamp. useMsec=.T. returns milliseconds. |
UNXTOD(timestamp) | DateTime | Converts a Unix timestamp (seconds or milliseconds) to a VFP DateTime. |
Base64URLEncode(input) | String | Encodes to Base64URL (used in JWTs and safe URLs). |
Base64URLDecode(input) | String | Decodes from Base64URL. |
ABCUnix dates, GUIDs and Format
* Conversiones de fecha
lnTs = THIS.DTOUNX(DATETIME(), .F.) && segundos
ldFecha = THIS.UNXTOD(1716825600)
* Identificadores
lcId = THIS.NewGuid() && "a1b2c3d4e5f647g8h9i0j1k2l3m4n5o6"
* Formato de mensajes
lcMsg = THIS.Format("Usuario {0} accedio a {1} desde {2}", lcUser, lcUrl, lcIp)
Lifecycle and resources
| Property / Method | Description |
Initialize() | Runs automatically when an instance is created. Configures cPath, loads JSONFox and ApiService. |
LoadJsonFox() | Loads JSONFox.app if not yet available. Called internally by Initialize(). |
GetGlobalResource(name) | Gets a shared global resource (connections, configs) through the application lifecycle. |
SetWorkingDirectory(path) | Sets cPath and calls Initialize(). Useful for testing outside the FoxServer cycle. |
CleanUp() / Release() / Dispose() | Releases resources at the end of the controller lifecycle. Invoked automatically by FoxServer. |
FoxServerRequest: detailed methods
| Property / Method | Description |
GetHeader(key, default) | Returns the value of the specified HTTP header. Returns default if not present. |
GetParam(key, default) | Returns a URL route parameter (e.g. {id}). Returns default if not present. |
GetQuery(key, default) | Returns a query string parameter (?key=value). Returns default if not present. |
GetQueryParams() | Returns all query parameters as a FoxServerTupleDictionary. |
HasJsonBody() | .T. if the request has a valid, parsed JSON body. Check before accessing req.json.*. |
GetRawBody() | Returns the raw unparsed request body. Required for webhook signature verification. |
GetFormField(key, default) | Returns the value of a multipart or urlencoded form field. |
HasFormField(key) | .T. if the form field exists in the request. |
File(field) | Returns the object for a file uploaded via multipart. Exposes FileName and SaveToFile(path). |
Files(field) | Returns a collection of files uploaded under the same field name. |
ABCHeaders, body, uploaded files and webhook signature
* Leer headers y params
lcToken = req.GetHeader("Authorization", "")
lcId = req.GetParam("id", "")
lcPage = req.GetQuery("page", "1")
* Verificar body antes de acceder
IF req.HasJsonBody()
lcEmail = req.json.email
ELSE
res.status(400).json('{"error":"JSON body required"}')
RETURN
ENDIF
* Subir archivo
loFile = req.File("avatar")
IF !ISNULL(loFile)
loFile.SaveToFile(THIS.cPath + "uploads\" + loFile.FileName)
ENDIF
* Webhook: raw body para HMAC
lcRaw = req.GetRawBody()
lcSig = req.GetHeader("X-Signature", "")
Collections
FoxServerTupleDictionary
Key-value dictionary used for Headers, Params and Query. Case-insensitive access.
| Property / Method | Description |
Count | Number of entries. |
GetKey(n) | Key at position n (1-based). |
GetValue(n) | Value at position n. |
Contains(key) | .T. if the key exists. |
GetValueByKey(key) | Value by key name. |
ABCIterating over all query parameters
* Iterar todos los query params
loParams = req.GetQueryParams()
FOR lnI = 1 TO loParams.Count
lcKey = loParams.GetKey(lnI)
lcVal = loParams.GetValue(lnI)
THIS.LogInfo(THIS.Format("Param {0}={1}", lcKey, lcVal))
ENDFOR
FoxServerTuple
Simple key-value pair. Internal component of FoxServerTupleDictionary.
| Property / Method | Description |
Key | Key name. |
Value | Associated value. |
Next: FAQ →