Skip to content

mpenet/legba

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Legba



He who controls access between the two, deciding who and what can pass through


Legba is a library designed for building fully OpenAPI 3.1 compliant services in Clojure.

Legba streamlines the creation of OpenAPI based servers by emphasizing the OpenAPI schema file as the foundation of your service definition. Unlike other libraries that generate the schema from routes & validation defined in a separate DSL, Legba takes a different approach: it uses your OpenAPI schema to create the necessary routes and wraps your supplied handlers with OpenAPI validation.

Legba ensures that the final OpenAPI file exposed to your users remains unrestricted, reviewable, and editable.

Legba goals

  • Provide rock solid, full coverage of OpenAPI 3.1 spec

  • Support all the bells & whistles such as $refs, conditionals, etc

  • Great performance, being built on networknt/json-schema-validator, which consistently ranks on the top of the java based json-schema validators for both performance and correctness.

  • Be server adapter agnostic, it should be usable with any RING compliant server adapter (jetty, http-kit, aleph, hirundo, etc...)

  • Provide building blocks to build on top of your preferred routing libraries or plug onto any existing router

  • But can also provide a default, easy to use single handler entry point with routing for a given schema, built on the aforementioned primitives

  • Provide detailed, informative and customizable error messages

  • Defaults to https://datatracker.ietf.org/doc/html/rfc9457 for error formatting

  • Works with both JSON and YAML OpenAPI schemas

How does it work

You can either:

  • Either use s-exp.legba.handler/handlers: taking an OpenAPI file and a map of [method path] -> handler, it will return a new map of [method path] -> handler, with all handlers wrapped with an OpenAPI validation middleware. From this map you can then plug the OpenAPI handlers in any routing solution and compose as you prefer.

  • Or use s-exp.legba/routing-handler: taking an OpenAPI file and a map of [method path] -> handler that matches the routes of the OpenAPI schema, returns a single handler that will manage routing and perform validation and marshaling of the data according to the schema (via networknt/json-schema-validator). This handler can simply be plugged to a RING server adapter and you're good to go.

Route Syntax

Examples:

[:get "/items"]
[:get "/item/{itemId}"]     ;; Path parameter
[:post "/items"]
[:delete "/user/{userId}/role/{roleId}"] ;; Multiple params
[:get "/assets/*"] ;; Prefix matching

Route Syntax Rules:

  • http-method is a keyword: e.g. :get, :put, :patch, etc.
  • path is a string using {param} for path parameters—matching OpenAPI.
  • All path parameters will be extracted and made available in the request under :path-params (default, configurable).
  • You can define routes with multiple or nested parameters: /foo/{id}/bar/{otherId}
  • You can define routes with prefixes such as /foo/bar/*, in which case any request starting with /foo/bar/ will be accepted.
  • Additional static or catch-all routes can be added via the :extra-routes option (for custom needs outside of the OpenAPI schema).

Installation

Clojars Project

Or via git deps:

com.s-exp/legba {:git/url "https://github.com/mpenet/legba.git" :git/sha "..."}

Documentation

API docs

Usage

/!\

Please note that this is still an alpha release. While it is fairly stable and I make an effort to maintain API contracts, some aspects are still under development. I reserve the right to make changes until the alpha tag is removed.

(require '[s-exp.legba :as l])

(l/routing-handler {[:get "/item/{itemId}"]
                    (fn [_request]
                      {...})
                    [:get "/items"]
                    (fn [_request]
                      {...})
                    [:get "/search"]
                    (fn [_request]
                      {...})
                    [:post "/items"]
                    (fn [_request]
                      {..})}
                   "classpath://schema/oas/3.1/catalog.json"
                   ;; {...} ; options
                   )

There's also an extra argument with options:

  • :not-found-response - defaults to {:status 404 :body "Not found"}

  • :key-fn - Control map keys decoding when turning jackson JsonNodes to clj data for the handler - default to keyword

  • :query-string-params-key - where to find the decoded query-string parameters - defaults to :query-params

  • :validation-result - function that controls how to turn com.networknt.schema.ValidationResult into a clj -> json response. Defaults to s-exp.legba.schema/validation-result

  • :extra-routes - extra routes to be passed to the underlying router

  • :path-params-key - where to locate the routing extracted path parameters - Defaults to :path-params

  • soft-response-validation - boolean, if true response validation doesn't throw and assocs the error on the ring response as response-validation-error.

  • rfc9457-type-fn - defaults to s-exp.legba.middleware/ex->rfc9457-type - return rfc9457 error type to be used in json+problem payloads from an exception.

Notes

  • You don't have to do any JSON marshaling, if the content-type is of application/json type we will read data as such, same goes for writing. Given the validation library needs the data to be parsed, we preserve this work and re-use the parsed content.

  • ring.middleware.params/wrap-params is applied to the incoming request as to allow path params matching for validation.

Standalone Json-Schema validation

Using s-exp.legba.json-schema you can also perform standalone json-schema validation.

Example

Define a simple JSON schema file json-schema.json

  {"type" "object"
   "properties" {"name" {"type" "string"}
                 "age"  {"type" "integer"}}
   "required" ["name" "age"]
   "additionalProperties" false}
(require '[s-exp.legba.json-schema :as json-schema]

;; Validate the data
(-> (json-schema/schema "classpath://json-schema.json")
    (json-schema/validate!  "{\"name\":\"Alice\",\"age\":30}"))

OpenAPI Overlay Support

Legba provides utilities for OpenAPI Overlay via s-exp.legba.overlay.

This allows you to dynamically update or remove parts of your OpenAPI schema using overlay instructions, making it easy to tailor or extend schemas programmatically.

Main Functionality

  • The core function is s-exp.legba.overlay/apply:
    • Takes an OpenAPI schema (as a JSON string) and overlay instructions (as a JSON string).
    • Applies update/remove actions as per the overlay specification, returning the modified schema.

Example

(require '[s-exp.legba.overlay :as overlay])

(def openapi-schema
  (slurp "path/to/openapi.json"))
(def overlay-json
  "{\"actions\": [
      {\"target\": \"$..['x-private']\", \"remove\": true}
    ]}")

;; Returns updated schema JSON string with actions applied
(overlay/apply openapi-schema overlay-json)

This can be useful for customizing schemas for different consumers, removing internal fields, or overlaying additional information as needed.

License

Copyright © 2024-2025 Max Penet

Distributed under the Eclipse Public License version 1.0.

About

Clojure library for building OpenAPI services

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project