Skip to content
This repository was archived by the owner on Jan 12, 2025. It is now read-only.

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jigordev committed Sep 7, 2024
0 parents commit 7dddf59
Show file tree
Hide file tree
Showing 42 changed files with 2,494 additions and 0 deletions.
202 changes: 202 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Maia - Web Framework for Nim

**Maia** is a simple and powerful asynchronous web framework written in Nim, designed to build fast and scalable web applications. It provides routing, middleware support, filters, hooks, file handling, and more.

## Features

- **Routing**: Define routes and handlers easily.
- **Middlewares**: Apply custom middlewares to routes.
- **Asynchronous**: Built with asynchronous execution in mind.
- **Stream Support**: Send chunked data or Server-Sent Events (SSE).
- **Request Hooks**: Pre- and post-processing hooks for custom behavior.
- **Filters**: Apply request filters like authentication.
- **Static Files**: Serve static files or send specific files for download.
- **Error Handling**: Custom error handlers to manage exceptions.
- **Multiple Routers**: Support for extending routers.

## Getting Started

### Installation

1. Install the Nim language from [nim-lang.org](https://nim-lang.org).
2. Install Maia by cloning the repository or using a package manager (if available).

### Hello World Example

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Hello World!")
let router = newRouter()
.register(get("/", handler))
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

This simple example sets up a web server on port 5000 that returns "Hello World!" for requests to the root route (`/`).

### Redirect Example

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.redirect("/endpoint")
proc handler2(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Redirected!")
let router = newRouter()
.register(get("/", handler))
.register(get("/endpoint", handler2))
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

This example redirects requests from `/` to `/endpoint`.

### Handling Query Parameters

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
let name = req.query.getString("name")
await res.send("Welcome " & name)
let router = newRouter()
.register(get("/", handler))
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

Extract query parameters from the URL and respond accordingly.

### Handling POST Requests

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
let form = req.form.get()
let name = form.getString("name")
await res.send("Welcome " & name)
let router = newRouter()
.register(post("/form", handler))
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

Handle `POST` requests, process form data, and send a response.

### Middleware Example

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Hello World!")
proc myMiddleware(req: Request, res: Response, next: Handler): Future[Response] {.async.} =
echo "Middleware executed!"
await next(req, res)
let router = newRouter()
.register(get("/", handler).apply(myMiddleware))
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

Add middleware to log requests or manipulate the request/response pipeline.

### Streams and Server-Sent Events (SSE)

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.prepare()
await res.write("Chunked")
await sleepAsync(5.seconds)
await res.write("Stream")
await res.finish()
proc handler2(req: Request, res: Response): Future[Response] {.async.} =
await res.prepare(HttpResponseStreamType.SSE)
await res.event("stream", "SSE Event")
await sleepAsync(5.seconds)
await res.event("stream", "Stream")
await res.finish()
let router = newRouter()
.register(get("/chunks", handler))
.register(get("/event", handler2))
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

Handle chunked responses and Server-Sent Events (SSE) for real-time data streaming.

### Filters (Authentication Example)

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Authenticated!")
let token = "your_auth_token"
let authFilter = Filter(code: Http401, body: "Authorization token required!")
.headerEqTo("Authorization", token)
let router = newRouter()
.register(get("/", handler))
.setFilter(authFilter)
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

Apply request filters for authentication or other request validation.

### Error Handling Example

```nim
import maia
proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.error(Http400, "Bad Request")
proc errorHandler(req: Request, res: Response, exc: ref Exception): Future[Response] {.async.} =
await res.setStatus(Http500).send("An error occurred")
let router = newRouter()
.setErrHandler(errorHandler)
.register(get("/", handler))
discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
```

Handle errors gracefully with custom error handlers.

## Conclusion

Maia makes it easy to develop fast, flexible, and scalable web applications in Nim. With support for routing, middleware, filters, and more, it is a lightweight framework that gives you full control over your web services.
15 changes: 15 additions & 0 deletions examples/errors.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.error(Http400, "Return error message")

proc errorHandler(req: Request, res: Response, exc: ref Exception): Future[Response] {.async.} =
await res.setStatus(Http500).send("An error occurred")

let router = newRouter()
.setErrHandler(errorHandler)
.register(get("/", handler))

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
12 changes: 12 additions & 0 deletions examples/files.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.sendFile("filename.txt")

let router = newRouter()
.register(get("/download", handler))
.register(staticFile("/static", "staticfile.txt"))

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
17 changes: 17 additions & 0 deletions examples/filters.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Authenticated!")

let token = "your_auth_token"

let authFilter = Filter(code: Http401, body: "Authorization token required!")
.headerEqTo("Authorization", token)

let router = newRouter()
.register(get("/", handler))
.setFilter(authFilter)

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
11 changes: 11 additions & 0 deletions examples/helloworld.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import maia

proc handler(req: Request, res: Response: Future[Response] {.async.} =
await res.send("Hello World!")

let router = newRouter()
.register(get("/", handler))

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
31 changes: 31 additions & 0 deletions examples/hooks.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Hello World!")

proc beforeRequest(req: Request) {.async.} =
echo "Before request hook"

proc afterRequest(res: Response) {.async.} =
echo "After request hook"

proc teardownRequest(exc: ref Exception) {.async.} =
echo "Teardown request hook"

proc startServerHook(address: TransportAddress) {.async.} =
echo "Server started"

proc stopServerHook(address: TransportAddress) {.async.} =
echo "Server stopped"

let router = newRouter()
.register(get("/", handler))

discard newHttpServer(router)
.addBeforeRequest(beforeRequest)
.addAfterRequest(afterRequest)
.addTeardownRequest(teardownRequest)
.addStartServerHook(startServerHook)
.addStopServerHook(stopServerHook)
.setConfig(Config(port: 5000))
.start()
16 changes: 16 additions & 0 deletions examples/middlewares.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Hello World!")

proc myMiddleware(req: Request, res: Response, next: Handler): Future[Response] {.async.} =
echo "My Middleware"
await next(req, res)

let router = newRouter()
.register(get("/", handler)
.apply(myMiddleware))

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
26 changes: 26 additions & 0 deletions examples/multi_routers.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Handler 1")

proc handler2(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Handler 2")

proc handler3(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Handler 3")

proc handler4(req: Request, res: Response): Future[Response] {.async.} =
await res.send("Handler 4")

let router2 = newRouter()
.register(get("/handle3", handler3))
.register(get("/handler4", handler4))

let router = newRouter()
.register(get("/handler", handler))
.register(get("/handler2", handler2))
.extends(router2)

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
13 changes: 13 additions & 0 deletions examples/path_args.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
let args = req.args.get()
let name = args.getString("name")
await res.send("Welcome " & name)

let router = newRouter()
.register(get("/:name", handler))

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
25 changes: 25 additions & 0 deletions examples/post_data.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
let form = req.form.get()
let name = form.getString("name")
await res.send("Welcome " & name)

proc handler2(req: Request, res: Response): Future[Response] {.async.} =
let data = req.json.get()
let name = data["name"].getStr
await res.send(%*{"message": "Welcome " & name})

proc handler3(req: Request, res: Response): Future[Response] {.async.} =
let files = req.files.get()
let filename = files["file"].filename
await res.send(filename)

let router = newRouter()
.register(post("/form", handler))
.register(post("/json", handler2))
.register(post("/files", handler3))

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
12 changes: 12 additions & 0 deletions examples/query.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import maia

proc handler(req: Request, res: Response): Future[Response] {.async.} =
let name = req.query.getString("name")
await res.send("Welcome " & name)

let router = newRouter()
.register(get("/", handler))

discard newHttpServer(router)
.setConfig(Config(port: 5000))
.start()
Loading

0 comments on commit 7dddf59

Please sign in to comment.