### Start REPL and Server Source: https://github.com/metosin/reitit/blob/master/examples/buddy-auth/README.md Instructions for starting the REPL and the server in the example project. ```sh lein repl ``` ```clojure (start) ``` -------------------------------- ### Example Application Setup Source: https://github.com/metosin/reitit/blob/master/doc/ring/transforming_middleware_chain.md Sets up a basic Reitit Ring application with middleware applied at different levels. Demonstrates how middleware is composed. ```clojure (require '[reitit.ring :as ring]) (require '[reitit.middleware :as middleware]) (defn wrap [handler id] (fn [request] (handler (update request ::acc (fnil conj []) id)))) (defn handler [{::keys [acc]}] {:status 200, :body (conj acc :handler)}) (def app (ring/ring-handler (ring/router ["/api" {:middleware [[wrap 1] [wrap 2]]} ["/ping" {:get {:middleware [[wrap 3]] :handler handler}}]]))) (app {:request-method :get, :uri "/api/ping"}) ; {:status 200, :body [1 2 3 :handler]} ``` -------------------------------- ### Start REPL and Restart Server Examples Source: https://github.com/metosin/reitit/blob/master/examples/just-coercion-with-ring/README.md Instructions for starting a REPL and restarting the server with different coercion strategies (naive, schema, spec, data-spec). ```clojure > lein repl (require '[example.server :as server]) ;; the manually coerced version (require '[example.naive :as naive]) (server/restart naive/app) ;; schema-coercion (require '[example.schema :as schema]) (server/restart schema/app) ;; spec-coercion (require '[example.spec :as spec]) (server/restart spec/app) ;; data-spec-coercion (require '[example.dspec :as dspec]) (server/restart dspec/app) ``` -------------------------------- ### Example Application Setup Source: https://github.com/metosin/reitit/blob/master/doc/http/transforming_interceptor_chain.md Sets up a basic Reitit HTTP application with interceptors and a handler. Demonstrates the default interceptor chain execution. ```clojure (require '[reitit.http :as http]) (require '[reitit.interceptor.sieppari :as sieppari]) (defn interceptor [message] {:enter (fn [ctx] (update-in ctx [:request :message] (fnil conj []) message))}) (defn handler [req] {:status 200 :body (select-keys req [:message])}) (def app (http/ring-handler (http/router ["/api" {:interceptors [(interceptor 1) (interceptor 2)]} ["/ping" {:get {:interceptors [(interceptor 3)] :handler handler}}]]) {:executor sieppari/executor})) (app {:request-method :get, :uri "/api/ping"}) ; {:status 200, :body {:message [1 2 3]}} ``` -------------------------------- ### Start Reitit Application Source: https://github.com/metosin/reitit/blob/master/examples/http-swagger/README.md Start the Reitit application in a REPL environment. ```clojure > lein repl (start) ``` -------------------------------- ### Full Reitit and Schema Coercion Example Source: https://github.com/metosin/reitit/blob/master/doc/coercion/coercion.md This example demonstrates setting up a router with Schema coercion and a function to match paths and apply coercion. It shows both successful and failed coercion scenarios. ```clojure (require '[reitit.coercion.schema]) (require '[reitit.coercion :as coercion]) (require '[reitit.core :as r]) (require '[schema.core :as s]) (def router (r/router ["/:company/users/:user-id" {:name ::user-view :coercion reitit.coercion.schema/coercion :parameters {:path {:company s/Str :user-id s/Int}}}] {:compile coercion/compile-request-coercers})) (defn match-by-path-and-coerce! [path] (if-let [match (r/match-by-path router path)] (assoc match :parameters (coercion/coerce! match)))) (match-by-path-and-coerce! "/metosin/users/123") ; #Match{:template "/:company/users/:user-id", ; :data {:name :user/user-view, ; :coercion <<:schema>> ; :parameters {:path {:company java.lang.String, ; :user-id Int}}}, ; :result {:path #object[reitit.coercion$request_coercer$]}, ; :path-params {:company "metosin", :user-id "123"}, ; :parameters {:path {:company "metosin", :user-id 123}} ; :path "/metosin/users/123"} (match-by-path-and-coerce! "/metosin/users/ikitommi") ; => ExceptionInfo Request coercion failed... ``` -------------------------------- ### HTTPie GET Request Example Source: https://github.com/metosin/reitit/blob/master/doc/ring/content_negotiation.md Example of making a GET request to the /xml endpoint using httpie to test forced XML response. ```bash > http :3000/xml HTTP/1.1 200 OK Content-Length: 20 Content-Type: text/xml Date: Wed, 22 Aug 2018 16:59:58 GMT Server: Jetty(9.2.21.v20170120) kukka ``` -------------------------------- ### Minimalistic Pedestal Example with Reitit Router Source: https://github.com/metosin/reitit/blob/master/doc/http/pedestal.md This example demonstrates how to replace Pedestal's default router with a reitit router. It defines custom interceptors and routes, then configures the Pedestal server to use reitit for routing. ```clojure ; [io.pedestal/pedestal.service "0.5.5"] ; [io.pedestal/pedestal.jetty "0.5.5"] ; [metosin/reitit-pedestal "0.10.1"] ; [metosin/reitit "0.10.1"] (require '[io.pedestal.http :as server]) (require '[reitit.pedestal :as pedestal]) (require '[reitit.http :as http]) (require '[reitit.ring :as ring]) (defn interceptor [number] {:enter (fn [ctx] (update-in ctx [:request :number] (fnil + 0) number))}) (def routes ["/api" {:interceptors [(interceptor 1)]} ["/number" {:interceptors [(interceptor 10)] :get {:interceptors [(interceptor 100)] :handler (fn [req] {:status 200 :body (select-keys req [:number])})}}]]) (-> {::server/type :jetty ::server/port 3000 ::server/join? false ;; no pedestal routes ::server/routes []} (server/default-interceptors) ;; swap the reitit router (pedestal/replace-last-interceptor (pedestal/routing-interceptor (http/router routes))) (server/dev-interceptors) (server/create-server) (server/start)) ``` -------------------------------- ### Build Project Source: https://github.com/metosin/reitit/blob/master/doc/development.md Run this command to clean and install the project modules. ```bash ./scripts/lein-modules do clean, install ``` -------------------------------- ### Start Figwheel Development Server Source: https://github.com/metosin/reitit/blob/master/examples/frontend-prompt/README.md Use this command to start the Figwheel development server for the reitit-frontend project. Access the application via the provided localhost URL. ```bash lein figwheel ``` -------------------------------- ### Full Coercion Example with Reitit, Ring, and Schema Source: https://github.com/metosin/reitit/blob/master/doc/ring/coercion.md This example demonstrates setting up Reitit with coercion middleware for request and response handling. It includes defining a custom schema, configuring the router with middleware, and defining routes with parameter and response coercion. ```clojure (require '[reitit.ring.coercion :as rrc]) (require '[reitit.coercion.schema]) (require '[reitit.ring :as ring]) (require '[schema.core :as s]) (def PositiveInt (s/constrained s/Int pos? 'PositiveInt)) (def app (ring/ring-handler (ring/router ["/api" ["/ping" {:name ::ping :get (fn [_] {:status 200 :body "pong"})}] ["/plus/:z" {:name ::plus :post {:coercion reitit.coercion.schema/coercion :parameters {:query {:x s/Int} :body {:y s/Int} :path {:z s/Int}} :responses {200 {:body {:total PositiveInt}}} :handler (fn [{:keys [parameters]}] (let [total (+ (-> parameters :query :x) (-> parameters :body :y) (-> parameters :path :z))] {:status 200 :body {:total total}}))}}]] {:data {:middleware [rrc/coerce-exceptions-middleware rrc/coerce-request-middleware rrc/coerce-response-middleware]}}))) ``` -------------------------------- ### Start reitit-frontend Development Server Source: https://github.com/metosin/reitit/blob/master/examples/frontend-auth/README.md Use this command to start the Figwheel development server for reitit-frontend. Access the application at http://localhost:3449. ```clojure > lein figwheel ``` -------------------------------- ### HTTPie POST Request Example Source: https://github.com/metosin/reitit/blob/master/doc/ring/content_negotiation.md Example of making a POST request to the /math endpoint using httpie to test content negotiation. ```bash > http POST :3000/math x:=1 y:=2 HTTP/1.1 200 OK Content-Length: 11 Content-Type: application/json; charset=utf-8 Date: Wed, 22 Aug 2018 16:59:54 GMT Server: Jetty(9.2.21.v20170120) { "total": 3 } ``` -------------------------------- ### Clojure Performance Test Setup Source: https://github.com/metosin/reitit/blob/master/doc/performance.md This code block outlines the setup for running performance tests in Clojure, including the REPL command and system specifications. It's important to run tests with your own routing tables for accurate results. ```clojure ;; ;; start repl with `lein perf repl` ;; perf measured with the following setup: ;; ;; Model Name: MacBook Pro ;; Model Identifier: MacBookPro11,3 ;; Processor Name: Intel Core i7 ;; Processor Speed: 2,5 GHz ;; Number of Processors: 1 ;; Total Number of Cores: 4 ;; L2 Cache (per Core): 256 KB ;; L3 Cache: 6 MB ;; Memory: 16 GB ;; ``` -------------------------------- ### Per-Content-Type Coercions with Multiple Examples Source: https://github.com/metosin/reitit/blob/master/doc/ring/openapi.md Defines a route with multiple content types and examples for a GET request. Supports JSON and EDN responses, each with specific schemas and named examples. ```clojure "/pizza" {:get {:summary "Fetch a pizza | Multiple content-types, multiple examples" :responses {200 {:description "Fetch a pizza as json or EDN" :content {"application/json" {:schema [:map [:color :keyword] [:pineapple :boolean]] :examples {:white {:description "White pizza with pineapple" :value {:color :white :pineapple true}} :red {:description "Red pizza" :value {:color :red :pineapple false}}}} "application/edn" {:schema [:map [:color :keyword] [:pineapple :boolean]] :examples {:red {:description "Red pizza with pineapple" :value (pr-str {:color :red :pineapple true})}}}}}} ``` -------------------------------- ### Basic Router Setup Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_data_validation.md A simple Reitit router configuration. This serves as a baseline before validation is introduced. ```clojure (require '[reitit.core :as r]) (r/router ["/api" {:handler "identity"}]) ``` -------------------------------- ### Matching with String Parameters Source: https://github.com/metosin/reitit/blob/master/doc/coercion/coercion.md This example demonstrates how a path is matched and the resulting `:path-params` are all strings, reflecting the default behavior. ```clojure (r/match-by-path router "/metosin/users/123") ; #Match{:template "/:company/users/:user-id", ; :data {:name :user/user-view}, ; :result nil, ; :path-params {:company "metosin", :user-id "123"}, ; :path "/metosin/users/123"} ``` -------------------------------- ### Correctly Add Examples to Query Parameters Source: https://github.com/metosin/reitit/blob/master/doc/ring/openapi.md When adding examples for query parameters, ensure they are applied to individual parameters, not the surrounding map schema, due to OpenAPI representation limitations. ```clojure ;; Wrong! {:parameters {:query [:map {:json-schema/example {:a 1}} [:a :int]]}} ;; Right! {:parameters {:query [:map [:a {:json-schema/example 1} :int]]}} ``` -------------------------------- ### Full Example of Middleware Execution Order Source: https://github.com/metosin/reitit/blob/master/doc/ring/ring.md A comprehensive example demonstrating the execution order of middleware applied through various methods: top-level, top-level route data, nested route data, and route-specific middleware. This illustrates the combined effect of different middleware application strategies. ```clojure (def app (ring/ring-handler (ring/router ["/api" {:middleware [[wrap :3-parent]]} ["/get" {:get handler :middleware [[wrap :4-route]]}]] {:data {:middleware [[wrap :2-top-level-route-data]]}}) nil {:middleware [[wrap :1-top]]})) ``` -------------------------------- ### Example Usage of Programmatic Route Generation Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md Shows how to call the `cqrs-routes` function with sample actions to produce a route structure. ```clojure (cqrs-routes [[:query 'get-user] [:command 'add-user] [:command 'add-order]]) ; ["/api" {:interceptors [::api ::db]} ; (["/get-user" {:get {:interceptors [get-user]}}] ; ["/add-user" {:post {:interceptors [add-user]}}] ; ["/add-order" {:post {:interceptors [add-order]}}])] ``` -------------------------------- ### Run Ring Server from Command Line Source: https://github.com/metosin/reitit/blob/master/examples/ring-integrant/README.md Execute this command in your terminal to start the Ring server. ```bash lein run ``` -------------------------------- ### Compojure Dynamic Routing Example Source: https://github.com/metosin/reitit/blob/master/doc/advanced/composing_routers.md A basic example of dynamic routing using Compojure's `context` function, provided for performance comparison with Reitit's dynamic routing. ```clojure (require '[compojure.core :refer [context]]) (def app (context "/dynamic" [] (constantly :duo))) (app {:uri "/dynamic/duo" :request-method :get}) ; :duo ``` -------------------------------- ### Run Ring Server from REPL Source: https://github.com/metosin/reitit/blob/master/examples/ring-integrant/README.md Start the Ring server interactively from the Clojure REPL. Ensure you are in the project directory. ```clojure (go) ``` -------------------------------- ### Test Endpoints with httpie Source: https://github.com/metosin/reitit/blob/master/examples/ring-malli-lite-swagger/README.md Examples of how to test the application's endpoints using the httpie command-line tool. ```bash http GET :3000/math/plus x==1 y==20 ``` ```bash http POST :3000/math/plus x:=1 y:=20 ``` ```bash http GET :3000/swagger.json ``` -------------------------------- ### Bidi Route Syntax Example Source: https://github.com/metosin/reitit/blob/master/doc/faq.md Illustrates Bidi's route definition syntax, which uses special Clojure syntax for route patterns. ```clojure (def routes ["/" [["auth/login" :auth/login] [["auth/recovery/token/" :token] :auth/recovery] ["workspace/" [[[:project-uuid "/" :page-uuid] :workspace/page]]]]]) ``` -------------------------------- ### Reitit Route Syntax Example Source: https://github.com/metosin/reitit/blob/master/doc/faq.md Demonstrates Reitit's route definition syntax, which uses simple path strings and separates them from route data. ```clojure (def routes ["/auth/login" :auth/login] ["/auth/recovery/token/:token" :auth/recovery] ["/workspace/:project-uuid/:page-uuid" :workspace/page]]) ``` -------------------------------- ### App Execution Example (Internal Zone) Source: https://github.com/metosin/reitit/blob/master/doc/ring/route_data_validation.md Demonstrates executing the configured application with a request to an internal zone. This shows a successful request when roles are not explicitly checked or are implicitly valid. ```clojure (app {:request-method :get :uri "/api/zones/admin/ping"}) ; in zone :internal ; => {:status 200, :body "ok"} ``` -------------------------------- ### Frontend Routing with reitit-frontend Source: https://context7.com/metosin/reitit/llms.txt Use reitit.frontend.easy for stateful singleton routing in ClojureScript browser apps. Start the router with `start!`, navigate using `navigate!` and `href`, and access coerced parameters via `match-by-path`. ```clojure (ns my-app.core (:require [reitit.frontend :as rf] [reitit.frontend.easy :as rfe] [reitit.coercion.spec :as rcs] [reagent.core :as r])) (def routes [["/" {:name ::home :view #'home-page}] ["/users" {:name ::users :view #'users-page}] ["/users/:id" {:name ::user :view #'user-page :parameters {:path {:id int?}} :coercion rcs/coercion}]]) (defonce current-match (r/atom nil)) (defn on-navigate [match _history] (reset! current-match match)) (defn init! [] (rfe/start! (rf/router routes) on-navigate {:use-fragment false})) ; use HTML5 history (server must serve index.html for all routes) ;; Generate links (rfe/href ::user {:id 42}) ;; => "/users/42" (rfe/href ::users nil {:page 2 :per-page 20}) ;; => "/users?page=2&per-page=20" ;; Navigate programmatically (rfe/navigate! ::user {:id 42}) (rfe/push-state ::home) (rfe/replace-state ::users nil {:page 3}) ;; Read coerced params in handler (let [match @current-match] (-> match :parameters :path :id)) ; integer, not string ;; => 42 ``` -------------------------------- ### Successful Path Coercion Example Source: https://github.com/metosin/reitit/blob/master/doc/coercion/clojure_spec_coercion.md Demonstrates a successful call to `match-by-path-and-coerce!` with a path that matches the defined route and its parameters are coerced correctly. ```clojure (match-by-path-and-coerce! "/metosin/users/123") ; #Match{:template "/:company/users/:user-id", ; :data {:name :user/user-view, ; :coercion <<:spec>> ; :parameters {:path ::path-params}}, ; :result {:path #object[reitit.coercion$request_coercer$]}, ; :path-params {:company "metosin", :user-id "123"}, ; :parameters {:path {:company "metosin", :user-id 123}} ; :path "/metosin/users/123"} ``` -------------------------------- ### Pedestal Route Syntax Example Source: https://github.com/metosin/reitit/blob/master/doc/faq.md Shows Pedestal's route definition syntax, which can use keyword arguments or maps for route data. ```clojure ["/api/ping" :get identity :route-name ::ping] ``` -------------------------------- ### Define a Simple Ring Router Source: https://github.com/metosin/reitit/blob/master/doc/ring/ring.md Create a basic router with a GET handler for the '/ping' path. ```clojure (require '[reitit.ring :as ring]) (defn handler [_] {:status 200, :body "ok"}) (def router (ring/router ["/ping" {:get handler}])) ``` -------------------------------- ### Router Configuration with Middleware Source: https://github.com/metosin/reitit/blob/master/doc/ring/route_data_validation.md Configure a Reitit router with the `wrap-enforce-roles` middleware. This example shows how the middleware is added to a route group. ```clojure (def app (ring/ring-handler (ring/router ["/api" {:middleware [zone-middleware wrap-enforce-roles]} ;; <--- added ["/public" {:zone :public} ["/ping" {:get handler}]] ["/internal" {:zone :internal} ["/users" {:get {:handler handler} :delete {:handler handler}}]]] {:validate rrs/validate ::rs/explain e/expound-str}))) ``` -------------------------------- ### Test Ring Endpoints with httpie Source: https://github.com/metosin/reitit/blob/master/examples/ring-example/README.md Examples of how to test different Ring endpoints using httpie. Supports plain, schema, data-specs, and specs. ```bash # Plain http GET :3000/plain/plus x==1 y==20 http POST :3000/plain/plus x:=1 y:=20 ``` ```bash # Schema http GET :3000/schema/plus x==1 y==20 http POST :3000/schema/plus x:=1 y:=20 ``` ```bash # Data-specs http GET :3000/dspec/plus x==1 y==20 http POST :3000/dspec/plus x:=1 y:=20 ``` ```bash # Specs http GET :3000/spec/plus x==1 y==20 http POST :3000/spec/plus x:=1 y:=20 ``` -------------------------------- ### Valid Request Example Source: https://github.com/metosin/reitit/blob/master/doc/ring/coercion.md Demonstrates a successful request to the '/api/plus' endpoint with correctly formatted query, body, and path parameters, resulting in a 200 OK response. ```clojure (app {:request-method :post :uri "/api/plus/3" :query-params {"x" "1"} :body-params {:y 2}}) ; {:status 200, :body {:total 6}} ``` -------------------------------- ### Successful Coercion Example Source: https://github.com/metosin/reitit/blob/master/doc/coercion/malli_coercion.md Demonstrates a successful path coercion using the defined router. The output shows the matched route and the correctly coerced parameters. ```clojure (match-by-path-and-coerce! "/metosin/users/123") ; #Match{:template "/:company/users/:user-id", ; :data {:name :user/user-view, ; :coercion <<:malli>> ; :parameters {:path [:map ; [:company string?] ; [:user-id int?]]}}, ; :result {:path #object[reitit.coercion$request_coercer$]}, ; :path-params {:company "metosin", :user-id "123"}, ; :parameters {:path {:company "metosin", :user-id 123}} ; :path "/metosin/users/123"} ``` -------------------------------- ### Slash-Free Routing Examples Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md Demonstrates routes that do not require explicit slashes, useful for dynamic path structures. ```clojure [["broker.{customer}.{device}.{*data}" {...}] ["events.{target}.{type}" {...}]] ``` -------------------------------- ### Matching Root and Nested Routes Source: https://github.com/metosin/reitit/blob/master/doc/advanced/composing_routers.md Examples of matching paths against the composed router, demonstrating both direct static route matches and matches within nested routers like '/beers/lager'. ```clojure (name-path router "/vodka/russian") ; nil (name-path router "/gin/napue") ; [:napue] ``` ```clojure (name-path router "/beers/lager") ; [:beers :lager] (name-path router "/beers/saison") ; nil ``` -------------------------------- ### Router with Closed Specs and Missing Required Key Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_data_validation.md Uses `spec-tools.spell/closed` to enforce that only defined keys are present. This example fails because the required `:description` key is missing. ```clojure (require '[clojure.spec.alpha :as s]) (require '[reitit.core :as r]) (require '[reitit.spec :as rs]) (require '[spec-tools.spell :as spell]) (require '[reitit.dev.pretty :as pretty]) (s/def ::description string?) (r/router ["/api" {:summary "kikka"}] {:validate rs/validate :spec (s/merge ::rs/default-data (s/keys :req-un [::description])) ::rs/wrap spell/closed :exception pretty/exception}) ``` -------------------------------- ### Test Middleware Application Order Source: https://github.com/metosin/reitit/blob/master/doc/ring/data_driven_middleware.md Verify that the middleware functions are applied in the correct order by inspecting the request context after processing. This example demonstrates the accumulated data from nested middleware. ```clojure (app {:request-method :get, :uri "/api/ping"}) ; {:status 200, :body [1 2 3 :handler]} ``` -------------------------------- ### Nested Controllers Example Source: https://github.com/metosin/reitit/blob/master/doc/frontend/controllers.md Controllers from nested routes are concatenated. Root controllers start for any route, and nested controllers start/stop based on their parameters. ```clojure ["/" {:controllers [{:start (fn [_] (js/console.log "root start"))}]} ["/item/:id" {:controllers [{:parameters {:path [:id]} :start (fn [parameters] (js/console.log "item start" (-> parameters :path :id))) :stop (fn [parameters] (js/console.log "item stop" (-> parameters :path :id)))} ]}] ``` -------------------------------- ### Get Router Name Source: https://github.com/metosin/reitit/blob/master/doc/advanced/different_routers.md The router name can be queried from an existing router instance. This example shows the default router name for a given route configuration. ```clojure (require '[reitit.core :as r]) (def router (r/router [["/ping" ::ping] ["/api/:users" ::users]])) (r/router-name router) ``` -------------------------------- ### User API Endpoint with Path Parameter Coercion Source: https://context7.com/metosin/reitit/llms.txt An example of a GET endpoint that retrieves user information by ID. It showcases path parameter definition and automatic type coercion from string to integer. ```APIDOC ## GET /api/users/:id ### Description Retrieves user information based on the provided user ID. The ID is automatically coerced to an integer. ### Method GET ### Endpoint /api/users/:id ### Parameters #### Path Parameters - **id** (int) - Required - The unique identifier for the user. ### Response #### Success Response (200) - **id** (int) - The user's ID. - **name** (string) - The user's name. #### Response Example ```json { "id": 7, "name": "Alice" } ``` ``` -------------------------------- ### Applying RESTful Form Method Middleware in Reitit Source: https://github.com/metosin/reitit/blob/master/doc/ring/RESTful_form_methods.md This example shows how to apply the custom `wrap-hidden-method` middleware within the Reitit Ring handler setup. Ensure `parameters-middleware` and `multipart-middleware` are included before `wrap-hidden-method`. ```clojure (reitit.ring/ring-handler (reitit.ring/router ...) (reitit.ring/create-default-handler) {:middleware [reitit.ring.middleware.parameters/parameters-middleware ;; needed to have :form-params in the request map reitit.ring.middleware.multipart/multipart-middleware ;; needed to have :multipart-params in the request map wrap-hidden-method]}) ;; our hidden method wrapper ``` -------------------------------- ### Install NPM Dependencies for CLJS Source: https://github.com/metosin/reitit/blob/master/CONTRIBUTING.md Install Node.js dependencies required for ClojureScript support. ```bash npm install ``` -------------------------------- ### Simple Reitit HTTP Interceptor Example Source: https://github.com/metosin/reitit/blob/master/doc/http/interceptors.md Demonstrates setting up an HTTP router with interceptors defined at different levels (global, route, and method). Requires `reitit.ring`, `reitit.http`, and an interceptor executor like `reitit.interceptor.sieppari`. ```clojure (require '[reitit.ring :as ring]) (require '[reitit.http :as http]) (require '[reitit.interceptor.sieppari :as sieppari]) (defn interceptor [number] {:enter (fn [ctx] (update-in ctx [:request :number] (fnil + 0) number))}) (def app (http/ring-handler (http/router ["/api" {:interceptors [(interceptor 1)]} ["/number" {:interceptors [(interceptor 10)] :get {:interceptors [(interceptor 100)] :handler (fn [req] {:status 200 :body (select-keys req [:number])})}}]]) ;; the default handler (ring/create-default-handler) ;; executor {:executor sieppari/executor})) (app {:request-method :get, :uri "/"}) ; {:status 404, :body "", :headers {}} (app {:request-method :get, :uri "/api/number"}) ; {:status 200, :body {:number 111}} ``` -------------------------------- ### Reitit Router with Content Negotiation Source: https://github.com/metosin/reitit/blob/master/doc/ring/content_negotiation.md Set up a Reitit Ring router with content negotiation, coercion, and other middlewares. This example demonstrates handling JSON, EDN, and Transit requests and responses, as well as a forced XML response. ```clojure (require '[reitit.ring :as ring]) (require '[reitit.ring.coercion :as rrc]) (require '[reitit.coercion.spec :as rcs]) (require '[ring.adapter.jetty :as jetty]) (require '[muuntaja.core :as m]) (def app (ring/ring-handler (ring/router (["/math" {:post {:summary "negotiated request & response (json, edn, transit)" :parameters {:body {:x int?, :y int?}} :responses {200 {:body {:total int?}}} :handler (fn [{{{:keys [x y]} :body} :parameters}] {:status 200 :body {:total (+ x y)}})}}] ["/xml" {:get {:summary "forced xml response" :handler (fn [_] {:status 200 :headers {"Content-Type" "text/xml"} :body "kukka"})}}]] {:data {:muuntaja m/instance :coercion rcs/coercion :middleware [muuntaja/format-middleware rrc/coerce-exceptions-middleware rrc/coerce-request-middleware rrc/coerce-response-middleware]}}))) (jetty/run-jetty #'app {:port 3000, :join? false}) ``` -------------------------------- ### Wildcards with {bracket} Syntax Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md Examples of using {bracket} path parameters, which can have various terminators. ```clojure [["/api/{version}" {...}] ["/files/{name}.{extension}" {...}] ["/user/{user-id}/orders" {...}]] ``` -------------------------------- ### Get Flattened Routes Source: https://github.com/metosin/reitit/blob/master/doc/basics/router.md Inspect the flattened route tree of a router using `r/routes`. ```clojure (r/routes router) ; [["/api/ping" {:name :user/ping}] ; ["/api/user/:id" {:name :user/user}]] ``` -------------------------------- ### Get Router Name Source: https://github.com/metosin/reitit/blob/master/doc/basics/router.md Retrieve the name of a created router instance using `r/router-name`. ```clojure (r/router-name router) ; :mixed-router ``` -------------------------------- ### Math Plus Endpoint (GET) Source: https://github.com/metosin/reitit/blob/master/doc/ring/swagger.md Performs addition of two integers provided as query parameters. ```APIDOC ## GET /math/plus ### Description Performs addition of two integers provided as query parameters. ### Method GET ### Endpoint /math/plus ### Parameters #### Query Parameters - **x** (int) - Required - The first integer. - **y** (int) - Required - The second integer. ### Request Example `/math/plus?x=5&y=3` ### Response #### Success Response (200) - **total** (int) - The sum of x and y. #### Response Example ```json { "total": 8 } ``` ``` -------------------------------- ### Full Reitit Ring Application Setup Source: https://github.com/metosin/reitit/blob/master/doc/ring/swagger.md This code defines a complete Reitit Ring application, including routing, Swagger generation, Swagger UI integration, and various middleware for request/response handling. It's designed to be served via Jetty. ```clojure (ns example.server (:require [reitit.ring :as ring] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] [reitit.ring.coercion :as coercion] [reitit.coercion.spec] [reitit.ring.middleware.muuntaja :as muuntaja] [reitit.ring.middleware.exception :as exception] [reitit.ring.middleware.multipart :as multipart] [reitit.ring.middleware.parameters :as parameters] [ring.middleware.params :as params] [ring.adapter.jetty :as jetty] [muuntaja.core :as m] [clojure.java.io :as io])) (def app (ring/ring-handler (ring/router [["/swagger.json" {:get {:no-doc true :swagger {:info {:title "my-api"} :basePath "/"} ;; prefix for all paths :handler (swagger/create-swagger-handler)}}] ["/files" {:swagger {:tags ["files"]}} ["/upload" {:post {:summary "upload a file" :parameters {:multipart {:file multipart/temp-file-part}} :responses {200 {:body {:file multipart/temp-file-part}}} :handler (fn [{{{:keys [file]} :multipart} :parameters}] {:status 200 :body {:file file}})}}] ["/download" {:get {:summary "downloads a file" :swagger {:produces ["image/png"]} :handler (fn [_] {:status 200 :headers {"Content-Type" "image/png"} :body (io/input-stream (io/resource "reitit.png"))})}}]] ["/math" {:swagger {:tags ["math"]}} ["/plus" {:get {:summary "plus with spec query parameters" :parameters {:query {:x int?, :y int?}} :responses {200 {:body {:total int?}}} :handler (fn [{{{:keys [x y]} :query} :parameters}] {:status 200 :body {:total (+ x y)}})} :post {:summary "plus with spec body parameters" :parameters {:body {:x int?, :y int?}} :responses {200 {:body {:total int?}}} :handler (fn [{{{:keys [x y]} :body} :parameters}] {:status 200 :body {:total (+ x y)}})}}]]] {:data {:coercion reitit.coercion.spec/coercion :muuntaja m/instance :middleware [;; query-params & form-params parameters/parameters-middleware ;; content-negotiation muuntaja/format-negotiate-middleware ;; encoding response body muuntaja/format-response-middleware ;; exception handling exception/exception-middleware ;; decoding request body muuntaja/format-request-middleware ;; coercing response bodys coercion/coerce-response-middleware ;; coercing request parameters coercion/coerce-request-middleware ;; multipart multipart/multipart-middleware]}}) (ring/routes (swagger-ui/create-swagger-ui-handler {:path "/"}) (ring/create-default-handler)))) (defn start [] (jetty/run-jetty #'app {:port 3000, :join? false}) (println "server running in port 3000")) ``` -------------------------------- ### Get Router Options Source: https://github.com/metosin/reitit/blob/master/doc/basics/router.md Access the options map associated with a router instance using `r/options`. ```clojure (r/options router) {:lookup #object[...] :expand #object[...] :coerce #object[...] :compile #object[...] :conflicts #object[...]} ``` -------------------------------- ### Create a Ring Router with Handlers and Middleware Source: https://github.com/metosin/reitit/blob/master/doc/README.md Set up a Ring-compatible router that includes handlers, middleware, and request method routing. Middleware can be applied at different levels. ```clojure (require '[reitit.ring :as ring]) (defn handler [_] {:status 200, :body "ok"}) (defn wrap [handler id] (fn [request] (update (handler request) :wrap (fnil conj '()) id))) (def app (ring/ring-handler (ring/router ["/api" {:middleware [[wrap :api]]} ["/ping" {:get handler :name ::ping}] ["/admin" {:middleware [[wrap :admin]]} ["/users" {:get handler :post handler}]]]))) ``` -------------------------------- ### Basic Reitit App with Swagger and Swagger UI Source: https://github.com/metosin/reitit/blob/master/doc/ring/swagger.md Sets up a Reitit Ring application with two API routes, a Swagger JSON endpoint, and a Swagger UI endpoint. For production, consider adding content negotiation middleware. ```clojure (require '[reitit.ring :as ring]) (require '[reitit.swagger :as swagger]) (require '[reitit.swagger-ui :as swagger-ui]) (def app (ring/ring-handler (ring/router [["/api" ["/ping" {:get (constantly {:status 200, :body "ping"})}] ["/pong" {:post (constantly {:status 200, :body "pong"})}]] ["" {:no-doc true} ["/swagger.json" {:get (swagger/create-swagger-handler)}] ["/api-docs/*" {:get (swagger-ui/create-swagger-ui-handler)}]]]))) ``` -------------------------------- ### Test HTTP Endpoints with httpie Source: https://github.com/metosin/reitit/blob/master/examples/http-swagger/README.md Use httpie to test the GET and POST endpoints of the Reitit application. ```bash http GET :3000/math/plus x==1 y==20 ``` ```bash http POST :3000/math/plus x:=1 y:=2 ``` ```bash http GET :3000/async results==1 seed==reitit ``` -------------------------------- ### Async-ring Request with Sieppari Source: https://github.com/metosin/reitit/blob/master/doc/http/sieppari.md Demonstrates how to make an asynchronous Ring request to the Reitit application configured with Sieppari's executor. This example shows how to handle the response using a promise. ```clojure (let [respond (promise)]) (app {:request-method :get, :uri "/api/ping"} respond nil) (deref respond 1000 ::timeout)) ;enter :api ;enter :ping ;enter :get ;leave :get ;leave :ping ;leave :api ;=> {:status 200, :body "pong"} ``` -------------------------------- ### Get Merged Route Tree Source: https://github.com/metosin/reitit/blob/master/doc/basics/router.md View the combined and flattened route tree after composing multiple route definitions. ```clojure (r/routes router) ; [["/admin/ping" {:name :user/ping}] ; ["/admin/db" {:name :user/db}] ; ["/users" {:name :user/users}] ; ["/users/:id" {:name :user/user}]] ``` -------------------------------- ### Default Router Creation with Error Handling Source: https://github.com/metosin/reitit/blob/master/doc/basics/error_messages.md This snippet demonstrates the default router creation process where all exceptions are caught and formatted by `reitit.core/router` using the default exception formatter. ```clojure (require '[reitit.core :as r]) (r/router [["/ping"] ["/:user-id/orders"] ["/bulk/:bulk-id"] ["/public/*path"] ["/:version/status"]]) ``` -------------------------------- ### Serve Static Assets Internally Source: https://github.com/metosin/reitit/blob/master/doc/ring/static.md Use `reitit.ring/create-resource-handler` within a router for non-conflicting paths like `"/assets/*"`. Ensure `reitit.ring` is required. ```clojure (require '[reitit.ring :as ring]) (ring/ring-handler (ring/router [["/ping" (constantly {:status 200, :body "pong"})] ["/assets/*" (ring/create-resource-handler)]]) (ring/create-default-handler)) ``` -------------------------------- ### Wildcards with :colon Syntax Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md Examples of using :colon path parameters that must end with a slash or the end of the path string. ```clojure [["/api/:version" {...}] ["/files/file-:number" {...}] ["/user/:user-id/orders" {...}]] ``` -------------------------------- ### Create Release Commit Source: https://github.com/metosin/reitit/blob/master/doc/development.md Steps to create a release commit, including setting the version and committing changes. ```bash # create a release commit ./scripts/set-version "1.0.0" # !!! update the changelog git add -u git commit -m "Release 1.0.0" # push the commit git push ``` -------------------------------- ### Add Reitit Dependency Source: https://github.com/metosin/reitit/blob/master/doc/README.md Include the Reitit library in your project dependencies. This example shows the latest version for all bundled modules. ```clojure [metosin/reitit "0.10.1"] ``` -------------------------------- ### Override Router Implementation Source: https://github.com/metosin/reitit/blob/master/doc/advanced/different_routers.md Manually set the router implementation using the `:router` option. This example forces the use of the `:linear-router`. ```clojure (require '[reitit.core :as r]) (def router (r/router [["/ping" ::ping] ["/api/:users" ::users]] {:router r/linear-router})) (r/router-name router) ``` -------------------------------- ### Create Router with Default Path Conflict Detection Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_conflicts.md Attempt to create a router with the default settings. This will throw an exception if path conflicts are detected. ```clojure (r/router routes) ``` -------------------------------- ### Invoke a Ring Handler Source: https://github.com/metosin/reitit/blob/master/doc/ring/ring.md Call the generated Ring handler with a request map to get a response. A non-matching request returns nil. ```clojure (app {:request-method :get, :uri "/favicon.ico"}) ; nil (app {:request-method :get, :uri "/ping"}) ; {:status 200, :body "ok"} ``` -------------------------------- ### Handle Exact Path Match with Parameters Source: https://github.com/metosin/reitit/blob/master/doc/basics/path_based_routing.md Shows how `match-by-path` returns a Match record containing route details and extracted path parameters when a path is an exact match. ```clojure (r/match-by-path router "/api/user/1") ; #Match{:template "/api/user/:id" ; :data {:name :user/user} ; :path "/api/user/1" ; :result nil ; :path-params {:id "1"}} ``` -------------------------------- ### Execute Full Middleware Execution Order Example Source: https://github.com/metosin/reitit/blob/master/doc/ring/ring.md Tests the full middleware execution order by making a request to a deeply nested route. Verifies that all middleware, from top-level to route-specific, are applied in the correct sequence. ```clojure (app {:request-method :get, :uri "/api/get"}) ``` ```clojure ; {:status 200, :body [:1-top :2-top-level-route-data :3-parent :4-route :handler]} ``` -------------------------------- ### Test reitit-ring endpoints with httpie Source: https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/README.md Use httpie to test GET and POST requests to the /math/plus endpoint and to retrieve the swagger.json specification. ```bash http GET :3000/math/plus x==1 y==20 http POST :3000/math/plus x:=1 y:=20 http GET :3000/swagger.json ``` -------------------------------- ### Retrieving Expanded Routes Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_data.md Use `r/routes` to get the fully expanded route data from a router. This is useful for introspection or generating documentation. ```clojure (r/routes router) ; [["/ping" {:name ::ping}] ; ["/pong" {:handler identity}] ; ["/users" {:get {:roles #{:admin} ; :handler identity}}]] ``` -------------------------------- ### Define and Use Middleware in Registry Source: https://github.com/metosin/reitit/blob/master/doc/ring/middleware_registry.md Demonstrates how to define a custom middleware and use it via keywords in the router's options and route definitions. The registry is stored under ::middleware/registry. ```clojure (require '[reitit.ring :as ring]) (require '[reitit.middleware :as middleware]) (defn wrap-bonus [handler value] (fn [request] (handler (update request :bonus (fnil + 0) value)))) (def app (ring/ring-handler (ring/router ["/api" {:middleware [[:bonus 20]]} ["/bonus" {:middleware [:bonus10] :get (fn [{:keys [bonus]}] {:status 200, :body {:bonus bonus}})}]] {::middleware/registry {:bonus wrap-bonus :bonus10 [:bonus 10]}}))) ``` -------------------------------- ### Route with Catch-All Parameter (*path syntax) Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md Define a route that captures the rest of the path using the *path syntax. ```clojure ["/public/*path" {:handler get-file}] ``` -------------------------------- ### Define Route with Controller Source: https://github.com/metosin/reitit/blob/master/doc/frontend/controllers.md Add controllers to a route's data using the `:controllers` vector. Specify parameters, start, and stop functions. ```clojure ["/item/:id" {:controllers [{:parameters {:path [:id]} :start (fn [parameters] (js/console.log :start (-> parameters :path :id))) :stop (fn [parameters] (js/console.log :stop (-> parameters :path :id)))}]}] ``` -------------------------------- ### Route with Catch-All Parameter ({*path} syntax) Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md Define a route that captures the rest of the path using the {*path} syntax. ```clojure ["/public/{*path}" {:handler get-file}] ``` -------------------------------- ### Annotate Schema Parameters with Schema-Tools Source: https://github.com/metosin/reitit/blob/master/doc/ring/openapi.md Use schema-tools to annotate parameters with descriptions, deprecation status, examples, and default values for OpenAPI generation. ```clojure ["/plus" {:post {:parameters {:body {:x (schema-tools.core/schema s/Num {:description "Description for X parameter" :openapi/deprecated true :openapi/example 13 :openapi/default 42}) :y int?}}}}] ``` -------------------------------- ### Handle Missing Middleware in Registry Source: https://github.com/metosin/reitit/blob/master/doc/ring/middleware_registry.md Illustrates how router creation fails with a descriptive error if a middleware keyword used in the routes is not found in the provided registry. ```clojure (def app (ring/ring-handler (ring/router ["/api" {:middleware [[:bonus 20]]} ["/bonus" {:middleware [:bonus10] :get (fn [{:keys [bonus]}] {:status 200, :body {:bonus bonus}})}]] {::middleware/registry {:bonus wrap-bonus}}))) ;CompilerException clojure.lang.ExceptionInfo: Middleware :bonus10 not found in registry. ; ;Available middleware in registry: ; ;| :id | :description | ;|--------+--------------------------------------| ;| :bonus | reitit.ring_test$wrap_bonus@59fddabb | ``` -------------------------------- ### Serve Swagger UI from Reitit App Source: https://github.com/metosin/reitit/blob/master/doc/ring/swagger.md This code demonstrates how to invoke the Reitit application to serve the Swagger UI interface. ```clojure (app {:request-method :get, :uri "/api-docs/index.html"}) ``` -------------------------------- ### Customizing Route Expansion with Protocol Extension Source: https://github.com/metosin/reitit/blob/master/doc/basics/route_data.md Extend the `r/Expand` protocol to customize how route arguments are expanded. This example adds support for `java.io.File`. ```clojure (extend-type java.io.File r/Expand (expand [file options] (r/expand #(slurp file) options))) (r/router ["/" (java.io.File. "index.html")]) ```