# jester **Repository Path**: zbzpo/jester ## Basic Information - **Project Name**: jester - **Description**: Used by nim - **Primary Language**: ε…Άδ»– - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-08-16 - **Last Updated**: 2025-09-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # πŸƒ Jester πŸƒ The sinatra-like web framework for Nim. Jester provides a DSL for quickly creating web applications in Nim. ```nim # example.nim import htmlgen import jester routes: get "/": resp h1("Hello world") ``` Compile and run with: ``` cd tests/example nim c -r example.nim ``` View at: [localhost:5000](http://localhost:5000) Before deploying to production ensure you run your application behind a reverse proxy. This library is not yet hardened against HTTP security exploits so applications written in it should not be exposed to the public internet. ## Routes ```nim routes: get "/": # do something here. ``` All routes must be inside a ``routes`` block. Routes will be executed in the order that they are declared. So be careful when halting. The route path may contain a special pattern or just a static string. Special patterns are almost identical to Sinatra's, the only real difference is the use of ``@`` instead of the ``:``. ```nim get "/hello/@name": # This matches "/hello/fred" and "/hello/bob". # In the route ``@"name"`` will be either "fred" or "bob". # This can of course match any value which does not contain '/'. resp "Hello " & @"name" ``` The patterns in Jester are currently a bit more limited, there is no wildcard patterns. You can use the '?' character to signify optional path parts. ```nim get "/hello/@name?": # This will match what the previous code example matches but will also match # "/hello/". if @"name" == "": resp "No name received :(" else: resp "Hello " & @"name" ``` In this case you might want to make the leading '/' optional too, you can do this by changing the pattern to "/hello/?@name?". This is useful because Jester will not match "/hello" if the leading '/' is not made optional. ### Regex Regex can also be used as a route pattern. The subpattern captures will be placed in ``request.matches`` when a route is matched. For example: ```nim get re"^\/([0-9]{2})\.html$": resp request.matches[0] ``` This will match URLs of the form ``/15.html``. In this case ``request.matches[0]`` will be ``15``. ## Conditions Jester supports conditions, however they are limited to a simple ``cond`` template. ```nim routes: get "/@name": cond @"name" == "daniel" # ``cond`` will pass execution to the next matching route if @"name" is not # "daniel". resp "Correct, my name is daniel." get "/@name": # This will be the next route that is matched. resp "No, that's not my name." ``` ## Return values Route bodies all have an implicit ``request`` object. This object is documented in jester.nim and documentation can be generated by executing ``nim doc jester.nim``. Returning a response from a route should be done using one of the following functions: * One of the ``resp`` functions. * By setting ``body``, ``headers`` and/or ``status`` and calling ``return``. * ``redirect`` function * ``attachment`` function There might be more. Take a look at the documentation of jester.nim for more info. ## Manual routing It is possible not to use the ``routes`` macro and to do the routing yourself. You can do this by writing your own ``match`` procedure. Take a look at [example2](tests/example2.nim) for an example on how to do this. ## Static files By default Jester looks for static files in ``./public``. This can be overriden using the ``setStaticDir`` function. Files will be served like so: ./public/css/style.css ``->`` http://example.com/css/style.css **Note**: Jester will only serve files, that are readable by ``others``. On Unix/Linux you can ensure this with ``chmod o+r ./public/css/style.css``. ## Cookies Cookies can be set using the ``setCookie`` function. ```nim get "/": # Set a cookie "test:value" and make it expire in 5 days. setCookie("test", @"value", daysForward(5)) ``` They can then be accessed using the ``request.cookies`` procedure which returns a ``Table[string, string]``. ## Request object The request object holds all the information about the current request. You can access it from a route using the ``request`` variable. It is defined as: ```nim Request* = ref object params*: StringTableRef ## Parameters from the pattern, but also the ## query string. matches*: array[MaxSubpatterns, string] ## Matches if this is a regex ## pattern. body*: string ## Body of the request, only for POST. ## You're probably looking for ``formData`` ## instead. headers*: StringTableRef ## Headers received with the request. ## Retrieving these is case insensitive. formData*: MultiData ## Form data; only present for ## multipart/form-data port*: int host*: string appName*: string ## This is set by the user in ``run``, it is ## overriden by the "SCRIPT_NAME" scgi ## parameter. pathInfo*: string ## This is ``.path`` without ``.appName``. secure*: bool path*: string ## Path of request. query*: string ## Query string of request. cookies*: StringTableRef ## Cookies from the browser. ip*: string ## IP address of the requesting client. reqMeth*: HttpMethod ## Request method, eg. HttpGet, HttpPost settings*: Settings ``` ## Examples ### Custom router A custom router allows running your own initialization code and pass dynamic settings to Jester before starting the async loop. ```nim import asyncdispatch, jester, os, strutils router myrouter: get "/": resp "It's alive!" proc main() = let port = paramStr(1).parseInt().Port let settings = newSettings(port=port) var jester = initJester(myrouter, settings=settings) jester.serve() when isMainModule: main() ``` ### Github service hooks The code for this is pretty similar to the code for Sinatra given here: http://help.github.com/post-receive-hooks/ ```nim import jester, json routes: post "/": var push = parseJson(@"payload") resp "I got some JSON: " & $push ```