DEV Community

ringabout
ringabout

Posted on

Prologue: A powerful web framework written in Nim

Discord

We are proud to announce that Prologue v0.4.0 has been released!

Prologue is a powerful and extensible web framework written in Nim language.

Installation

Nim Version >= 1.4.0 Installation

Prologue Version >= 0.4.0

nimble install prologue@0.4.0
Enter fullscreen mode Exit fullscreen mode

Let's see the new features in this release!

Powerful Routing system

Prologue has switched to nest routing system.

Wildcard Support

Prologue now supports wildcard(*) character which can match one URL section.

import prologue


proc hello*(ctx: Context) {.async.} =
  resp "Hello, Prologue"

var app = newApp()
app.get("/static/*", hello)
app.get("/*/static", hello)
app.get("/static/templates/{path}/*", hello)
app.run()
Enter fullscreen mode Exit fullscreen mode

Greedy Support

Prologue now supports greedy($) character which must be at the end of the URL. Greedy character will match all the remain URL sections if the former sections are matched.

import prologue


proc hello*(ctx: Context) {.async.} =
  resp "Hello, Prologue"

var app = newApp()
app.get("/test/{param}$", hello)
app.get("/test/static/*$", hello)
app.run()
Enter fullscreen mode Exit fullscreen mode

Grouping Support

Prologue now supports grouping routing.

import prologue
import std/with


var 
  app = newApp()
  base = newGroup(app, "/apiv2", @[])
  level1 = newGroup(app,"/level1", @[], base)
  level2 = newGroup(app, "/level2", @[], level1)
  level3 = newGroup(app, "/level3", @[], level2)


proc hello(ctx: Context) {.async.} =
  resp "Hello"

proc hi(ctx: Context) {.async.} =
  resp "Hi"

proc home(ctx: Context) {.async.} =
  resp "Home"


with base:
  get("/hello", hello)
  get("/hi", hi)
  post("/home", home)

# /apiv2/level1/hello
with level1:
  get("/hello", hello)
  get("/hi", hi)
  post("/home", home)

# /apiv2/level1/level2/hello
with level2:
  get("/hello", hello)
  get("/hi", hi)
  post("/home", home)

# /apiv2/level1/level2/level3/hello
with level3:
  get("/hello", hello)
  get("/hi", hi)
  post("/home", home)

app.run()
Enter fullscreen mode Exit fullscreen mode

Prologue also supports URL pattern grouping.

import prologue


var
  app = newApp()
  base = newGroup(app, "/apiv2", @[])
  level1 = newGroup(app,"/level1", @[], base)
  level2 = newGroup(app, "/level2", @[], level1)


proc hello(ctx: Context) {.async.} =
  resp "Hello"

proc hi(ctx: Context) {.async.} =
  resp "Hi"

proc home(ctx: Context) {.async.} =
  resp "Home"


let
  urlpattern1 = @[pattern("/hello", hello), pattern("/hi", hi)]
  urlpattern2 = @[pattern("/home", home)]
  tab = {level1: urlpattern1, level2: urlpattern2}

app.addGroup(tab)
app.run()
Enter fullscreen mode Exit fullscreen mode

Extensible Components

Websocket support

Echo server example:

import prologue
import prologue/websocket


proc hello*(ctx: Context) {.async.} =
  var ws = await newWebSocket(ctx)
  await ws.send("Welcome to simple echo server")
  while ws.readyState == Open:
    let packet = await ws.receiveStrPacket()
    await ws.send(packet)

  resp "<h1>Hello, Prologue!</h1>"

var app = newApp()
app.get("/ws", hello)
app.run()
Enter fullscreen mode Exit fullscreen mode

static file middleware

static file serving is a middleware now.

import prologue
import prologue/middlewares/staticfile

var app = newApp()
app.use(staticFileMiddleware("templates"))
app.run()
Enter fullscreen mode Exit fullscreen mode

User-defined Context

Extend Context

Users can define their own Context object.

import prologue
import strformat


type
  UserContext = ref object of Context
    data: int


proc hello*(ctx: Context) {.async.} =
  inc ctx.UserContext.data
  echo fmt"{ctx.UserContext.data = }"
  resp "<h1>Hello, Prologue!</h1>"

var app = newApp()
app.use(extendContextMiddleWare(UserContext))
app.get("/", hello)
app.run()
Enter fullscreen mode Exit fullscreen mode

Middlewares

Users can define middlewares for their own Context object.

import prologue
import strformat


type
  UserContext = ref object of Context
    data: int

  ExperimentContext = concept ctx
    ctx is Context
    ctx.data is int


proc experimentMiddleware[T: ExperimentContext](ctxType: typedesc[T]): HandlerAsync =
  mixin await
  result = proc(ctx: Context) {.async.} =
    inc ctx.ctxType.data
    echo fmt"{ctx.ctxType.data = }"
    await switch(ctx)

proc hello*(ctx: Context) {.async.} =
  inc ctx.UserContext.data
  echo fmt"{ctx.UserContext.data = }"
  resp "<h1>Hello, Prologue!</h1>"

var app = newApp()
app.use(extendContextMiddleWare(UserContext))
app.use(experimentMiddleware(UserContext))
app.get("/", hello)
app.run()
Enter fullscreen mode Exit fullscreen mode

Resources

Additional examples repository

Thanks to @keshon

https://github.com/planety/prologue-examples

Documentation

Documentation Index Page
Core API Index Page Search Page
Full API Index Page Search Page

Supports

awesome-nim: https://github.com/xflywind/awesome-nim

awesome-prologue: https://github.com/planety/awesome-prologue

Discord Server

If you are interested in Prologue or want to learn more about Prologue, welcome to join our discord server.

Top comments (0)