All Projects → xmonader → nim-servy

xmonader / nim-servy

Licence: other
Servy is a fast, simple and lightweight micro web-framework for Nim

Programming Languages

nim
578 projects
shell
77523 projects

Projects that are alternatives of or similar to nim-servy

Masonite
The Modern And Developer Centric Python Web Framework. Be sure to read the documentation and join the Slack channel questions: http://slack.masoniteproject.com
Stars: ✭ 1,681 (+5503.33%)
Mutual labels:  webframework
Webcontext
webcontext is a web framework and web application server based on node.js
Stars: ✭ 151 (+403.33%)
Mutual labels:  webframework
Frappejs
Node + Electron + Vue based metadata web framework (inspired by Frappe)
Stars: ✭ 214 (+613.33%)
Mutual labels:  webframework
App Turbo
A framework based on tornado for easier development, scaling up and maintenance
Stars: ✭ 131 (+336.67%)
Mutual labels:  webframework
Pando.py
Filesystem dispatch + Simplates + Python = a nice web framework.
Stars: ✭ 146 (+386.67%)
Mutual labels:  webframework
Webgo
A minimal framework to build web apps; with handler chaining, middleware support; and most of all standard library compliant HTTP handlers(i.e. http.HandlerFunc).
Stars: ✭ 165 (+450%)
Mutual labels:  webframework
Raxx kit
Get started with Raxx + Elixir
Stars: ✭ 120 (+300%)
Mutual labels:  webframework
mango
mango is a powerful and simple golang web framework
Stars: ✭ 20 (-33.33%)
Mutual labels:  webframework
Gotham
A flexible web framework that promotes stability, safety, security and speed.
Stars: ✭ 1,935 (+6350%)
Mutual labels:  webframework
Clog
CLOG - The Common Lisp Omnificent GUI
Stars: ✭ 181 (+503.33%)
Mutual labels:  webframework
Jolla
Python Web Framework
Stars: ✭ 132 (+340%)
Mutual labels:  webframework
Sactive Web
🚀 A dependency injection web framework for Node.js.
Stars: ✭ 143 (+376.67%)
Mutual labels:  webframework
Alpas
🚀 The Rapid and Delightful Kotlin Web Framework. Easy, elegant, and productive!
Stars: ✭ 166 (+453.33%)
Mutual labels:  webframework
Nim websitecreator
Nim fullstack website framework - deploy a website within minutes
Stars: ✭ 124 (+313.33%)
Mutual labels:  webframework
Golf
⛳️ The Golf web framework
Stars: ✭ 248 (+726.67%)
Mutual labels:  webframework
Rapidoid
Rapidoid - Extremely Fast, Simple and Powerful Java Web Framework and HTTP Server!
Stars: ✭ 1,571 (+5136.67%)
Mutual labels:  webframework
Ninja
Ninja is a full stack web framework for Java. Rock solid, fast and super productive.
Stars: ✭ 1,892 (+6206.67%)
Mutual labels:  webframework
webfr
moved to: https://github.com/godzillaframework/godzilla.git
Stars: ✭ 13 (-56.67%)
Mutual labels:  webframework
GCMS
PHP FASTEST CMS with Ajax support
Stars: ✭ 19 (-36.67%)
Mutual labels:  webframework
Closp
Clojure template for web development (with SPA support)
Stars: ✭ 173 (+476.67%)
Mutual labels:  webframework

servy

Servy is a fast, simple and lightweight micro web-framework for Nim

Installation

nimble install servy

quickstart

First create the router

import servy
var router = initRouter()

Define your first handler function

proc handleHello(req: Request, res: Response) : Future[void] {.async.} =
    res.code = Http200
    res.content = "hello world from handler /hello" & $req

Wire the handler to a path

    router.addRoute("/hello", handleHello)

Running Servy

let opts = ServerOptions(address:"127.0.0.1", port:9000.Port, debug:true)
var s = initServy(opts, router)
s.run()

Making first request

using curl or your web browser go to localhost:9000/hello

➜  servy git:(master) ✗ curl localhost:9000/hello
hello world from handler /hello%    

Defining handlers and wiring them

    proc handleGreet(req: Request, res: Response) : Future[void] {.async.} =
      res.code = Http200
      res.content = "generic greet" & $req
      if "username" in req.urlParams:
        echo "username is: " & req.urlParams["username"]
      
      if "first" in req.urlParams:
        echo "first is: " & req.urlParams["first"]

      if "second" in req.urlParams:
        echo "second is: " & req.urlParams["second"]

      if "lang" in req.urlParams:
        echo "lang is: " & req.urlParams["lang"]


    router.addRoute("/greet", handleGreet, HttpGet, @[])
    router.addRoute("/greet/:username", handleGreet, HttpGet, @[])
    router.addRoute("/greet/:first/:second/:lang", handleGreet, HttpGet, @[])


addRoute takes the following params

  • pat route pattern
  • handlerFunc to execute on match
  • httpMethod to only execute on a certain http method
  • middlewares list of middlewares to execute before request (for specific route) the captured route variables are available in req.urlParams table

Handling different HTTP methods

    proc handlePost(req: Request, res: Response) : Future[void] {.async.} =
      #   req.fullInfo
      echo "USERNAME: " & $(req.formData.getValueOrNone("username"))
      res.code = Http200
      res.content = $req



router.addRoute("/post", handlePost, HttpPost)

Here we handle POST on path /post with handler handlePost

  • formData table is available on the request body handling both multipart and x-www-form-urlencoded post formats
  • req.formData.getValueOrNone gives you access to form data.

Abort

proc handleAbort(req: Request, res: Response) : Future[void] {.async.} =
      res.abortWith("sorry mate")

router.addRoute("/abort", handleAbort, HttpGet)

response object has abortWith proc available

Redirect

proc handleRedirect(req: Request, res:  Response): Future[void] {.async.} =
      res.redirectTo("https://python.org")

router.addRoute("/redirect", handleRedirect, HttpGet)

response object has redirectTo proc available, also you can set status code as optional param.

Defining middlewares

Here's an example of a logging middleware that runs before processing any handler

Logging Middleware

proc loggingMiddleware*(request: Request,  response: Response): Future[bool] {.async.} =
  let path = request.path
  let headers = request.headers
  echo "==============================="
  echo "from logger handler"
  echo "path: " & path
  echo "headers: " & $headers
  echo "==============================="
  return true

if you want to terminate the middleware chain return false

Trim slashes Middleware

proc trimTrailingSlash*(request: Request,  response: Response): Future[bool] {.async.} =
  let path = request.path
  if path.endswith("/"):
    request.path = path[0..^2]

  echo "==============================="
  echo "from slash trimmer "
  echo "path was : " & path
  echo "path: " & request.path
  echo "==============================="
  return true

Static files middleware

let serveTmpDir = newStaticMiddleware("/tmp", "/tmppublic")
let serveHomeDir = newStaticMiddleware(getHomeDir(), "/homepublic")

You can serve static assets from a certain directory using static middleware.

newStaticMiddleware takes in a directory to serve and a path onRoute to serve on.

Basic Auth

Here's how it's defined

proc basicAuth*(users: Table[string, string], realm="private", text="Access denied"): proc(request: Request, response: Response): Future[bool] {.async, closure, gcsafe.} =


  result = proc(request: Request, response: Response): Future[bool] {.async, closure, gcsafe.} =

    var processedUsers = initTable[string, string]()
    for u, p in users:
      let encodedAuth = encode(fmt"{u}:{p}")
      processedUsers.add(fmt"Basic {encodedAuth}", u)

    let authHeader = request.headers.getOrDefault("authorization", @[""])[0]

    var found = authHeader in processedUsers
      
    if not found or authHeader.len == 0:
      let realmstring = '"' & realm & '"'
      response.headers.add("WWW-Authenticate", fmt"Basic realm={realmstring}") 
      response.abortWith("Access denied", Http401)
      return false
    else:
      return true

Example of HTTP Basic Auth

    proc handleBasicAuth(req: Request, res: Response) : Future[void] {.async.} =
      res.code = Http200
      res.content = "logged in!!"

    let users = {"ahmed":"password", "xmon":"xmon"}.toTable
    router.addRoute("/basicauth", handleBasicAuth, HttpGet, @[basicAuth(users)])

Websockets

servy is integrated with treeform/ws for websocket support, here is an example

    proc handleWS(req: Request, res: Response) : Future[void] {.async.} =
      var ws = await newServyWebSocket(req)
      await ws.send("Welcome to simple echo server")
      while ws.readyState == Open:
        let packet = await ws.receiveStrPacket()
        await ws.send(packet)

    router.addRoute("/ws", handleWS, HttpGet, @[])

and to test you can use nim client e.g

import ws, asyncdispatch, asynchttpserver

proc main(): Future[void]{.async.} =
    var w = await newWebSocket("ws://127.0.0.1:9000/ws")
    echo await w.receiveStrPacket()
    await w.send("Hi, how are you?")
    echo await w.receiveStrPacket()
    w.close()

waitFor main()

or from javascript

> ws = new WebSocket("ws://127.0.0.1:9000/ws")
> ws.onmessage = (m)=>console.log(m.data)
(m)=>console.log(m.data)
> ws.send("bye")
bye

Running

let opts = ServerOptions(address:"127.0.0.1", port:9000.Port)
var s = initServy(opts, router, @[loggingMiddleware, trimTrailingSlash, serveTmpDir, serveHomeDir])
s.run()

initServy takes in some options for binding address and the router to use for incoming requests in a list of middlewares to execute before handlers.

curl examples

Here are some curl examples to play with servy if you start it with nimble run servy

curl localhost:9000/hello
curl localhost:9000/greet
curl localhost:9000/greet/ahmed
curl localhost:9000/greet/first/sec/en
curl -XPOST localhost:9000/post
curl -X POST -F 'username=auser' -F 'password=apassword' http://localhost:9000/post
curl -X POST -F '[email protected]' http://localhost:9000/post
curl -L localhost:9000/abort
curl -L localhost:9000/redirect
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].