IR
irwinrodriguez.dev
Volver a documentacion

Sistema de rutas

FoxServer utiliza comentarios HELP en los metodos VFP para declarar rutas. No hay archivos de configuracion de rutas separados: la declaracion vive junto al codigo del controlador.

Sintaxis del comentario HELP

Cada metodo endpoint debe tener un comentario HELP con este formato:

PROCEDURE NombreMetodo(req, res) HELP "METODO: ruta/path [public]"
ParteDescripcionEjemplo
Metodo HTTPGET, POST, PUT, PATCH, DELETE, HEADGET
RutaPath relativo al prefijo del servidor. Puede contener {parametros}.products/{id}
Visibilidadpublic = sin JWT requerido. Sin este token = JWT obligatorio.public

Metodos HTTP soportados

MetodoDescripcionTiene body?Ejemplo de ruta
GETRecuperar datosNoGET: products public
POSTCrear recursoSiPOST: products public
PUTActualizar (completo)SiPUT: products/{id} public
PATCHActualizar (parcial)SiPATCH: products/{id} public
DELETEEliminar recursoNoDELETE: products/{id} public
HEADSolo encabezadosNoHEAD: products public

Parametros de URL

* Single parameter
PROCEDURE GetProduct(req, res) HELP "GET: products/{id} public"
    LOCAL lcId
    lcId = req.params.id          && /api/products/42 -> "42"
    res.status(200).json(...)
ENDPROC

* Multiple parameters
PROCEDURE GetVariant(req, res) HELP "GET: products/{id}/variants/{vid} public"
    LOCAL lcProductId, lcVariantId
    lcProductId = req.params.id   && /api/products/42/variants/black -> "42"
    lcVariantId = req.params.vid  && "black"
    res.status(200).json(...)
ENDPROC

Query string

Los parametros de query string (?clave=valor) se acceden via req.query:

PROCEDURE SearchProducts(req, res) HELP "GET: products/search public"
    * GET /api/products/search?name=Widget&minprice=10&page=2
    LOCAL lcName, lnMin, lnPage
    lcName  = req.query.name               && "Widget"
    lnMin   = VAL(req.query.minprice)      && 10
    lnPage  = IIF(EMPTY(req.query.page), 1, VAL(req.query.page))
    res.status(200).json(...)
ENDPROC

Objeto Request — propiedades

PropiedadTipoDescripcion
methodStringMetodo HTTP (GET, POST, etc.)
urlUriURL completa de la peticion
pathStringRuta sin query string
paramsObjectParametros de URL ({id} → params.id)
queryObjectParametros de query string (?key=val)
headersDictCabeceras HTTP (case-insensitive)
bodyStringBody de la peticion en texto plano
jsonObjectBody JSON parseado (si Content-Type: application/json)
contentTypeStringValor del header Content-Type
remoteIPStringDireccion IP del cliente

Objeto Response — metodos

MetodoFirmaDescripcion
status(code)res.status(200)Establece el codigo HTTP. Retorna res para encadenamiento.
json(jsonStr)res.json('{"ok":true}')Envia body JSON. Establece Content-Type: application/json.
send(text)res.send("Hello")Envia texto plano o HTML.
header(key, val)res.header("X-Id","123")Agrega o sobreescribe un header de respuesta.
location(url)res.location("/api/v2")Establece el header Location (para redirects).
sendFile(path, disp)res.sendFile("C:\\rep.pdf","attachment")Envia un archivo. disp: "inline" o "attachment".
* Chaining example — multiple headers + JSON response
res.status(201)
   .header("X-Resource-Id", lcNewId)
   .header("Cache-Control", "no-cache")
   .json(THIS.ToJson(loResponse))

Ciclo de vida del controlador

Cada controlador puede definir hooks que se ejecutan antes y despues de cada endpoint:

DEFINE CLASS ProductsController AS ApiController OLEPUBLIC

    FUNCTION BeforeEndpoint(req, res) AS BOOLEAN
        SET DELETED ON
        SET EXCLUSIVE OFF
        USE data\products SHARED
        RETURN .T.   && .F. here would abort the request
    ENDFUNC

    PROCEDURE AfterEndpoint(req, res)
        CLOSE DATABASES ALL
    ENDPROC

    PROCEDURE GetProducts(req, res) HELP "GET: products public"
        && Products table is already open (BeforeEndpoint)
        && It will be closed automatically (AfterEndpoint)
        res.status(200).json(...)
    ENDPROC

ENDDEFINE

Rutas publicas vs. protegidas

La visibilidad se controla con la palabra clave public en el comentario HELP:

* PUBLIC endpoint — no JWT needed
PROCEDURE GetProducts(req, res) HELP "GET: products public"
    res.status(200).json(...)
ENDPROC

* PRIVATE endpoint — JWT required (omit "public")
PROCEDURE DeleteProduct(req, res) HELP "DELETE: products/{id}"
    && Client must send: Authorization: Bearer eyJhb...
    res.status(200).json(...)
ENDPROC
Cuando el JWT middleware esta activo, los endpoints sin public requieren que el cliente envie el token en el header Authorization: Bearer {token}.

Siguiente: Middleware →