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}