https://github.com/freckle/wai-middleware-openapi
https://github.com/freckle/wai-middleware-openapi
ghvm-managed
Last synced: about 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/freckle/wai-middleware-openapi
- Owner: freckle
- License: mit
- Created: 2024-10-10T13:12:18.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-04-21T20:48:57.000Z (about 2 months ago)
- Last Synced: 2026-04-21T22:37:25.985Z (about 2 months ago)
- Topics: ghvm-managed
- Language: Haskell
- Homepage:
- Size: 63.5 KB
- Stars: 1
- Watchers: 4
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# WAI OpenAPI Middleware
Validates request and response bodies against your service's OpenAPI spec.
This is useful in a non-Servant web application, where OpenAPI specs cannot be
generated from the API implementation itself, and so must be maintained
manually.
## Usage
```hs
import Network.Wai (Middleware)
import Network.Wai.Middleware.OpenApi qualified as OpenApi
middleware :: Middleware
middleware =
thisMiddleware
. thatMiddleware
. OpenApi.validate openApi
. theOtherMiddleware
-- Defined in your /docs or /openapi.json Handler
openApi :: OpenApi
openApi = undefined
```
Default behavior:
- If a request body is invalid, a 400 is returned
- If a response body is invalid, a 500 is returned
- In both cases, the validation errors are included in the response body
This is useful if you,
1. Are confident your server is currently complying with spec
2. Trust your spec enough to reject all invalid-according-to-it requests
3. Trust your observability to catch the 5xx increase any future
response-validity bug would cause
If all or some of these are not true, see the next section.
## Configuring
The `validate` function is equivalent to,
```hs
validateRequests defaultOnRequestErrors
. validateResponses defaultOnResponseErrors
```
Where those "on" functions take the appropriate error type and return a
`Middleware`. The reason it returns a middleware is so that it can decide if it
should take over the response or let it run normally:
```hs
defaultOnRequestErrors :: RequestErrors -> Middleware
defaultOnRequestErrors = \case
RequestSchemaNotFound {} -> id -- respond normally
RequestIsNotJson {} -> id
RequestInvalid _ errs -> \_ _ respond ->
respond $ clientErrorResponse errs -- respond with an error
```
Implementing and using a function like this would be how you:
1. Decide to error on missing schema, etc
2. Change the shape of of the JSON errors
3. Emit non-JSON errors
## Evaluation
When first implementing this, you probably want to log invalid cases but still
respond normally. To support this use-case, the library ships replacements for
`defaultOn*` that are named `evaluateOn*`. These functions take an action to
apply to the errors (presumably to log them) and then responds normally.
```hs
validateRequests (evaluateOnRequestErrors logIt)
. validateResponses (evaluateOnResponseErrors logIt)
logIt :: Show e => e -> IO ()
logIt = undefined
```
The action is necessarily `IO` because we're in a WAI middleware context.
## Performance & Sampling
This middleware may add a performance tax depending on the size of your typical
requests, responses, and OpenAPI spec itself. If you are concerned, we recommend
enabling this middleware on a sampling of requests.
For example,
```hs
openApiMiddleware :: OpenApi.Settings -> Middleware
openApiMiddleware settings =
-- Only validate 20% of requests
sampledMiddleware 20 $ OpenApi.validate spec
sampledMiddleware :: Int -> Middleware -> Middleware
sampledMiddleware percent m app request respond = do
roll <- randomRIO (0, 100)
if percent <= roll
then m app request respond
else app request respond
```
> [!NOTE]
> We will likely upstream `sampledMiddleware` to `wai-extra` at some point.
---
[CHANGELOG](./CHANGELOG.md) | [LICENSE](./LICENSE)