Code Monkey home page Code Monkey logo

okapi's Introduction

🦓🦒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..."

okapi's People

Contributors

georgefst avatar rashadg1030 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

okapi's Issues

Implement CSRF protection

Now that we have sessions, it should be possible to add CSRF protection. Maybe introduce constraint HasCSRFProtection m.

Small typo in the introduction

The introduction has the following example:

greet = do
  seg "greet"
  name <- pathParam
  returnPlainText [] $ "Hello " <> name <> "! I'm Okapi."

And say that this will set up a server that listens to http://localhost:3000/greeting/Bob.

I think that either should seg "greet" be changed to seg "greeting" or the URL should be changed to http://localhost:3000/greet/Bob.

OpenAPI documentation generation

This might not be possible without giving up the simplicity of Okapi, but maybe it is? Using WriterT or something similar would it be possible to generate OpenAPI docs? I've looked at final-tagless style and wondering if we can apply this to Okapi. We can "interpret" Okapi, both for parsing HTTP requests, and generating API docs. I'm just blabbering here. Any ideas?

Paper on tagless final/final tagless:
https://okmij.org/ftp/tagless-final/course/lecture.pdf

Add logging

To take Okapi to the next level, there should be logging. Should it be built in? Or left to the user? They are both possible. To add built-in logging, OkapiT will need WriterT. Or some other alternative because WriterT performance isn't great. The user could also just add WriterT in their custom monad stack. Should logging be built in or left to the user?

Is it too soon to use the new API?

I really like the idea of Okapi, as I usually find myself wanting something more type-safe than Scotty, but easier to work with than Servant.

Unfortunately, version 0.1 on Hackage is basically unusable because of the way things are constantly dumped to stdout. Plus, there's little documentation, and what there is contains broken links and examples which don't compile. But it is a version 0.1, and you've mentioned that it's an early and experimental release, so that's fine!

What I'm curious about is that the wiki describes a very different API, as mentioned in your Reddit post, which looks like a great improvement (the overall idea anyway - I could quibble about minor things like having to import so much qualified). But it's not clear whether it's even possible to use it yet. The examples don't compile with the current main branch, and there are other newer branches floating around, which mostly don't build at all.

So, is there a commit anywhere with which it is possible to use the new API, or do we just need to wait for things to stabilise? I'm using 0.1 for now on a small, low-stakes personal project, so I really don't mind breaking changes or the occasional bug, and I'd be happy to act as a sort of beta tester.

Split `MonadServer m` into `MonadRequest m` and `MonadResponse m`

Should look something like:

-- type MonadServer m = (MonadRequest m, MonadResponse m)

class (MonadRequest m, MonadResponse m) => MonadServer m where
  throw :: ...
  next :: ...

class Monad m => MonadRequest m where
  pathParam :: ...
  queryParam :: ...

class Monad m => MonadResponse m where
  write :: forall a. Writeable a => a -> m ()

to make Okapi functions more descriptive and to constrain their abilities accordingly. Inspired by "IO Monad Considered Harmful". Don't know if this would work.

Add support for multipart form data

I can do this two ways:

  1. Introduce IO into the MonadOkapi stack and parse multipart form data inside a Okapi parser
  2. Parse the multipart form data from the request before it is passed into Okapi. We can avoid introducing IO into the MonadOkapi constraint this way. Have to change Body to sum type representing the different types of form data.

Better errors

Currently, errors provide no information as to what caused the error. Aborts only provide an HTTP error response, which may give a little idea of what caused it. Skip errors provide no information at all. Skip errors should at least provide an idea of what parser caused the error. To do this, the Skip data constructor can take an additional Text argument to say what happened. Abort issues could have the same. Then the issue becomes, how should the Monoid instance work? We want to see all the errors that happened so discarding them is not an option. The parser will have to return a [Error] instead of just Error.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.