{"id":13500988,"url":"https://github.com/dom96/jester","last_synced_at":"2025-03-23T04:42:10.223Z","repository":{"id":3148613,"uuid":"4178258","full_name":"dom96/jester","owner":"dom96","description":"A sinatra-like web framework for Nim.","archived":false,"fork":false,"pushed_at":"2024-01-20T07:08:51.000Z","size":452,"stargazers_count":1585,"open_issues_count":66,"forks_count":122,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-01-28T11:21:47.054Z","etag":null,"topics":["framework","hacktoberfest","http","nim","web"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"bundler/bundler","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dom96.png","metadata":{"files":{"readme":"readme.markdown","changelog":"changelog.markdown","contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-04-29T23:18:14.000Z","updated_at":"2025-01-27T20:32:44.000Z","dependencies_parsed_at":"2024-10-14T10:00:40.194Z","dependency_job_id":"356ec098-fb4c-46b8-8015-7205095c8762","html_url":"https://github.com/dom96/jester","commit_stats":{"total_commits":281,"total_committers":49,"mean_commits":5.73469387755102,"dds":0.6476868327402135,"last_synced_commit":"94031268beb848a47344ca28dfede61ec2cf4ebd"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dom96%2Fjester","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dom96%2Fjester/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dom96%2Fjester/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dom96%2Fjester/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dom96","download_url":"https://codeload.github.com/dom96/jester/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245056903,"owners_count":20553854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","hacktoberfest","http","nim","web"],"created_at":"2024-07-31T22:01:21.592Z","updated_at":"2025-03-23T04:42:10.200Z","avatar_url":"https://github.com/dom96.png","language":"Nim","readme":"# 🃏 Jester 🃏\n\nThe sinatra-like web framework for Nim. Jester provides a DSL for quickly\ncreating web applications in Nim.\n\n```nim\n# example.nim\nimport htmlgen\nimport jester\n\nroutes:\n  get \"/\":\n    resp h1(\"Hello world\")\n```\n\nCompile and run with:\n\n```\n  cd tests/example\n  nim c -r example.nim\n```\n\nView at: [localhost:5000](http://localhost:5000)\n\nBefore 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.\n\n## Routes\n\n```nim\nroutes:\n  get \"/\":\n    # do something here.\n```\n\nAll routes must be inside a ``routes`` block.\n\nRoutes will be executed in the order that they are declared. So be careful when\nhalting.\n\nThe route path may contain a special pattern or just a static string. Special\npatterns are almost identical to Sinatra's, the only real difference is the\nuse of ``@`` instead of the ``:``.\n\n```nim\nget \"/hello/@name\":\n  # This matches \"/hello/fred\" and \"/hello/bob\".\n  # In the route ``@\"name\"`` will be either \"fred\" or \"bob\".\n  # This can of course match any value which does not contain '/'.\n  resp \"Hello \" \u0026 @\"name\"\n```\n\nThe patterns in Jester are currently a bit more limited, there is no\nwildcard patterns.\n\nYou can use the '?' character to signify optional path parts.\n\n```nim\nget \"/hello/@name?\":\n  # This will match what the previous code example matches but will also match\n  # \"/hello/\".\n  if @\"name\" == \"\":\n    resp \"No name received :(\"\n  else:\n    resp \"Hello \" \u0026 @\"name\"\n```\n\nIn this case you might want to make the leading '/' optional too, you can do this\nby changing the pattern to \"/hello/?@name?\". This is useful because Jester will\nnot match \"/hello\" if the leading '/' is not made optional.\n\n### Regex\n\nRegex can also be used as a route pattern. The subpattern captures will be\nplaced in ``request.matches`` when a route is matched. For example:\n\n```nim\nget re\"^\\/([0-9]{2})\\.html$\":\n  resp request.matches[0]\n```\n\nThis will match URLs of the form ``/15.html``. In this case\n``request.matches[0]`` will be ``15``.\n\n## Conditions\n\nJester supports conditions, however they are limited to a simple ``cond`` template.\n\n```nim\nroutes:\n  get \"/@name\":\n    cond @\"name\" == \"daniel\"\n    # ``cond`` will pass execution to the next matching route if @\"name\" is not\n    # \"daniel\".\n    resp \"Correct, my name is daniel.\"\n\n  get \"/@name\":\n    # This will be the next route that is matched.\n    resp \"No, that's not my name.\"\n```\n\n## Return values\n\nRoute bodies all have an implicit ``request`` object. This object is documented\nin jester.nim and documentation can be generated by executing ``nim doc jester.nim``.\n\nReturning a response from a route should be done using one of the following\nfunctions:\n\n  * One of the ``resp`` functions.\n  * By setting ``body``, ``headers`` and/or ``status`` and calling ``return``.\n  * ``redirect`` function\n  * ``attachment`` function\n\nThere might be more. Take a look at the documentation of jester.nim for more info.\n\n## Manual routing\n\nIt is possible not to use the ``routes`` macro and to do the routing yourself.\n\nYou can do this by writing your own ``match`` procedure. Take a look at\n[example2](tests/example2.nim) for an example on how to do this.\n\n## Static files\n\nBy default Jester looks for static files in ``./public``. This can be overriden\nusing the ``setStaticDir`` function. Files will be served like so:\n\n./public/css/style.css ``-\u003e`` http://example.com/css/style.css\n\n**Note**: Jester will only serve files, that are readable by ``others``. On\nUnix/Linux you can ensure this with ``chmod o+r ./public/css/style.css``.\n\n## Cookies\n\nCookies can be set using the ``setCookie`` function.\n\n```nim\nget \"/\":\n  # Set a cookie \"test:value\" and make it expire in 5 days.\n  setCookie(\"test\", @\"value\", daysForward(5))\n```\n\nThey can then be accessed using the ``request.cookies`` procedure which returns\na ``Table[string, string]``.\n\n## Request object\n\nThe request object holds all the information about the current request.\nYou can access it from a route using the ``request`` variable. It is defined as:\n\n```nim\nRequest* = ref object\n  params*: StringTableRef       ## Parameters from the pattern, but also the\n                                ## query string.\n  matches*: array[MaxSubpatterns, string] ## Matches if this is a regex\n                                          ## pattern.\n  body*: string                 ## Body of the request, only for POST.\n                                ## You're probably looking for ``formData``\n                                ## instead.\n  headers*: StringTableRef      ## Headers received with the request.\n                                ## Retrieving these is case insensitive.\n  formData*: MultiData          ## Form data; only present for\n                                ## multipart/form-data\n  port*: int\n  host*: string\n  appName*: string              ## This is set by the user in ``run``, it is\n                                ## overriden by the \"SCRIPT_NAME\" scgi\n                                ## parameter.\n  pathInfo*: string             ## This is ``.path`` without ``.appName``.\n  secure*: bool\n  path*: string                 ## Path of request.\n  query*: string                ## Query string of request.\n  cookies*: StringTableRef      ## Cookies from the browser.\n  ip*: string                   ## IP address of the requesting client.\n  reqMeth*: HttpMethod          ## Request method, eg. HttpGet, HttpPost\n  settings*: Settings\n```\n\n## Examples\n\n### Custom router\n\nA custom router allows running your own initialization code and pass dynamic settings to Jester before starting the async loop.\n\n```nim\nimport asyncdispatch, jester, os, strutils\n\nrouter myrouter:\n  get \"/\":\n    resp \"It's alive!\"\n\nproc main() =\n  let port = paramStr(1).parseInt().Port\n  let settings = newSettings(port=port)\n  var jester = initJester(myrouter, settings=settings)\n  jester.serve()\n\nwhen isMainModule:\n  main()\n```\n\n### Github service hooks\n\nThe code for this is pretty similar to the code for Sinatra given here: http://help.github.com/post-receive-hooks/\n\n```nim\nimport jester, json\n\nroutes:\n  post \"/\":\n    var push = parseJson(@\"payload\")\n    resp \"I got some JSON: \" \u0026 $push\n```\n","funding_links":[],"categories":["Nim","Uncategorized","Web"],"sub_categories":["Uncategorized","Web Frameworks","Frameworks"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdom96%2Fjester","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdom96%2Fjester","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdom96%2Fjester/lists"}