Middleware
Middleware in FoxServer are components that process requests before or after they reach the controller. You can use the built-in ones (JWT, CORS, logging) or write your own.
JWT — Authentication
The JWT middleware protects private endpoints. Clients must send a valid token in the Authorization header.
Configuration in JSON:
"middleware": {
"auth": {
"enabled": true,
"type": "jwt",
"secret": "your-256-bit-secret-key-base64-encoded",
"tokenexpirationseconds": 3600,
"loginendpoint": "auth/login"
}
} Authentication flow:
- Client POSTs to the login endpoint with credentials.
- Controller validates credentials and generates a JWT token.
- Client stores the token and sends it on subsequent requests.
- Middleware intercepts the request, validates the token before reaching the controller.
- If the token is invalid or expired, middleware returns 401 automatically.
Login controller (generates the token):
PROCEDURE PostLogin(req, res) HELP "POST: auth/login public"
LOCAL lcUser, lcPass, lcToken, loResp
lcUser = req.json.username
lcPass = req.json.password
IF !THIS.ValidateCredentials(lcUser, lcPass)
res.status(401).json('{"error":"Invalid credentials"}')
RETURN
ENDIF
* GenerateJWT is a built-in ApiController method
lcToken = THIS.GenerateJWT(lcUser)
loResp = THIS.newObject("token,expiresin")
loResp.token = lcToken
loResp.expiresin = 3600
res.status(200).json(THIS.ToJson(loResp))
ENDPROC Protected endpoint (no additional code needed):
* Omit "public" — JWT middleware runs automatically before this
PROCEDURE GetMyProfile(req, res) HELP "GET: profile"
LOCAL lcUserId, loResp
lcUserId = THIS.GetUserIdFromToken(req)
loResp = THIS.newObject("id,name,email")
loResp.id = lcUserId
loResp.name = THIS.GetUserName(lcUserId)
loResp.email = THIS.GetUserEmail(lcUserId)
res.status(200).json(THIS.ToJson(loResp))
ENDPROC # Request with valid token:
curl http://localhost:8080/api/profile \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Request without token → 401 Unauthorized (automatic)
curl http://localhost:8080/api/profile
# {"error":"Unauthorized","message":"Missing or invalid token"} CORS — Cross-Origin
The CORS middleware automatically responds to OPTIONS preflight requests and adds the necessary headers to all responses.
{
"allowedorigins": "https://myapp.com,https://admin.myapp.com",
"allowedmethods": "GET,POST,PUT,DELETE,OPTIONS",
"allowedheaders": "Content-Type,Authorization,X-Requested-With"
} # Browser sends preflight:
OPTIONS /api/products HTTP/1.1
Origin: https://myapp.com
Access-Control-Request-Method: POST
# FoxServer responds automatically:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400 OPTIONS/preflight requests are answered automatically by the middleware without reaching the controller.
Logging
Automatically logs requests, responses and errors to daily files.
- Simple — one line per request
- Detailed — full information including headers and body (useful in development)
- JSON — structured format for consumption by monitoring tools
Sample output (Detailed format):
[2026-04-11 14:23:45.123] INFO GET /api/v1/products?page=1
Client : 192.168.1.100
Status : 200 OK
Duration : 45ms
Bytes out : 2048
[2026-04-11 14:23:46.456] ERROR POST /api/v1/orders
Client : 192.168.1.100
Status : 500 Internal Server Error
Error : Database connection failed
Duration : 1250ms Custom middleware (Hooks)
You can intercept the request/response cycle with project-level hooks:
* Root controller — hooks apply to all endpoints in this project
DEFINE CLASS AppController AS ApiController OLEPUBLIC
* Runs before EVERY endpoint
FUNCTION BeforeRequest(req, res) AS BOOLEAN
* Example: block specific IPs
IF req.remoteIP == "10.0.0.99"
res.status(403).json('{"error":"Forbidden"}')
RETURN .F. && .F. = stop processing
ENDIF
* Open shared resources
SET DELETED ON
SET EXCLUSIVE OFF
RETURN .T. && .T. = continue to controller
ENDFUNC
* Runs after EVERY response is sent
PROCEDURE AfterResponse(req, res)
* Cleanup, metrics, audit log
THIS.LogAudit(req.method, req.path, res.statusCode, req.remoteIP)
ENDPROC
ENDDEFINE Hooks are special methods in the root controller. Returning .F. in BeforeRequest aborts the request and returns 403 automatically.
Next: Licensing →