|
Katalis is nim lang micro web framework
Katalis always focusing on protocol implementation and performance improvement. For fullstack framework using katalis it will be depends on developer needs, we will not provides frontend engine or database layer engine (ORM) because it will vary for each developer taste!.
If you want to use katalis as fullstack nim, you can read on fullstack section in this documentation.
nimble install katalis
If some reason failed to install using nimble directory, you can install directly from the github
nimble install https://github.com/zendbit/katalis
After installing katalis, you can simply create your first app by import katalisApp
import katalis/katalisApp
Katalis has some macros for it's own DSL. Katalis use @! as macros prefix (We will explore about katalis DSL on the next section).
Create directory for the app
mkdir simpleapp
cd simpleapp
Create nim source file, in this case we will use app.nim
import katalis/katalisApp
## lets do some basics configuration
@!Settings.enableServeStatic = true
@!Settings.enableKeepAlive = true
## we also can use custom port
#@!Settings.port = Port(8080)
## lets start simple app
@!App:
@!Get "/":
@!Context.reply(Http200, "<h1>Hello World!.</h1>")
## kick and start the app
@!Emit
Compile the source with --threads:on switch to enable thread support and run it!
nim c -r --threads:on app.nim
Katalis will run on port 8000 as default port
Listening non secure (plain) on http://0.0.0.0:8000
Lets open with the browser http://localhost:8000
Internal katalis structure is devided into some folders structure
Core folder contains base katalis framework it's focused on http protocol implementataion and some protocols enhancements
Filename | Description |
---|---|
constants.nim | contains constans declaration used by katalis |
environment.nim | contains shared environment (settings, shared threads variable) |
form.nim | contains functionalities for construct form (urlencoded, data) |
httpContext.nim | contains http context per client request (cookie, request, response) |
katalis.nim | katalis object type and instance |
multipart.nim | contains functionality for construct multipart data |
replyMsg.nim | universal response message to client |
request.nim | request object type used by http context |
response.nim | response object type used by http context |
routes.nim | route object type and instance, contains funtionalities for handling route request |
session.nim | contains funtionalities for handling cookies |
staticFile.nim | contains funtionalities for handling static file |
webSocket.nim | websocket object type for handling websocket request |
Pipelines in katalis is like middleware, it will process request from client and response with appropriate response. Katalis has some pipelines
Pipelines | Descriptions |
---|---|
after | this will be evaluate after route process |
before | this will be evaluate before route process |
initialize | will be eveluate on katalis initialization when katalis start |
onReply | will be evaluate before response message to client, this usually used for modified response message |
Initialize pipeline will be eveluate on katalis initialization when katalis start.
Filename | Description |
---|---|
taskMonitor.nim | this will start task monitor for katalis |
We can also add custom task with schedules like cron job
Before pipeline will be evaluate before route processing, this pipeline has advantages like early checking like authentication. Katalis has some predefines before pipelines
Filename | Description |
---|---|
http.nim | handle http request from client (get, post, head, etc) |
httpRanges.nim | handle http ranges request from client |
session.nim | session initialization |
webSocket.nim | handle web socket request from client, if http protocol upgrade request present |
After pipeline will be evaluate after route processing, this pipelines has advantages like early checking if request has access to route resource or not
Filename | Description |
---|---|
httpStaticFile.nim | handle static file request from client |
Static file must be placed is in static folder, but we can also changes default static folder from configuration (For more information about configuration see configuration section).
OnReply pipeline will be evaluate before sending response to client, this pipeline used for modifying payload.
Filename | Description |
---|---|
httpChunked.nim | handle chunked payload response, default is chunked as http standard |
httpComposePayload.nim | handle composing payload header + body for response |
httpCompress.nim | handle compression support (gzip) if client support zip compression |
Clenup pipeline will evaluate after sending response to client, this pipeline will evaluate after all process response to client finished.
Filename | Description |
---|---|
httpContext.nim | will cleanup unused cache data related with http context |
Macros folder contains macros definition for katalis framework
Filename | Description |
---|---|
sugar.nim | macros definition for katalis DSL (Domain Specific Language) |
More information about DSL, see DSL (Domain Specific Languate) section
Utilities and helper for katalis framework
Filename | Description |
---|---|
crypt.nim | some cryptohraphy algorithm |
debug.nim | debug msg |
httpcore.nim | http core stdlib extension |
json.nim | some json stdlib extension |
Internal extension for katalis framework
Filename | Description |
---|---|
mustache.nim | mustache template engine using mustachu nimble pkg |
taskMonitor.nim | simple cron job for katalis |
validation.nim | simplify validation for form, json, and Table[string, string] |
Katalis application, this is starting poin of katalis framework. Includes all file needed for developing katalis application.
Filename | Description |
---|---|
katalisApp.nim | include this file for starting the app server |
Katalis pipeline contains include declaration for katalis pipelines order, include declaration is important depend on this order:
- initialize
- before
- after
- onReply
- cleanup
Filename | Description |
---|---|
pipelines.nim | pipeline order includes declaration |
Katalis come with Domain Specific Language, the purpose using DSL is for simplify the development and write less code. Katalis using @! prefix for the DSL to prevent confict and make it easy for coding convention. Katalis DSL available in katalis/macros/sugar.nim. There are some macros that only can be called inside @!App block and block pipeline in katalis let see the table.
Available on outside @!App block
Name | Description |
---|---|
@!Settings | katalis settings instance, related to Settings type object in katalis/core/environment.nim |
@!Emit | start katalis app, related to Katalis type object in katalis/core/katalis.nim |
@!Routes | katalis routes object instance, related to Route type object in katalis/core/routes.nim |
@!Katalis | katalis object instance, related to Katalis type object in katalis/core/katalis.nim |
@!Environment | katalis environment instance, related to Environment type object in katalis/core/environment.nim |
@!SharedEnv | katalis shared Table[string, string] type object for sharing between the app instance, related to Environment type object in katalis/core/environment.nim |
@!Trace | trace block for displaying debug message, available when @!Settings.enableTrace = true |
Available only inside @!App block
Name | Description |
---|---|
@!Before | before route block pipeline |
@!After | after route block pipeline |
@!OnReply | on reply pipeline |
@!Cleanup | cleanup pipeline |
@!EndPoint | set endpoint for each route prefix (Optional) |
@![Get, Post, Patch, Delete, Put, Options, Trace, Head, Connect] | http method for routing |
@!Context | http context route parameter, related to HttpContext type object in katalis/core/httpContext.nim |
@!Env | environment route parameter, related to Environment type object in katalis/core/environment.nim |
@!Req | request context from client, related to Request type object in katalis/core/request.nim |
@!Res | response context to client, related to Response type object in katalis/core/response.nim |
@!WebSocket | websocket context service, related to WebSocket type object in katalis/core/webSocket.nim |
@!Client | socket client context, related to AsyncSocket type object in katalis/core/httpContext.nim |
@!Body | request body from client, related to Request type object field in katalis/core/request.nim |
@!Segment | path segment url from client request, related to RequestParam type object in katalis/core/request.nim also related to Table[string, string] nim stdlib |
@!Query | query string from client request, related to RequestParam type object in katalis/core/request.nim also related to Table[string, string] nim stdlib |
@!Json | json data from client request, related to RequestParam type object in katalis/core/request.nim also related to JsonNode nim stdlib |
@!Xml | xml data from client request, related to RequestParam type object in katalis/core/request.nim also related to XmlNode nim stdlib |
@!Form | form data from client will handle form urlencode/multipart, related to Form type object in katalis/core/form.nim |
DSL Code structure in Katalis
## available on global
## @!Settings
## @!Emit -> Should called after @!App block
## @!Route
## @!Katalis
## @!Environment
## @!SharedEnv
## @!Trace
## katalis app block
@!App:
## code here
## endpoint optional, this endpoint prefix path will append to each route path request
## this is optional, and should be define before all other pipeline
@!EndPoint "/test/api"
## before route block
@!Before:
## available here
## @!Context
## @!Req
## @!Res
## @!Env
## @!Res
## @!WebSocket
## @!Client
## @!Body
## @!Segment
## @!Query
## @!Json
## @!Xml
## @!Form
## also global katalis macros
## code here
## after route block
@!After:
## code here
## on reply block
@!OnReply:
## code here
## cleanup block
@!Cleanup:
## code here
## routing
## available method @!Get, @!Post, @!Put, @!Delete, @!Patch, @!Head, @!Connect, @!Options, @!Trace
@!Post "/register":
## code here
@!Get "/home":
## code here
## also support for multiple method on routing
@![Get, Post] "/login":
## code here
Configuration can be set using @!Settings macro. See katalis/core/environment.nim (Settings object type)
@!Settings.address = "0.0.0.0" ## default
@!Settings.port = Port(8000) ## default
## available settings (default value, all size metric in bytes):
## address: string = "0.0.0.0"
## port: Port = Port(8000)
## enableReuseAddress: bool = true
## enableReusePort:bool = true
## sslSettings: SslSettings = nil
## maxRecvSize: int64 = 209715200
## enableKeepAlive: bool = true
## enableOOBInline: bool = false
## enableBroadcast: bool = false
## enableDontRoute: bool = false
## storagesDir: string = getCurrentDir().joinPath("storages")
## storagesUploadDir: string = getCurrentDir().joinPath("storages", "upload")
## storagesBodyDir: string = getCurrentDir().joinPath("storages", "body")
## storagesSessionDir: string = getCurrentDir().joinPath("storages", "session")
## staticDir: string = getCurrentDir().joinPath("static")
## enableServeStatic: bool = false
## readRecvBuffer: int = 524288
## enableTrace: bool = false
## chunkSize: int = 16384
## maxSendSize: int = 52428800
## enableChunkedTransfer: bool = true
## enableRanges: bool = true
## rangesSize: int = 2097152
## enableCompression: bool = true
## maxBodySize: int = 52428800
For serving static file like static html, css, image, video, etc. We only need to enable enableServeStatic in katalis settings.
Lets create serverstatic-example folder.
mkdir servestatic-example
cd servestatic-example
Then create static folder inside servestatic-example folder
mkdir static
Inside servestatic-example folder we create minimal katalis app for serving static file. In this case we create app.nim
import katalis/katalisApp
## enable static file service
@!Settings.enableServeStatic = true
@!Settings.enableKeepAlive = true
@!Emit
Compile and start the server
nim c -r app.nim
Don't forget to put your static files into static folder
Open with browser http://localhost:8000/index.html
import katalis/katalisApp
import katalis/extension/mustache
@!Settings.enableServeStatic = true
@!Settings.enableKeepAlive = true
@!App:
## we can also create prefix for all routes
@!EndPoint "/admin" ## \
## all routes will prefixed with /admin/
## get request for default /
## in this case, because we already set @!EndPoint to "/admin"
## so the route url will be http://localhost:8000/admin
@!Get "/":
await @!Context.reply(Http200, "<h1>This is admin the root page!")
## another get example
## http://localhost:8000/hello
@!Get "/hello":
await @!Context.reply(Http200, "<h1>world!</h1>")
## mapping route, retrieve segment to variable
@!Get "/birthdate/:month/:day/:year":
## this will retrieve segments
## as variable month, day, and year
## http://localhost/admin/birthdate/may/22/2000
let birthdate = [@!Segment["month"], @!Segment["day"], @!Segment["year"]].join("/")
await @!Context.reply(
Http200,
&"<h3>Birthdate</h3> <p>{birthdate}</p>"
)
## mapping route, retrieve query string to variable
@!Get "/birthdate":
## this will retrieve query string
## as variable month, day, and year
## http://localhost/admin/birthdate?month=may&day=22&year=2000
let birthdate = [@!Query.getOrDefault("month"), @!Query.getOrDefault("day"), @!Query.getOrDefault("year")].join("/")
await @!Context.reply(
Http200,
&"<h3>Birthdate</h3> <p>{birthdate}</p>"
)
## mapping route, retrieve segment as regex pattern
@!Get "/birthdate/re<:month([a-zA-Z]+)_:day([0-9]+)_:year([0-9]+)>":
## this will retrieve query string
## as variable month, day, and year
## http://localhost/admin/birthdate/may_22_2000
let birthdate = [@!Segment.getOrDefault("month"), @!Segment.getOrDefault("day"), @!Segment.getOrDefault("year")].join("/")
await @!Context.reply(
Http200,
&"<h3>Birthdate</h3> <p>{birthdate}</p>"
)
## we also can have multiple method
## in one route definition
@![Get, Post] "/login":
## we can use validation to check fields
## but for validation details we will discuss in other section
## about validation
## let get form data username, password
## if method http post validate the form data
let username = @!Form.data.getOrDefault("username")
let password = @!Form.data.getOrDefault("password")
var errorMsg = ""
if @!Req.httpMethod == HttpPost:
if username == "" or password == "":
errorMsg = "username or password is required!"
##
## katalis come with mustache template engine
## for template engine we will explain later
##
let tpl =
"""
<html>
<head>
<title>example</title>
</head>
<body>
<h3>Login</h3>
<form method="POST">
<input type="text" name="username" placeholder="username" value="{{username}}">
<br>
<input type="password" name="password" placeholder="password" value="{{password}}">
<br>
<input type="submit" value="Login">
</form>
<h4>{{errorMsg}}</h4>
</body>
</html>
"""
let m = newMustache()
m.context["errorMsg"] = errorMsg
m.context["username"] = username
m.context["password"] = password
@!Context.reply(Http200, m.render(tpl))
## we can have multiple @!App for routes separation
@!App:
## let definde endpoint for users
@!EndPoint "/user"
## the user root page
## http://localhost:8000/user
@!Get "/":
await @!Context.reply(Http200, "<h1>This is user root page</h1>")
@!Emit
import katalis/katalisApp
@!Settings.enableServeStatic = true
@!Settings.enableKeepAlive = true
@!App:
@!Get "/test-qs":
## lets do query string test
## http://localhost:8000/test-qs?city=ngawi&province=surabaya with get method
let city = @!Query.getOrDefault("city")
let province = @!Query.getOrDefault("province")
@!Context.reply(Http200, &"<h3>Welcome to {province}, {city}.</h3>")
import katalis/katalisApp
@!Settings.enableServeStatic = true
@!Settings.enableKeepAlive = true
@!App:
@!Post "/test-form":
## lets do form test
## http://localhost:8000/test-form with post method
let city = @!Form.data.getOrDefault("city")
let province = @!Form.data.getOrDefault("province")
@!Context.reply(Http200, &"<h3>Welcome to {province}, {city}.</h3>")
All json request data will convert to nim stdlib json see https://nim-lang.org/docs/json.html
import katalis/katalisApp
@!Settings.enableServeStatic = true
@!Settings.enableKeepAlive = true
@!App:
@!Post "/test-json":
## lets do json test
## http://localhost:8000/test-json with post method
let data = @!Json ## \
## json data from client request
## all data will convert to nim stdlib JsonNode
## see https://nim-lang.org/docs/json.html
## lets modify the data add country to json
data["country"] = %"indonesia"
## katalis will automatic response as json if we pass JsonNode
## lets pass JsonNode from client and we modify it
await @!Context.replyJson(Http200, data)
All xml request data will convert to nim stdlib xmltree see https://nim-lang.org/docs/xmltree.html
@!Post "/test-xml":
## lets do xml test
## http://localhost:8000/test-xml with post method
let data = @!Xml ## \
## xml data from client request
## all data will convert to nim stdlib XmlNode
## see https://nim-lang.org/docs/xmltree.html
##
## Try to send data using this xml format
## <Address>
## <City>Ngawi</City>
## <Province>Surabaya</Province>
## </Address>
##
## lets modify the data add country
let country = newElement("Country")
country.add(newText("Indonesia"))
data.add(country)
## katalis will automatic response as xml if we pass XmlNode
## lets pass XmlNode from client and we modify it
await @!Context.replyXml(Http200, data)
@![Get, Post] "/test-upload":
## lets do upload multipart data
## katalis come with mustache template engine
## for template engine we will explain later
##
let tpl =
"""
<html>
<head>
<title>upload test</title>
</head>
<body>
<h3>Upload files</h3>
<form method="POST" enctype="multipart/form-data">
Upload Single
<br>
<input name="onefile" type="file" />
<br>
<br>
Upload Multiple
<br>
<input name="multiplefiles[]" type="file" multiple />
<br>
<br>
<button type="submit">Upload</button>
</form>
</body>
</html>
"""
if @!Req.httpMethod == HttpPost:
## test show uploaded file info to console
if @!Form.files.len != 0:
for name, files in @!Form.files:
echo name
for file in files:
echo file.extension
echo file.path
echo file.mimetype
echo file.isAccessible
## create directory uploaded if not exist
if not "uploaded".dirExists:
"uploaded".createDir
## check if files exists
if "onefile" in @!Form.files:
let onefile = @!Form.files["onefile"][0]
## move files to uploaded dir
onefile.path.moveFile("uploaded".joinPath(onefile.name))
if "multiplefiles" in @!Form.files:
let multiplefiles = @!Form.files["multiplefiles"]
for file in multiplefiles:
## move files to uploaded dir
file.path.moveFile("uploaded".joinPath(file.name))
let m = newMustache()
@!Context.reply(Http200, m.render(tpl))
We can modify response header for redirection purpose
@!App:
@!Get "/home":
@!Context.reply(Http200, "<h3>Welcome home!</h3>")
@!Get "/test-redirect":
## modify response header add redirect location to /home
@!Res.headers["Location"] = "/home"
@!Context.reply(Http307, "")
See katalis/core/session.nim
@!App:
## init cookie session
@!Before:
await @!Context.initCookieSession()
@!Get "/hello":
await @!Context.addCookieSession("name", %"Tian Long")
let name = await @!Context.getCookieSession("name")
## remove individual session with @!Context.deleteCookieSession("name")
## destroy all session value with @!Context.destroyCookieSession()
await @!Context.reply(Http200, &"Hello {name}!")
Before pipeline will execute before routing process, also before serving staticfile. We can use it to check for all route before route process. We can skip all route by returning true statement
@!App:
@!Before:
## your code here
if something_wrong:
@!Context.reply(Http403, "Anauthorized access!")
## by returning true, will skip all process and return the error message, this is simplify for checking
return true
After pipeline will execute after routing process, also after serving staticfile
@!App:
@!After:
## your code here
if something_wrong:
@!Res.headers["Location"] = "/home"
@!Context.reply(Http307, "")
## return true for skip all routing definition
return true
OnReply pipeline will process before sending request to client, we can modify for all response from route. This example is from katalis/pipelines/onReply/httpChunked.nim, this code is for converting all response to chunked data before sending to client
##
## katalis framework
## This framework if free to use and to modify
## License: MIT
## Author: Amru Rosyada
## Email: amru.rosyada@gmail.com, amru.rosyada@amscloud.co.id
## Git: https://github.com/zendbit/katalis
##
##
## add pipeline onreply
## as http protocol handler
## check if chunked Transfer-Encoding enabled or not
##
import std/
[
streams,
strutils
]
import
../../core/routes,
../../macros/sugar,
../../core/environment
proc composeChunkPayload(
ctx: HttpContext,
env: Environment = environment.instance()
): string {.gcsafe.} =
## compose chunk payload
## chunked transfer encoding must follow this role
## for more details see about Transfer-Encoding: chunked
result = ctx.response.body
let bodyLength = ctx.response.body.len
var chunkSize = env.settings.chunkSize
if env.settings.enableChunkedTransfer and
bodyLength >= chunkSize:
proc constructChunk(
chunkSize: string,
chunk: string
): string =
result = chunkSize &
$CRLF &
chunk &
$CRLF
let numberOfChunks = (bodyLength / chunkSize).int
var chunkSizeInHex = chunkSize.toHex
let bodyStream = newStringStream(result)
var bodyBuffer: string = ""
for _ in 1..numberOfChunks:
bodyBuffer &= constructChunk(
chunkSizeInHex,
bodyStream.readStr(chunkSize)
)
if bodyLength > chunkSize:
chunkSize = bodyLength mod chunkSize
bodyBuffer &= constructChunk(
chunkSize.toHex,
bodyStream.readStr(chunkSize)
)
bodyBuffer &= constructChunk("0", "")
bodyStream.close()
result = bodyBuffer
@!App:
@!OnReply:
if @!Req.httpMethod == HttpHead: return
if not @!Settings.enableChunkedTransfer or
@!Res.body.len > @!Env.settings.chunkSize:
@!Res.body = @!Context.composeChunkPayload(@!Env)
@!Res.headers["transfer-encoding"] = "chunked"
Cleanup pipeline will process after all pipeline finished, this usually for cleanup resource. This example is from katalis/pipelines/cleanup/httpContext.nim
##
## katalis framework
## This framework if free to use and to modify
## License: MIT
## Author: Amru Rosyada
## Email: amru.rosyada@gmail.com, amru.rosyada@amscloud.co.id
## Git: https://github.com/zendbit/katalis
##
##
## Cleanup body request cache
##
import
../../core/routes,
../../macros/sugar,
../../core/environment
@!App:
@!Cleanup:
if @!Req.body.fileExists:
@!Req.body.removeFile
if @!Req.param.form.files.len != 0:
## remove file after file uploaded
## uploaded file should be move after finished
## file uploaded before cleanup present
for _, files in @!Req.param.form.files:
for file in files:
if not file.isAccessible or not file.path.fileExists:
continue
file.path.removeFile
## clear http context
@!Context.clear
Response message is universal response message, using this response message will always response application/json. See katalis/core/replyMsg
@!App:
@!Get "/test-replymsg":
## see core katalis/core/replyMsg.nim
@!Context.reply(newReplyMsg(
httpCode = Http200,
success = true,
data = %*{
"username": "tian",
"address": "Guangdong"
},
error = %*{}
))
Katalis comes with validations feature. See katalis/extension/validation.nim.
Available validations are:
- isRequired
- isEmail
- minValue
- maxValue
- minLength
- maxLength
- isDateTime
- minDateTime
- maxDateTime
- inList
- matchWith -> regex validation
@!App:
@![Get, Post] "/test-validation":
## validation is extension on katalis
## see katalis/extension/validation.nim
let tpl =
"""
<html>
<head><title>validation test</title></head>
<body>
<form method="POST">
<label>Username<label> <span>{{username.msg}}</span>
<br>
<input type="text" name="username" value="{{username.value}}">
<br>
<br>
<label>Password</label> <span>{{password.msg}}</span>
<br>
<input type="password" name="password" value="{{password.value}}">
<br>
<br>
<input type="submit" value="Register">
</form>
</body>
</html>
"""
## mustache template
let m = newMustache()
if @!Req.httpMethod == HttpPost:
## parameter can be Form, JsonNode or Table[string, string] type
let v = newValidation(@!Form)
v.withField("username").
isRequired(failedMsg = "Username is empty."). ## we can add custom failedMsg
minLength(8). ## minimum length of field value is 8 char length
maxLength(50). ## maximum length of field value is 50 char length
matchWith("([a-zA-Z0-9_]+)$", failedMsg = "Only a-z A-Z 0-9 _ are allowed") ## check with regex, only allow a-z A-Z 0-9 _
v.withField("password").
isRequired(failedMsg = "Password is empty."). ## we can add custom failedMsg
minLength(8). ## minimum length 8 char length
maxLength(254) ## maximum length 254 char length
## we can check validation result for each field
## lets print to console
echo "username " & v.fields["username"].msg & " -> " & $v.fields["username"].isValid
echo "password " & v.fields["password"].msg & " -> " & $v.fields["password"].isValid
## set mustache context send data to template
for fieldName, fieldData in v.fields:
m.context[fieldName] = %fieldData
@!Context.reply(Http200, m.render(tpl))
Nim come with Mustache template engine. see katalis/extension/mustache.nim, this template based on https://github.com/fenekku/moustachu.
For using mustache, we need to import mustache from the extension
import katalis/extension/mustache.nim
For mustache specs, you can refer to https://mustache.github.io/
Mustache can be inline or using .mustache file, in this case we will setup mustache using .mustache.
We need create templates directory
mkdir templates
Then add file index.mustache, header.mustache, footer.mustache. Mustache specs support partials template.
header.mustache
<div>
<h3>This is header section<h3>
</div>
footer.mustache
<div>
<h3>This is footer section<h3>
</div>
Then we will include partials header.mustache and footer.mustache
index.mustache
<html>
<head><title>mustache example</title></head>
<body>
<div>
{{> header}}
<div>
<h3>This is content section<h3>
<h3>{{post.title}}</h3>
<p>{{post.article}}</p>
</div>
{{> footer}}
</div>
</body>
</html>
Mustache using {{tag_mustache}} for data binding, in current nim it support JsonNode, Tables, and mustache Context it self.
Let do with the code
@!App:
@!Get "/test-mustache":
let m = newMustache()
m.context["post"] = %*{"title": "This is katalis", "article": "This is just simple micro framework but powerfull!"}
## call the index.mustache in the templates folder with m.render("index")
await @!Context.reply(Http200, m.render("index"))
Out of the box with webscoket. See katalis/core/webSocket.nim
@!App:
## it will accessed with ws://localhost:8000/ws
@!WebSocket "/ws":
case @!WebSocket.state
of WsState.Open:
case @!WebSocket.inFrame.opCode
of WsOpCode.TextFrame.uint8:
await @!WebSocket.reply("This is from end point.")
of WsOpCode.ContinuationFrame.uint8:
## code here
of WsOpCode.BinaryFrame.uint8:
## code here
else:
discard
of WsState.Close:
echo "Closed"
else:
discard
Katalis also support serve SSL, we just need ssl certificate or we can use self signed certificate for development purpose.
Hot to create self signed SSL?, you can follow this instruction https://devcenter.heroku.com/articles/ssl-certificate-self. Or you can find other resources from the internet world.
Then you can pass the certificate to the katalis settings
@!Settings.sslSettings = newSslSettings(
certFile = "path to certificate",
keyFile = "path to key file",
port = Port(8443), ## default value
enableVerify = false ## set to true if using production and valid ssl certificate
)
it will server on https://localhost:8443
Katalis is not fullstack framework, but if you want to use katalis as part of your stack you can use with others framework.
Frontend:
Databse (ORM):
Katalis coding style guideline is simple
- Follow nim lang Coding Style
- Only use Katalis DSL on the App and Pipeline don't use it on the core, utils to make katalis easy for debugging