A web microframework for Haskell based on monadic parsing
Go to file
2023-11-17 02:42:49 -08:00
docs Add Security field; update docs 2023-04-24 03:50:45 +00:00
lib Add SSE example 2023-11-17 02:42:49 -08:00
newdocs Use heterogenous list instead of Secret keys to pass arguments to handlers 2023-11-06 19:42:42 -08:00
old Not good 2 2023-09-18 22:08:50 -07:00
.gitignore Git ignore Cabal build products 2023-11-16 22:39:53 +00:00
cabal.project Add a cabal.project file 2023-11-16 22:39:27 +00:00
README.md Fix README 2023-11-14 08:19:59 -08:00

🦓🦒Okapi

Okapi is a data-driven micro framework for implementing HTTP servers.

  • Ergonomic DSLs for routing and parsing requests
  • Integrate Okapi with ANY monad stack or effect system
  • Automatically generate clients and OpenAPI specifications (coming soon)
  • Programatically generate your API's structure

Hello World Example

helloWorld =
  responder @200 @'[] @Text.Text @Text.Text
    . method HTTP.GET id
    $ \greet _req -> return $ greet noHeaders "Hello World!"

main =
  Warp.run 8000
    . withDefault helloWorld
    $ \_ resp -> resp $ Wai.responseLBS HTTP.status404 [] "Not Found..."

Calculator Example

data Operator
    = Add
    | Sub
    | Mul
    | Div
    | Sq
    | Neg
    deriving (Show)

instance Web.FromHttpApiData Operator where
    parseUrlPiece "add" = Right Add
    parseUrlPiece "sub" = Right Sub
    parseUrlPiece "minus" = Right Sub
    parseUrlPiece "mul" = Right Mul
    parseUrlPiece "div" = Right Div
    parseUrlPiece "neg" = Right Neg
    parseUrlPiece "sq" = Right Sq
    parseUrlPiece "square" = Right Sq
    parseUrlPiece _ = Left "Can't parse operator..."

shared =
  lit "calc"
    . param @Operator
    . param @Int

unary =
  responder @200 @'[] @Text.Text @Int
    . responder @500 @'[] @Text.Text @Text.Text
    . method HTTP.GET id

unaryHandler operator x ok wrongArgs _req =
  return $ case operator of
    Sq  -> ok noHeaders (x * x)
    Neg -> ok noHeaders (x * (-1))
    _   -> wrongArgs noHeaders $ Text.pack (show operator) <> " needs two arguments."

binary =
  param @Int
    . responder @200 @'[] @Text.Text @Int
    . responder @500 @'[] @Text.Text @Text.Text
    . responder @403 @'[] @Text.Text @Text.Text
    . method HTTP.GET id

binaryHandler operator x y ok wrongArgs divByZeroErr _req =
  return $ case operator of
    Add -> ok noHeaders (x + y)
    Sub -> ok noHeaders (x - y)
    Mul -> ok noHeaders (x * y)
    Div ->
      if y == 0
      then divByZeroErr noHeaders "You can't divide by 0."
      else ok noHeaders (div x y)
    _ -> wrongArgs noHeaders $ Text.pack (show operator) <> " needs one argument."

calc = shared $ choice
  [ unary unaryHandler
  , binary binaryHandler
  ]

main =
  Warp.run 8003
    . withDefault calc
    $ \_ resp -> resp $ Wai.responseLBS HTTP.status404 [] "Not Found..."