Prologue: A powerful web framework written in Nim
Tags: nim, webframework
Published: 2020-10-21 11:48:33

[![Discord](https://img.shields.io/discord/718010516034945045?label=Discord&logo=discord&logoColor=white)](https://discord.gg/e2dB4WT)

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

[Prologue](https://github.com/planety/prologue) is a powerful and extensible web framework written in Nim language.

## Installation

Nim Version >= 1.4.0 [Installation](https://nim-lang.org/install.html)

`Prologue` Version >= 0.4.0 

```
nimble install prologue@0.4.0
```

**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.

```nim
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()
```

### 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.

```nim
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()
```

### Grouping Support

`Prologue` now supports grouping routing.

```nim
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()
```

`Prologue` also supports URL pattern grouping.

```nim
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()
```

## Extensible Components

### Websocket support

Echo server example:

```nim
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()
```

### static file middleware

static file serving is a middleware now.

```nim
import prologue
import prologue/middlewares/staticfile

var app = newApp()
app.use(staticFileMiddleware("templates"))
app.run()
```

### User-defined Context

#### Extend Context

Users can define their own Context object.

```nim
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()
```

#### Middlewares

Users can define middlewares for their own Context object.

```nim
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()
```

## Resources

### Additional examples repository

Thanks to @keshon

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

### Documentation

<table class="tg">
<tbody>
  <tr>
    <td class="tg-0pky">Documentation</td>
    <td class="tg-c3ow" text-align="center" colspan="2"><a href="https://planety.github.io/prologue" target="_blank" rel="noopener noreferrer">Index Page</a></td>
  </tr>
  <tr>
    <td class="tg-c3ow">Core API</td>
    <td class="tg-0pky"><a href="https://planety.github.io/prologue/coreapi/theindex.html" target="_blank" rel="noopener noreferrer">Index Page</a></td>
    <td class="tg-0pky"><a href="https://planety.github.io/prologue/coreapi/application.html" target="_blank" rel="noopener noreferrer">Search Page</a></td>
  </tr>
  <tr>
    <td class="tg-c3ow">Full API</td>
    <td class="tg-0pky"><a href="https://planety.github.io/prologue/plugin/theindex.html" target="_blank" rel="noopener noreferrer">Index Page</a></td>
    <td class="tg-0pky"><a href="https://planety.github.io/prologue/plugin/index.html" target="_blank" rel="noopener noreferrer">Search Page</a></td>
  </tr>
</tbody>
</table>

### 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](https://discord.com/invite/e2dB4WT).