src/happyx/ssr/server

Search:
Group by:

Server 🔨

Provides a Server object that encapsulates the server's address, port, and logger. Developers can customize the logger's format using the built-in newConsoleLogger function. HappyX provides two options for handling HTTP requests: httpx, microasynchttpserver and asynchttpserver.

To enable httpx just compile with -d:httpx or -d:happyxHttpx. To enable MicroAsyncHttpServer just compile with -d:micro or -d:happyxMicro. To enable HttpBeast just compile with -d:beast or -d:happyxBeast

To enable debugging just compile with -d:happyxDebug.

Queries ❔

In any request you can get queries.

Just use query?name to get any query param. By default returns ""

If you want to use arrays in query just use queryArr?name to get any array query param.

WebSockets 🍍

In any request you can get connected websocket clients. Just use wsConnections that type is seq[WebSocket]

In any websocket route you can use wsClient for working with current websocket client. wsClient type is WebSocket.

Static directories 🍍

To declare static directory you just should mark it as static directory 🙂

serve(...):
  # Users can get all files in /myDirectory via
  # http://.../myDirectory/path/to/file
  staticDir "myDirectory"

Custom static directory ⚙

To declare custom path for static dir just use this

serve(...):
  # Users can get all files in /myDirectory via
  # http://.../customPath/path/to/file
  staticDir "/customPath" -> "myDirectory"

Note: here you can't use path params

Types

ApiDocObject = object
  description*: string
  path*: string
  httpMethod*: seq[string]
  pathParams*: seq[PathParamObj]
  models*: seq[RequestModelObj]
CustomHeaders = StringTableRef
ModelBase = ref object of PyNimObjectExperimental
Server = ref object
  address*: string
  port*: int
  routes*: seq[Route]
  path*: string
  parent*: Server
  notFoundCallback*: PyObject
  middlewareCallback*: PyObject
  logger*: Logger
  when enableHttpx:
      instance*: Settings

  elif enableHttpBeast:
      instance*: Settings

  elif enableMicro:
      instance*: MicroAsyncHttpServer

  else:
      instance*: AsyncHttpServer

  

Procs

proc `[]=`[T](self: CustomHeaders; key: string; value: T)
proc answerFile(req: Request; filename: string; code: HttpCode = Http200;
                asAttachment = false; bufSize: int = 40960;
                forceResponse: bool = false;
                headers: CustomHeaders = newCustomHeaders()): owned(
    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect],
                    forbids: [].}

Respond file to request.

Automatically enables streaming response when file size is too big (> 1 000 000 bytes)

Low-level API

Use this example instead of this procedure

get "/$filename":
  return FileResponse("/publicFolder" / filename)
func fgColored(text: string; clr: ForegroundColor): string {.inline,
    ...raises: [ValueError], tags: [], forbids: [].}

This function takes in a string of text and a ForegroundColor enum value and returns the same text with the specified color applied.

Arguments:

  • text: A string value representing the text to apply color to.
  • clr: A ForegroundColor enum value representing the color to apply to the text.

Return value:

  • The function returns a string value with the specified color applied to the input text.

Example:

echo fgColored("Hello, world!", fgRed)
proc newApiDocObject(httpMethod: seq[string]; description, path: string;
                     pathParams: seq[PathParamObj]; models: seq[RequestModelObj]): ApiDocObject {.
    ...raises: [], tags: [], forbids: [].}
proc newCustomHeaders(): CustomHeaders {....raises: [], tags: [], forbids: [].}
proc newServer(address: string = "127.0.0.1"; port: int = 5000): Server {.
    ...raises: [ValueError], tags: [], forbids: [].}

This procedure creates and returns a new instance of the Server object, which listens for incoming connections on the specified IP address and port. If no address is provided, it defaults to 127.0.0.1, which is the local loopback address. If no port is provided, it defaults to 5000.

Parameters:

  • address (optional): A string representing the IP address that the server should listen on. Defaults to "127.0.0.1".
  • port (optional): An integer representing the port number that the server should listen on.

Returns:

  • A new instance of the Server object.

Example:

var s = newServer()
assert s.address == "127.0.0.1"

Macros

macro `.`(obj: JsonNode; field: untyped): JsonNode
macro initServer(body: untyped): untyped

Shortcut for

Low-level API

proc main() {.gcsafe.} =
  `body`
main()
macro liveview(body: untyped): untyped
macro routes(server: Server; body: untyped = newStmtList()): untyped
You can create routes with this marco

Available Path Params

  • bool: any boolean (y, yes, on, 1 and true for true; n, no, off, 0 and false for false).
  • int: any integer.
  • float: any float number.
  • word: any word includes re2"\w+".
  • string: any string excludes "/".
  • enum(EnumName): any string excludes "/". Converts into EnumName.
  • path: any float number includes "/".
  • regex: any regex pattern excludes groups. Usage - "/path{pattern:/yourRegex/}"

Available Route Types

  • "/path/with/{args:path}": Just string with route path. Matches any request method
  • get "/path/{args:word}": Route with request method. Method can beget, post, patch, etc.
  • notfound: Route that matches when no other matched.
  • middleware: Always executes first.
  • finalize: Executes when server is closing

Route Scope:

  • req: Current request
  • urlPath: Current url path
  • query: Current url path queries
  • queryArr: Current url path queries (usable for seqstring)
  • wsConnections: All websocket connections

Available Websocket Routing

  • ws "/path/to/websockets/{args:word}: Route with websockets
  • wsConnect: Calls on any websocket client was connected
  • wsClosed: Calls on any websocket client was disconnected
  • wsMismatchProtocol: Calls on mismatch protocol
  • wsError: Calls on any other ws error

Websocket Scope:

  • req: Current request
  • urlPath: Current url path
  • query: Current url path queries
  • queryArr: Current url path queries (usable for seqstring)
  • wsClient: Current websocket client
  • wsConnections: All websocket connections

Example

var myServer = newServer()
myServer.routes:
  "/":
    "root"
  "/user{id:int}":
    "hello, user {id}!"
  middleware:
    echo req
  notfound:
    "Oops! Not found!"
macro serve(address: string; port: int; body: untyped): untyped

Initializes a new server and start it. Shortcut for

High-level API

proc main() =
  var server = newServer(`address`, `port`)
  server.routes:
    `body`
  server.start()
main()

For GC Safety you can declare your variables inside serve macro ✌

serve(...):
  var index = 0
  let some = "some"
  
  "/":
    inc index
    return {"index": index}
  
  "/some":
    return some

Templates

template answer(req: Request; message: string | int | float | bool | char;
                code: HttpCode = Http200; headers: HttpHeaders = newHttpHeaders(
    [("Content-Type", "text/plain; charset=utf-8")]);
                contentLength: Option[int] = int.none)

Answers to the request

Low-level API

Arguments: req: Request: An instance of the Request type, representing the request that we are responding to. message: string: The message that we want to include in the response body. code: HttpCode = Http200: The HTTP status code that we want to send in the response. This argument is optional, with a default value of Http200 (OK).

Use this example instead

get "/":
  return "Hello, world!"
template answerHtml(req: Request; data: string | TagRef;
                    code: HttpCode = Http200; headers: HttpHeaders = newHttpHeaders(
    [("Content-Type", "text/html; charset=utf-8")])): untyped

Answers to request with HTML data

Low-level API

Use this example instead:

var html = buildHtml:
  tDiv:
    "Hello, world!"

get "/1":
  # Respond HTML variable
  return html
get "/2":
  # Respond HTML directly
  return buildHtml:
    tDiv:
      "Hello, world!"
template answerJson(req: Request; data: untyped; code: HttpCode = Http200;
    headers: HttpHeaders = newHttpHeaders(
    [("Content-Type", "application/json; charset=utf-8")])): untyped

Answers to request with json data

Low-level API

Use this example instead

var json = %*{"response": 1}

get "/1":
  # respond variable
  return json
get "/2":
  # respond JSON directly
  return {"response": 1}
template start(server: Server): untyped
The start template starts the given server and listens for incoming connections. Parameters:
  • server: A Server instance that needs to be started.

Returns:

  • untyped: This template does not return any value.