Hice una encuesta a la comunidad FoxPro, en Patreon y en el grupo de WhatsApp, con una sola pregunta: qué es lo más difícil de tu aplicación VFP hoy. Dos de cada tres respondieron lo mismo: exponerla en la web o como API. Sacar los datos y la lógica que ya tienen a algo que un navegador, una app móvil o un programa de otra empresa puedan llamar.

Este tutorial es la respuesta a esa pregunta, hecha desde el IDE de Visual FoxPro, el de toda la vida. Al terminar tendrás una API REST que devuelve una tabla DBF en JSON y que acepta altas. El código es VFP 9 normal y corriente, y no tocas la aplicación que ya tienes: la publicas.

Parto de cero: Visual FoxPro cerrado. Cada paso dice qué pulsar, qué escribir y qué tienes que ver en pantalla. Si en algún momento lo que ves no se parece a la captura, para ahí: justo debajo te explico por qué pasa y cómo se arregla.

¿Te manejas mejor con la línea de comandos? Tienes el mismo tutorial en versión CLI: mismo proyecto, mismo resultado, con comandos que se pegan en PowerShell.

Lo que necesitas antes de empezar

  • Visual FoxPro 9 SP2 instalado. FoxServer compila tu proyecto con él.
  • FoxServer instalado. Con la licencia de prueba de 30 días que se emite al registrarse en el portal basta para todo lo que hay aquí. El instalador te pidió el fichero de licencia al final: si lo saltaste, vuelve a ejecutarlo.
  • Un navegador, el que uses: Edge, Chrome o Firefox.
  • Permiso para ejecutar programas como administrador en tu equipo. Solo hace falta para compilar, y en el paso 1 te cuento por qué.

No hace falta abrir ninguna consola ni instalar nada más.

Las tres cosas que hay que entender antes de escribir una línea

Si solo lees un recuadro de este artículo, que sea este.

1. Tu código sigue siendo tuyo. Solo se publica. FoxServer no traduce nada ni te impone un framework. Una PROCEDURE de una clase VFP se convierte en un endpoint (una dirección web que responde, como http://127.0.0.1:8099/hola) solo con añadirle una cláusula HELP:

Un endpoint mínimo
PROCEDURE pHola(req, res) HELP "GET: hola public"
    LOCAL loRes
    loRes         = THIS.newObject("mensaje")
    loRes.mensaje = "hola"
    RETURN THIS.Ok(loRes)
ENDPROC

Eso es una API. El resto del artículo es detalle.

2. La ruta no existe hasta que compilas. La ruta nace en esa cláusula HELP, y solo llega al servidor cuando se compila el proyecto. Es la causa número uno de «he añadido el endpoint y me da 404». Si añades una ruta y no aparece: falta compilar.

3. La llamada es síncrona. Un endpoint que tarda dos segundos, tarda dos segundos, y mientras tanto ocupa su sitio. Para consultas es cómodo; para un proceso de cuarenta segundos lo que se devuelve es un identificador y el trabajo se hace por otro lado.

Paso 1. Abrir Visual FoxPro como administrador y cargar FoxForge

FoxForge es el taller que FoxServer instala para el IDE de Visual FoxPro: un menú más en la barra, junto a File, Edit y compañía, desde el que se crean los proyectos. Se carga como cualquier .app, con un DO.

  1. Busca el icono de Visual FoxPro 9 que usas siempre: en el escritorio, en la barra de tareas o en el menú Inicio.
  2. Haz clic derecho sobre él y elige Ejecutar como administrador. Dónde está la opción depende de dónde tengas el icono:
    • En el escritorio, sale directamente en el menú del clic derecho.
    • En la barra de tareas, el primer clic derecho solo enseña vfp9.exe: haz otro clic derecho sobre vfp9.exe y ahí está, como en la imagen.
    • En el menú Inicio de Windows 11, está dentro de Más.

Menú del clic derecho sobre vfp9.exe en la barra de tareas, con Ejecutar como administrador resaltado

Según cómo tengas configurado Windows, puede que antes de abrirse te pregunte si quieres permitir que la aplicación haga cambios en el equipo. Es el Control de cuentas de usuario: si sale, responde Sí. Si no sale nada y el IDE se abre directamente, también está bien: tu Windows está configurado para no preguntar.

¿Por qué como administrador? Tu proyecto se compila como una DLL COM multihilo, la misma que harías con BUILD MTDLL. Al terminar de compilarla, Visual FoxPro la registra en Windows, y eso solo lo deja hacer a un administrador. Sin permisos, la compilación parece que termina pero deja una DLL a medias que el servidor no puede usar. Nada de lo demás necesita administrador: solo compilar.

  1. Con Visual FoxPro abierto, ve a la ventana de comandos. Si no la tienes a la vista: menú Window › Command Window, o Ctrl+F2.
  2. Escribe estas dos líneas, pulsando Intro después de cada una:
Cargar FoxForge
CD "C:\Program Files (x86)\FoxServer\ide"
DO FoxForge.app

Las comillas del CD hacen falta porque la ruta lleva espacios. Si instalaste FoxServer en otra carpeta, cambia la ruta por la tuya: es la carpeta ide\ de la instalación.

En un segundo aparece un menú nuevo al final de la barra: FoxForge.

Visual FoxPro 9 con las líneas CD y DO FoxForge.app en la ventana de comandos y el menú FoxForge en la barra

Si prefieres no escribir nada, el instalador de FoxServer deja en el menú Inicio, dentro de la carpeta FoxServer, un acceso Crear un proyecto (FoxForge) que abre Visual FoxPro con el menú ya puesto. Ábrelo también con clic derecho › Ejecutar como administrador.

Desde la 0.9.30, abrir un proyecto FoxServer carga FoxForge solo, si FoxServer está instalado en la máquina y el proyecto se creó o se actualizó con esa versión. Para tenerlo antes de abrir ningún proyecto, usa esas dos líneas; si lo quieres siempre, ponlas en tu programa de arranque de Visual FoxPro. Funciona igual en Visual FoxPro 9 y en VFP Advanced (VFPA).

Paso 2. FoxForge en español

FoxForge habla español e inglés, y la primera vez elige según tu Windows. Si los menús te salen en inglés:

Abre el menú FoxForge › Language › Spanish. FoxForge se acuerda de tu elección para las próximas veces.

Menú FoxForge › Language abierto, con Spanish señalado

Desde aquí te nombro los menús de FoxForge en español. Los de Visual FoxPro los dejo en inglés, que es como salen.

Paso 3. La carpeta de tus proyectos

Vamos a guardar el proyecto en C:\proyectos. El asistente del paso siguiente necesita que la carpeta ya exista, así que la creamos ahora.

Abre la ventana de comandos de Visual FoxPro, si no la tienes a la vista: menú Window › Command Window, o Ctrl+F2. Escribe esta línea y pulsa Intro:

Crear la carpeta de proyectos
MD C:\proyectos

Si te contesta que la carpeta ya existe, no pasa nada: sigue adelante.

¿Otra carpeta? El proyecto puede ir donde tú quieras, también en tus Documentos. Aquí usamos C:\proyectos porque es corta y así las rutas de los pasos siguientes se leen mejor.

Paso 4. Crear el proyecto

  1. Abre el menú FoxForge › Nuevo proyecto › Servicio web / API REST (FoxServer).

Menú FoxForge › Nuevo proyecto › Servicio web / API REST (FoxServer)

  1. Se abre la ventana Nuevo proyecto FoxServer. Rellénala así:
CampoQué escribir
Cómo se llamaAlmacen. Es el nombre de la DLL, así que no admite tildes, espacios ni símbolos: si escribes «Almacén», el asistente lo deja en Almacen y te avisa
Puerto8099
Dónde lo creoC:\proyectos\
Qué va a serDeja marcada la primera, API REST — lo normal
Pedir un token para usar la API (JWT)Déjala sin marcar

El puerto es el «número de ventanilla» por el que tu API atenderá: 8099 es uno que casi nunca está ocupado.

Al rellenarlo, el recuadro de abajo te dice lo que va a pasar. Tiene que decir esto:

Se creará en : C:\proyectos\Almacen\
Responderá en: http://127.0.0.1:8099/

Asistente Nuevo proyecto FoxServer relleno con Almacen, puerto 8099 y la carpeta C:\proyectos

127.0.0.1 es tu propio equipo (localhost): mientras desarrollas, la API solo atiende desde tu máquina.

Si el botón Crear sigue gris, lee el recuadro: te dice qué falta. «Esa carpeta no existe» quiere decir que te saltaste el paso 3.

  1. Pulsa Crear.

Visual FoxPro trabaja unos segundos y te abre el Project Manager del proyecto Almacen, como cualquier otro proyecto tuyo.

Project Manager de Almacen con la pestaña Code y los programas del proyecto

Esto es lo que ha dejado en C:\proyectos\Almacen\. Es la salida de tree /f, un comando de Windows que dibuja una carpeta con todo lo que tiene dentro; puedes verlo tú mismo desde una ventana de Símbolo del sistema:

Salida de tree /f en C:\proyectos\Almacen con las carpetas build, logs y src del proyecto recién creado

Carpeta o ficheroQué es
Almacen.pjxTu proyecto, el de siempre
build\Aquí saldrá la DLL, y aquí vive Almacen.json, la configuración del servidor: puerto, seguridad, registros
logs\Los registros del servidor, uno por día. Vacía hasta que arranques
src\controllers\Aquí van tus clases con endpoints
src\core\ y src\lib\El motor de FoxServer y la librería JSON. No los toques
src\hooks\La clase que se ejecuta al compilar y apunta tus rutas
src\utils\PreBuild.prg y PostBuild.prg, por si quieres hacer algo antes o después de compilar

Dentro de src\controllers\ hay un DemoController.prg con siete endpoints de ejemplo (hello, users, search…). Lo pone FoxForge para que el proyecto sirva algo desde el primer momento. No es tuyo: cuando termines este tutorial, quítalo del proyecto y vuelve a compilar, y sus rutas desaparecen.

Paso 5. El controlador de Artículos

Un controlador es una clase VFP con endpoints dentro. Vamos a crear uno para los artículos del almacén.

  1. En el Project Manager, abre la pestaña Code, selecciona Programs y pulsa New….
  2. Se abre un editor de programa vacío. Pega dentro este código entero:
Articulos.prg
*-- Articulos.prg: el controlador del tutorial.
*-- Cada PROCEDURE con su cláusula HELP es un endpoint.
DEFINE CLASS Articulos AS ApiController OLEPUBLIC

    *-- Se ejecuta ANTES de cada petición: abre la tabla.
    FUNCTION BeforeEndpoint(req, res)
        SET DELETED ON
        SET EXCLUSIVE OFF
        USE articulos IN 0 SHARED AGAIN
    ENDFUNC

    *-- Se ejecuta DESPUÉS de cada petición: lo cierra todo.
    PROCEDURE AfterEndpoint(req, res)
        CLOSE DATABASES ALL
    ENDPROC

    *-- GET /articulos: la tabla entera en JSON.
    PROCEDURE pArticulos(req, res) HELP "GET: articulos public as articulos.lista"
        LOCAL lcJson

        SELECT codigo, descripcion, precio, stock ;
            FROM articulos ;
            WHERE activo ;
            ORDER BY descripcion ;
            INTO CURSOR art

        lcJson = THIS.TableToJson("art")
        USE IN SELECT("art")

        RETURN THIS.OkRaw(lcJson)
    ENDPROC

    *-- GET /articulos/A001: un artículo por su código.
    PROCEDURE pArticulo(req, res) HELP "GET: articulos/{codigo} public as articulos.uno"
        LOCAL lcCodigo, loArt

        lcCodigo = req.GetParam("codigo", "")
        IF EMPTY(lcCodigo)
            RETURN THIS.BadRequest("Falta el código del artículo")
        ENDIF

        SELECT articulos
        LOCATE FOR ALLTRIM(articulos.codigo) == ALLTRIM(lcCodigo)
        IF !FOUND()
            RETURN THIS.NotFound("No existe el artículo " + lcCodigo)
        ENDIF

        loArt = THIS.NewObject("codigo,descripcion,precio,stock")
        loArt.codigo      = ALLTRIM(articulos.codigo)
        loArt.descripcion = ALLTRIM(articulos.descripcion)
        loArt.precio      = articulos.precio
        loArt.stock       = articulos.stock

        RETURN THIS.Ok(loArt)
    ENDPROC

    *-- POST /articulos: dar de alta un artículo.
    PROCEDURE pAlta(req, res) HELP "POST: articulos public as articulos.alta"
        LOCAL loDatos, loNuevo, lcDesc

        loDatos = req.json
        IF ISNULL(loDatos) OR VARTYPE(loDatos) != "O"
            RETURN THIS.BadRequest("El cuerpo tiene que ser un objeto JSON", "cuerpo_invalido")
        ENDIF

        lcDesc = ""
        TRY
            lcDesc = ALLTRIM(TRANSFORM(loDatos.descripcion))
        CATCH
            lcDesc = ""
        ENDTRY

        IF EMPTY(lcDesc)
            RETURN THIS.BadRequest("Falta la descripción", "falta_descripcion")
        ENDIF

        loNuevo = THIS.NewObject("codigo,descripcion")
        loNuevo.codigo      = THIS.NewGuid()
        loNuevo.descripcion = lcDesc

        INSERT INTO articulos (codigo, descripcion, activo) ;
            VALUES (loNuevo.codigo, loNuevo.descripcion, .T.)

        RETURN THIS.Created(loNuevo)
    ENDPROC

ENDDEFINE
  1. Guárdalo con File › Save (o Ctrl+S). Te pedirá dónde, y ojo: el diálogo no se abre en la carpeta del proyecto sino en la última en la que estuvo Visual FoxPro: la ide\ de FoxServer, si hiciste el CD del paso 1. La forma más rápida de llegar: escribe C:\proyectos\Almacen\src\controllers en la casilla del nombre y pulsa Intro, que te mete en esa carpeta. Después escribe Articulos.prg y pulsa Guardar.

Diálogo Save As dentro de la carpeta controllers con el nombre Articulos.prg

  1. Cierra el editor. En el Project Manager, dentro de Code › Programs, tiene que aparecer articulos.

Project Manager con articulos en la lista de programas

Si no aparece en la lista, el fichero se guardó pero no quedó dentro del proyecto, y para el servidor no existe. Selecciona Programs, pulsa Add… y elige src\controllers\Articulos.prg.

Qué hace cada parte

Léelo con calma, que es VFP de toda la vida:

  • DEFINE CLASS Articulos AS ApiController OLEPUBLIC. Tu clase hereda de ApiController, que es donde vive todo lo de THIS. que usas dentro. OLEPUBLIC es la palabra de siempre para que una clase se pueda usar desde fuera de VFP.
  • La cláusula HELP se lee así: "<VERBO>: <ruta> [public] [as <nombre>]".
    • El verbo es el método HTTP: GET para consultar y POST para dar de alta.
    • La ruta es lo que va detrás del puerto en la dirección. {codigo} es un trozo variable: en articulos/A001, codigo vale A001.
    • public quiere decir que no hace falta token para llamarlo. Sin él, si activas la autenticación, lo pedirá.
    • as articulos.lista es el nombre del endpoint. Ponlo siempre: si no, FoxServer se inventa uno a partir de la ruta, y dos endpoints GET sobre articulos acaban con el mismo nombre y una línea [ERROR] en la ventana del servidor.
    • La cláusula HELP no puede pasar de 255 caracteres. A 256, VFP no compila y el error no menciona la longitud por ningún lado.
  • BeforeEndpoint y AfterEndpoint se ejecutan antes y después de cada petición. Si tu aplicación tiene un procedimiento de arranque que pone SETs y rutas, este es su sitio.
  • TableToJson("art") convierte un cursor entero en JSON en una sola llamada. Es la función que ahorra el noventa por ciento del trabajo de una API de consulta.
  • OkRaw y no Ok en pArticulos, porque TableToJson() ya devuelve JSON: si lo pasas por Ok() se convierte otra vez y llega como un texto lleno de barras. Y al revés: Ok() con una cadena no convierte nada y devuelve {}. Pásale siempre un objeto.
  • Ojo al alias en pArticulo: busca sobre articulos, la tabla que abre BeforeEndpoint, y no sobre el cursor art. Ese cursor nace y muere dentro de pArticulos. Usarlo aquí da un error «No se encuentra el alias ‘ART’», de los que cuestan un rato porque el código se lee perfectamente.
  • req es la petición que llega: req.GetParam("codigo", "") para los trozos de la ruta, req.GetQuery("q", "") para lo que va detrás de ?, req.json para el cuerpo ya convertido en objeto. El segundo argumento es el valor por defecto; sin él, te enteras tarde de que no venía.
  • Valida siempre lo que entra: lo que llega por la red no lo ha escrito tu formulario. Y no uses PEMSTATUS() sobre req.json para ver si viene un campo, porque en el banco de pruebas falla. El acceso directo dentro de un TRY, como en pAlta, funciona siempre.
  • Lo que devuelves son códigos de estado HTTP, y cada uno tiene su función: Ok (200), OkRaw (200), Created (201), NoContent (204), BadRequest (400), Unauthorized (401), Forbidden (403), NotFound (404) y Fail(estado, mensaje, código) para cualquier otro. La lista completa, en la referencia.

Paso 6. La tabla de ejemplo

Vamos a crear la tabla articulos con dos registros para probar. Si ya tienes la tuya, en el recuadro de abajo te digo dónde tiene que estar.

Escribe estas líneas en la ventana de comandos, una a una, pulsando Intro después de cada una:

Crear la tabla de ejemplo
CD C:\proyectos\Almacen\build
CREATE DATABASE almacendata
CREATE TABLE articulos (codigo C(32), descripcion C(60), precio N(10,2), stock N(10,0), activo L)
INSERT INTO articulos VALUES ("A001", "Tornillo M6", 0.42, 1200, .T.)
INSERT INTO articulos VALUES ("A002", "Arandela M6", 0.11, 3400, .T.)
CLOSE DATABASES ALL

Ventana de comandos con las seis líneas que crean la tabla de ejemplo

Tres detalles de estas líneas, y ninguno es capricho:

  • La tabla va en build\. El servidor busca un USE articulos sin ruta en la carpeta de la DLL. Si tu tabla ya existe en otro sitio, ábrela con su ruta completa en BeforeEndpoint, o pon ahí un SET PATH.
  • Va dentro de una base de datos. descripcion tiene once letras, y una tabla libre no admite nombres de campo de más de diez. Te contestaría con un error 1712 que habla de nombres duplicados y no dice nada de la longitud. Y codigo es C(32) porque es lo que mide el código que genera NewGuid() en las altas.
  • CLOSE DATABASES ALL al final es obligatorio. El IDE abre las tablas en exclusiva (SET EXCLUSIVE ON es lo que trae por defecto), y mientras la tengas abierta aquí, el servidor no puede usarla. Sin esa línea, en el paso 9 verás un error 1705. Te lo enseño allí para que lo reconozcas.

Paso 7. Compilar

Se compila como siempre: con el botón Build… del Project Manager. Hay un solo detalle distinto de tus proyectos de siempre, y es la carpeta donde se guarda la DLL. Léelo antes de pulsar nada.

  1. En el Project Manager de Almacen, pulsa Build….
  2. En Build Options, marca Multi-threaded COM server (dll) y Recompile All Files, y pulsa OK.

Build Options con Multi-threaded COM server (dll) y Recompile All Files marcados

  1. Visual FoxPro pregunta dónde guardar la DLL y propone almacen.dll en la carpeta del proyecto. No aceptes esa carpeta. Haz doble clic en la carpeta build para entrar en ella, deja el nombre almacen.dll y pulsa Guardar.

Diálogo Save As dentro de la carpeta build con el nombre almacen.dll

Si guardas la DLL en la carpeta del proyecto por error. Visual FoxPro no se queja: compila, y el servidor arranca y carga tus tres endpoints como si nada. El fallo sale en la primera petición, con un error 500 que dice:

El archivo 'c:\proyectos\almacen\articulos.dbf' no existe.

Por qué pasa: el servidor busca las tablas en la carpeta donde está la DLL, y la tabla articulos está en build\. Cómo se arregla: vuelve a compilar con Build…, entra esta vez en la carpeta build al guardar y arranca el servidor de nuevo (paso 8). La DLL que quedó suelta en C:\proyectos\Almacen\ puedes borrarla.

Visual FoxPro no recuerda la carpeta: te propondrá la del proyecto cada vez que compiles, así que este aviso vale para siempre.

  1. Si ya habías compilado antes, Windows te preguntará si quieres reemplazar almacen.DLL. Responde Sí.

Aviso de Windows: almacen.DLL ya existe, ¿desea reemplazarlo?

Tarda unos segundos. Mientras compila, FoxForge lee todas las cláusulas HELP y apunta tus rutas en la configuración del servidor: ese es el momento en que tus endpoints empiezan a existir. Si no sale ninguna ventana de errores, ha compilado.

Si sale una ventana de errores, es un error de VFP en tu código, con su fichero y su línea, como en cualquier proyecto. Corrígelo y vuelve a compilar. Lo más habitual al pegar: una comilla que se ha perdido en una cláusula HELP.

Si al terminar de compilar sale esta ventana, abriste Visual FoxPro sin administrador:

Ventana de Visual FoxPro con el error Access to system Registry denied

«Access to system Registry denied» quiere decir que la DLL ha salido a medias. Visual FoxPro compila, pero al final no puede registrarla en Windows y la deja incompleta: pesa unos 24 KB en lugar de más de 200 KB, y el servidor no puede usarla. Pulsa OK, cierra Visual FoxPro, vuelve a abrirlo como administrador (paso 1) y compila otra vez.

Más cómodo: que FoxForge compile por ti. Con el proyecto abierto, FoxForge › Proyecto actual › Compilar la DLL en build\, o Ctrl+F7 desde cualquier sitio del IDE. Hace lo mismo que el botón Build… pero sin preguntar nada: recompila todo, deja la DLL en build\ con el nombre del proyecto y, si el servidor estaba en marcha, lo vuelve a arrancar. Así no hay carpeta que equivocar.

Menú FoxForge › Proyecto actual con Compilar la DLL en build\ (Ctrl+F7) y Arrancar el servidor arriba del todo

Si no ves esas opciones en Proyecto actual, abriste el proyecto después de cargar FoxForge: pulsa FoxForge › Refrescar menú.

Paso 8. Arrancar el servidor

El servidor es un programa aparte, FoxServer.Host.exe, que carga tu DLL y atiende las peticiones. Arráncalo desde el menú: FoxForge › Proyecto actual › Arrancar el servidor (la misma imagen del paso 7). FoxForge sabe dónde está instalado FoxServer y dónde está la configuración de tu proyecto.

¿Prefieres la ventana de comandos? Esta línea hace lo mismo. RUN /N abre un programa sin esperar a que termine, y --config le dice al servidor en qué carpeta está la configuración de tu proyecto. Si instalaste FoxServer en otra carpeta, cambia la primera ruta por la carpeta bin\ de tu instalación.

Arrancar el servidor con tu proyecto
RUN /N "C:\Program Files (x86)\FoxServer\bin\FoxServer.Host.exe" --config C:\proyectos\Almacen\build

Se abre una ventana negra: es el servidor. Al cabo de un par de segundos tiene que terminar con estas líneas:

Listening: http://127.0.0.1:8099/
  Server : Almacen [api] - C:\proyectos\Almacen\build\almacen.DLL

Press Ctrl+C to stop.

Un poco más arriba verás tus dos controladores cargados: DemoController con 7 endpoint(s) loaded y Articulos con 3 endpoint(s) loaded.

Ventana del servidor FoxServer con 3 endpoint(s) loaded y Listening en http://127.0.0.1:8099/

Deja esa ventana abierta mientras pruebas: si la cierras, se apaga la API. Cada petición que llegue dejará ahí unas líneas, que es donde mirar cuando algo no va. Para parar el servidor, pulsa Ctrl+C en ella o ciérrala.

Paso 9. Probar en el navegador

Abre tu navegador y escribe esta dirección en la barra, como si fuera una página web:

http://127.0.0.1:8099/articulos

Tienes que ver tu tabla en JSON, ordenada por descripción, «Arandela» antes que «Tornillo», que es lo que pide la consulta:

[{
  "codigo": "A002",
  "descripcion": "Arandela M6",
  "precio": 0.11,
  "stock": 3400
},{
  "codigo": "A001",
  "descripcion": "Tornillo M6",
  "precio": 0.42,
  "stock": 1200
}]

Navegador en http://127.0.0.1:8099/articulos con los dos artículos en JSON

Ahora uno solo, por su código:

http://127.0.0.1:8099/articulos/A001
{
  "codigo": "A001",
  "descripcion": "Tornillo M6",
  "precio": 0.42,
  "stock": 1200
}

Navegador en /articulos/A001 con el artículo Tornillo M6

Y uno que no existe, para ver el NotFound del código:

http://127.0.0.1:8099/articulos/ZZZZ
{
  "type": "about:blank",
  "title": "Not Found",
  "status": 404,
  "detail": "No existe el artículo ZZZZ",
  "instance": "/articulos/ZZZZ",
  "code": "not_found"
}

Navegador en /articulos/ZZZZ con el error 404 No existe el artículo ZZZZ

Ese formato es Problem Details, la RFC 9457: la forma estándar de describir un error en una API, la que ya entienden los programas que la van a llamar. El detail es tu mensaje, tal como lo escribiste en THIS.NotFound(...). Las respuestas buenas no llevan ese envoltorio: salen como las has visto arriba.

Si en vez de eso ves un error 500 con «1705»

{
  "type": "about:blank",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "Internal server error: Error: .beforeendpoint c:\\proyectos\\almacen\\build\\src\\controllers\\articulos.prg Error en la línea 9 Denegado c:\\proyectos\\almacen\\build\\articulos.dbf al acceso al archivo. 1705, Error Code: 1705",
  "instance": "/articulos"
}

Navegador con el error 500 y el código 1705 por la tabla abierta

Es la tabla, que sigue abierta en tu Visual FoxPro: te saltaste el CLOSE DATABASES ALL del paso 6. Escríbelo ahora en la ventana de comandos y recarga la página, sin tocar el servidor.

Paso 10. Dar de alta un artículo desde Visual FoxPro

Un navegador solo sabe pedir con GET. Para dar de alta hace falta un POST con el artículo dentro, y eso lo hacemos desde el propio Visual FoxPro con el objeto ServerXMLHTTP de Windows, el mismo con el que muchos ya llamáis a servicios web. Escribe estas líneas en la ventana de comandos, una a una:

Llamar a la API desde la ventana de comandos
oHttp = CREATEOBJECT("MSXML2.ServerXMLHTTP.6.0")
oHttp.Open("POST", "http://127.0.0.1:8099/articulos", .F.)
oHttp.SetRequestHeader("Content-Type", "application/json")
oHttp.Send('{"descripcion":"Arandela M8"}')
? oHttp.Status
? oHttp.ResponseText

En la pantalla principal de Visual FoxPro tienen que salir un 201, que es «creado», y el artículo nuevo con su código:

201
{
  "codigo": "4241c943794c45ccac61dd4af6361c44",
  "descripcion": "Arandela M8"
}

Pantalla de Visual FoxPro con el 201 y el artículo nuevo, y las seis líneas en la ventana de comandos

Tu código será otro: es un identificador único que se genera en cada alta. Vuelve al navegador, recarga http://127.0.0.1:8099/articulos y ahí está, con precio y stock a cero porque solo mandamos la descripción.

Paso 11. Cambiar algo y volver a compilar

Así va a ser tu día a día: tocas el código, compilas y pruebas. Hay un detalle que conviene saber desde ya.

Al compilar, el servidor se para. La DLL está en uso mientras el servidor la tiene cargada, y Windows no deja sobrescribirla. FoxForge lo para por ti antes de compilar, y la ventana negra se cierra sola.

Así que el ciclo es:

  1. Cambia lo que quieras en Articulos.prg y guárdalo.
  2. Pulsa Ctrl+F7 (o FoxForge › Proyecto actual › Compilar la DLL en build\). Compila y vuelve a arrancar el servidor él solo.
  3. Recarga el navegador.

Si compilas con el botón Build…, el servidor no vuelve solo: entra en la carpeta build al guardar, responde Sí a reemplazar y arráncalo otra vez con FoxForge › Proyecto actual › Arrancar el servidor, o con la línea RUN /N del paso 8 (en la ventana de comandos, sube con la flecha hasta ella y pulsa Intro).

Ventana de comandos con la línea RUN /N que arranca el servidor

Si añades un endpoint y el navegador te da 404, falta este ciclo. Vuelve a las tres cosas del principio: la ruta no existe hasta que compilas.

Si algo no sale

Lo que vesQué pasaQué hacer
El botón Crear del asistente sigue grisFalta un dato, o la carpeta no existeLee el recuadro de abajo del asistente; si dice que la carpeta no existe, vuelve al paso 3
Al compilar, «Access to system Registry denied»Visual FoxPro no está abierto como administrador y la DLL ha salido a mediasCiérralo, ábrelo como administrador (paso 1) y compila otra vez
La ventana negra del servidor se abre y se cierra enseguidaNo encuentra la configuración, o no hay DLLArráncalo desde FoxForge › Proyecto actual › Arrancar el servidor; con RUN /N, revisa que la ruta de --config sea C:\proyectos\Almacen\build. Y que el paso 7 terminara sin errores
El navegador dice que no puede conectarEl servidor no está arrancadoMira si la ventana negra sigue abierta; si no, repite el paso 8
En Proyecto actual no salen Compilar ni Arrancar el servidorAbriste el proyecto después de cargar FoxForgeFoxForge › Refrescar menú
404 en una ruta que acabas de escribirNo has compilado desde que la añadistePaso 11
500 con «El archivo c:\proyectos\almacen\articulos.dbf no existe»Guardaste la DLL en la carpeta del proyecto y no en build\Compila con Ctrl+F7, que la deja en su sitio, o con Build… entrando en la carpeta build al guardar
500 con 1705La tabla está abierta en tu Visual FoxProCLOSE DATABASES ALL en la ventana de comandos
500 con «No se encuentra el alias»Usas un alias que no existe en ese endpointMira el apunte del alias en el paso 5
403 llamando desde otro equipoEs la licencia, no tu códigoMira el recuadro de abajo

Un detalle para cuando quieras que te llamen desde otra máquina. El proyecto nace atendiendo solo en 127.0.0.1, así que desde fuera ni siquiera conecta: eso no es la licencia, es la configuración (host en build\Almacen.json). Cuando lo cambies a la IP de tu equipo, verás lo segundo: si en vez de 200 te devuelve 403, no es tu endpoint, es la licencia. Una licencia Developer solo acepta peticiones desde la propia máquina. Para servir a otras hace falta una licencia Runtime, que es la que se instala en el servidor del cliente. Mientras desarrollas no la necesitas.

Lo que has hecho, y lo que no

Has publicado una tabla de tu aplicación como API REST con tres endpoints, sin salir del IDE, sin reescribir la lógica, sin cambiar de lenguaje y sin un framework por medio. Todo lo que has escrito es VFP.

Lo que no has hecho todavía es ponerla en producción: certificado HTTPS, que el servidor arranque solo como servicio de Windows, los registros, y cómo desplegar una versión nueva sin tirar el servidor. Eso es el siguiente tutorial, porque es donde más se atasca la gente que ya tiene sistemas funcionando. Mientras tanto, la documentación de FoxServer lo cuenta por partes.

¿Te gustan las líneas de comandos? Tienes este mismo tutorial en versión CLI, con los mismos pasos convertidos en comandos que se pueden pegar y repetir.

Si algo de aquí no te sale como está escrito, escríbeme a soporte@irwinrodriguez.dev con lo que ves en pantalla. Cada salida de este artículo está copiada de una ejecución real, y si la tuya es distinta quiero saberlo.