• Stars
    star
    106
  • Rank 325,871 (Top 7 %)
  • Language
    Clojure
  • License
    MIT License
  • Created almost 9 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Easily build APIs in Pedestal using Schema and Swagger

pedestal-api

A batteries-included API for Pedestal using Swagger.

pedestal-api is a library for building APIs on the pedestal web server. It implements the parts of HTTP that are useful for APIs and allows you to document your handlers and middleware using idiomatic Clojure and generate a compliant Swagger specification.

Clojars Project

The example code can be seen running on Heroku at https://pedestal-api.herokuapp.com

Features

  • A Swagger API with input and output validation and coercion as provided by route-swagger.
  • Content deserialisation, including:
    • application/json
    • application/edn
    • application/transit+json
    • application/transit+msgpack
    • application/x-www-form-urlencoded
  • Content negotiation, including:
    • application/json
    • application/edn
    • application/transit+json
    • application/transit+msgpack
  • Human-friendly error messages when schema validation fails
    • e.g. {:error {:body-params {:age "(not (integer? abc))"}}}
  • Convenience functions for annotating routes and interceptors

Flexibility

pedestal-api is built on top of route-swagger which can still be used directly if more flexibility is needed. Interceptors are provided but not wired in, allowing you to choose those which suit you best.

Example

The example code (reproduced below) can be seen running on Heroku at https://pedestal-api.herokuapp.com

(ns pedestal-api-example.service
  (:require [io.pedestal.http :as bootstrap]
            [io.pedestal.interceptor.chain :refer [terminate]]
            [io.pedestal.interceptor :refer [interceptor]]
            [pedestal-api
             [core :as api]
             [helpers :refer [before defbefore defhandler handler]]]
            [schema.core :as s])
  (:import java.util.UUID))

(defonce the-pets (atom {}))

(s/defschema Pet
  {:name s/Str
   :type s/Str
   :age s/Int})

(s/defschema PetWithId
  (assoc Pet (s/optional-key :id) s/Uuid))

(def all-pets
  "Example of annotating a generic interceptor"
  (api/annotate
   {:summary     "Get all pets in the store"
    :parameters  {:query-params {(s/optional-key :sort) (s/enum :asc :desc)}}
    :responses   {200 {:body {:pets [PetWithId]}}}
    :operationId :all-pets}
   (interceptor
    {:name  ::all-pets
     :enter (fn [ctx]
              (assoc ctx :response
                     {:status 200
                      :body {:pets (let [sort (get-in ctx [:request :query-params :sort])]
                                     (cond->> (vals @the-pets)
                                       sort (sort-by :name)
                                       (= :desc sort) reverse))}}))})))

(def create-pet
  "Example of using the handler helper"
  (handler
   ::create-pet
   {:summary     "Create a pet"
    :parameters  {:body-params Pet}
    :responses   {201 {:body {:id s/Uuid}}}
    :operationId :create-pet}
   (fn [request]
     (let [id (UUID/randomUUID)]
       (swap! the-pets assoc id (assoc (:body-params request) :id id))
       {:status 201
        :body {:id id}}))))

;; Example using the defbefore helper
(defbefore load-pet
  {:summary    "Load a pet by id"
   :parameters {:path-params {:id s/Uuid}}
   :responses  {404 {:body s/Str}}}
  [{:keys [request] :as context}]
  (if-let [pet (get @the-pets (get-in request [:path-params :id]))]
    (update context :request assoc :pet pet)
    (-> context terminate (assoc :response {:status 404
                                            :body "No pet found with this id"}))))

;; Example of using the defhandler helper
(defhandler get-pet
  {:summary     "Get a pet by id"
   :parameters  {:path-params {:id s/Uuid}}
   :responses   {200 {:body PetWithId}
                 404 {:body s/Str}}
   :operationId :get-pet}
  [{:keys [pet] :as request}]
  {:status 200
   :body pet})

(def update-pet
  "Example of using the before helper"
  (before
   ::update-pet
   {:summary     "Update a pet"
    :parameters  {:path-params {:id s/Uuid}
                  :body-params Pet}
    :responses   {200 {:body s/Str}}
    :operationId :update-pet}
   (fn [{:keys [request]}]
     (swap! the-pets update (get-in request [:path-params :id]) merge (:body-params request))
     {:status 200
      :body "Pet updated"})))

(def delete-pet
  "Example of annotating a generic interceptor"
  (api/annotate
   {:summary     "Delete a pet by id"
    :parameters  {:path-params {:id s/Uuid}}
    :responses   {200 {:body s/Str}}
    :operationId :delete-pet}
   (interceptor
    {:name  ::delete-pet
     :enter (fn [ctx]
              (let [pet (get-in ctx [:request :pet])]
                (swap! the-pets dissoc (:id pet))
                (assoc ctx :response
                       {:status 200
                        :body (str "Deleted " (:name pet))})))})))

(s/with-fn-validation
  (api/defroutes routes
    {:info {:title       "Swagger Sample App built using pedestal-api"
            :description "Find out more at https://github.com/oliyh/pedestal-api"
            :version     "2.0"}
     :tags [{:name         "pets"
             :description  "Everything about your Pets"
             :externalDocs {:description "Find out more"
                            :url         "http://swagger.io"}}
            {:name        "orders"
             :description "Operations about orders"}]}
    [[["/" ^:interceptors [api/error-responses
                           (api/negotiate-response)
                           (api/body-params)
                           api/common-body
                           (api/coerce-request)
                           (api/validate-response)]
       ["/pets" ^:interceptors [(api/doc {:tags ["pets"]})]
        ["/" {:get all-pets
              :post create-pet}]
        ["/:id" ^:interceptors [load-pet]
         {:get get-pet
          :put update-pet
          :delete delete-pet}]]

       ["/swagger.json" {:get api/swagger-json}]
       ["/*resource" {:get api/swagger-ui}]]]]))

(def service
  {:env                      :dev
   ::bootstrap/routes        #(deref #'routes)
   ;; linear-search, and declaring the swagger-ui handler last in the routes,
   ;; is important to avoid the splat param for the UI matching API routes
   ::bootstrap/router        :linear-search
   ::bootstrap/resource-path "/public"
   ::bootstrap/type          :jetty
   ::bootstrap/port          8080
   ::bootstrap/join?         false})

Build

Circle CI

More Repositories

1

martian

The HTTP abstraction library for Clojure/script, supporting OpenAPI, Swagger, Schema, re-frame and more
Clojure
470
star
2

re-graph

A graphql client for clojurescript and clojure
Clojure
447
star
3

superlifter

A DataLoader for Clojure/script
Clojure
158
star
4

re-learn

A library for integrating tutorials into your re-frame/reagent application
Clojure
138
star
5

kamera

UI testing via image comparison and devcards
Clojure
86
star
6

lacinia-gen

Generators for GraphQL
Clojure
69
star
7

re-jump.el

emacs navigation for re-frame projects
Emacs Lisp
69
star
8

locksmith

Want to use GraphQL with Clojure/script but don't want keBab or snake_keys everywhere? Use locksmith to change all the keys!
Clojure
61
star
9

slacky

Memes as a Slack Service
Clojure
33
star
10

angel-interceptor

Express relations between Pedestal interceptors and decouple scope from execution order
Clojure
28
star
11

doo-chrome-devprotocol

A runner for doo which runs tests in Chrome, using the Chrome Dev Protocol with no need for karma or npm.
Clojure
27
star
12

carmine-streams

Utility functions for working with Redis streams in carmine
Clojure
26
star
13

fixa

Better test fixtures for clojure
Clojure
26
star
14

oxbow

A Server Sent Events (SSE) client for Clojurescript based on js/fetch
Clojure
24
star
15

spa-skeleton

A skeleton project for a ClojureScript Single Page Application backed by a Swagger API
Clojure
21
star
16

re-partee

How I build Clojurescript apps
Clojure
15
star
17

carve.el

Emacs plugin for borkdude/carve
Emacs Lisp
9
star
18

alrightee

Tee for re-frame
Clojure
7
star
19

learning-clojure

Learning materials for Clojure
Clojure
5
star
20

tinybeans-archive

Create an archive of a tinybeans journal
Clojure
4
star
21

cljockwork

A REST API for cron4j, written in Clojure
Clojure
4
star
22

stardev-feedback

Capturing feedback for https://stardev.io
3
star
23

haproxy-cert-jwt

A Lua extension for HAProxy to turn an SSL client certificate into a JWT for the backend
Lua
2
star
24

one-route

A Ring webserver with one route
HTML
2
star
25

slacky-bot

All the memes for Slack
Clojure
2
star
26

cljs-webapp-from-scratch

Clojure
2
star
27

ingred

Search recipes by ingredient - a REST api written in Clojure with data scraped from the BBC
Clojure
2
star
28

sunshine

Clojure
2
star
29

fast-feedback

A presentation giving guidance on how to optimise your feedback loop and improve efficiency
HTML
1
star
30

a-taste-of-clojure

A talk to introduce (Java) developers to Clojure
JavaScript
1
star
31

sanakone

Learn Finnish
Clojure
1
star
32

masvn

Subversion integration for emacs based on dsvn and inspired by magit
Emacs Lisp
1
star