rescript-express-server-template

1.4.1 • Public • Published

rescript-express-server-template

extemdable rescript server and routes builder

Depends

Example of usage

//file ExpressPublicUsageCase.res
open ExpressServer
open ExpressHandler
open ExpressParseUrlHandlerConverter
open ExpressParseJsonHandlerConverter
open ExpressFileHandlerConverter
open! ExpressHandlerMiddleware

//Defaultly server await "handler" type = Handler(array<middlewares>, (req, res) => unit)
//Handler modules allow to modify request and response and write hanlder-func use
//this modified versions: (req' => res'), and convert this funciton
//into default "handler" type
module Default = MakeDefault(DefaultErrorStrategy) //default handler builder
module QueryConverter = ExpressParseUrlHandlerConverter.Make(Default)
module QueryHandler = Make( Default, QueryConverter ) //parse url handler
module JsonConverter = ExpressParseJsonHandlerConverter.Make(QueryHandler)
module JsonHandler = Make( QueryHandler, JsonConverter ) //parse json-post-body handler
module FileParseConfig: FileParseConfig = {
    let getFileAwaitFields: () => array<fileAwaitField> =
        () => [{name: "fileInp", maxCount: 1}]
    let getDestPath: () => string =
        () => "/uploads"
}
module FileConverter = 
    ExpressFileHandlerConverter.Make(JsonHandler, FileParseConfig)
module FileHandler = Make( JsonHandler, FileConverter ) //files loading handler

let indexPageHtml = `
<form action="/apply-post" method="post" enctype="multipart/form-data">
    <h1>Test form</h1>
    <input name="textInp" value="" style="display: block;">
    <textarea name="textAr" style="display: block;"></textarea>
    <input type="file" name="fileInp" style="display: block;">
    <input type="submit" style="display: block;">
</form>` //page html

let stringifyAny: ('a) => string = %raw(`function (u) {
    return u ? JSON.stringify(u) : ""
}`)

let routes = [
    Route(#get, "/", QueryHandler.handler(r => { //show form and show get-query
        let UrlReq(_, urlData) = r
        let unkToJson: (unknown) => string = %raw(`function(u) {
            return JSON.stringify(u) + ""
        }`)
        let urlDataStr = unkToJson(urlData)
        Html("Url data: " ++ urlDataStr ++ "<br><br>" ++ indexPageHtml)
    })),
    Route(#post, "/apply-post", FileHandler.handler(r => { //form handler and show data
        let FileReq(JsonReq(_, json), files) = r
        let json = `{"json": "${stringifyAny(json)}",
        "files": "${stringifyAny(files)}"}`
        Json(json)
    })),
]

runServer(routes, [], 80, () => {Js.Console.log("Server started!")})

ExpressServer.resi

ExpressServer has simple signature to server running

open Belt
open WebTypes

type request
type response
type middleware
type expressApp

type scenario = (request, response) => unit
type handler = Handler(array<middleware>, scenario)

type url = string

type route = Route(method, url, handler)

let staticFilesMiddleware: string => middleware

let runServer: (array<route>, array<middleware>, int, unit => unit) => unit

ExpressHandler.resi

Defaultly server await handler: type handler = Handler(array<middlewares>, (req, res) => unit) Handler modules allow to modify request and response and write hanlder-func use this modified versions: handler' = (req' => res'), and convert this function into default handler type.

open ExpressServer
open WebTypes

module type Handler = {
  type hReq
  type hRes

  let wrapReq: ((request, response)) => hReq
  let applyRes: (hReq, hRes) => unit
  let primalReq: hReq => (request, response)
  let convert: (hReq => hRes, request, response) => unit
  let middlewares: array<middleware>
  let handler: (hReq => hRes) => handler
}

type serverRespType =
  | Html(string)
  | Json(string)
  | OpenFile(string)
  | DownloadFile(string)
  | Redirect(string, redirectStatus)
  | Error(string, errorStatus)

module type Default = Handler with type hReq = (request, response) and type hRes = serverRespType

module type ErrorStrategy = {
  let wrapTryCatch: (unit => unit) => unit
}

module DefaultErrorStrategy: ErrorStrategy

module type MakeDefault = (ErrorStrategy: ErrorStrategy) => Default
//Default handler
module MakeDefault: MakeDefault

where someF: req' => res' is handler-function writen on confortable for coding and reading language. Handler module allows this funciton conversation.

ExpressHandlerChain.resi

Is a Module of Hanlder type which allows depends with some old Handler and allowa chain of conversations.

This modules defining with ExpressHandlerChain.Make functor:

open ExpressHandler
open ExpressServer
open Belt

module type Converter = {
  type oldReq
  type newReq
  type oldRes
  type newRes

  let wrapStepReq: oldReq => newReq
  let getOldReq: newReq => oldReq
  let applyStepRes: (newReq, newRes) => oldRes
  let middlewares: array<middleware>
}

module type MakeConverter = (OldHandler: Handler) =>
(Converter with type oldReq = OldHandler.hReq and type oldRes = OldHandler.hRes)

module type Make = (
  OldHandler: Handler,
  Converter: Converter with type oldReq = OldHandler.hReq and type oldRes = OldHandler.hRes,
) => (Handler with type hReq = Converter.newReq and type hRes = Converter.newRes)

module Make: Make

ExpressHandlerConverter.resi

is a minimal required module functionality for creation HandlerMiddleware chain.

open ExpressServer
open ExpressHandler
open ExpressHandlerChain

type jsonReq<'a> = JsonReq('a, unknown)

module type Make = (OldHandler: Handler) =>
(
  Converter
    with type oldReq = OldHandler.hReq
    and type newReq = jsonReq<OldHandler.hReq>
    and type oldRes = OldHandler.hRes
    and type newRes = OldHandler.hRes
)

module Make: Make

Implement this module and you will make build chain of handlers

Standart Handler Converters

ExpressParseUrlHandlerConverter.resi

needs for parsing params from url of GET request

open ExpressServer
open ExpressHandler
open ExpressHandlerChain

type urlReq<'a> = UrlReq('a, unknown)

module type Make = (OldHandler: Handler) =>
(
  Converter
    with type oldReq = OldHandler.hReq
    and type newReq = urlReq<OldHandler.hReq>
    and type oldRes = OldHandler.hRes
    and type newRes = OldHandler.hRes
)

module Make: Make

ExpressJsonHandlerConverter.resi

needs for parsing json-request-body from POST request

open ExpressServer
open ExpressHandler
open ExpressHandlerChain

type jsonReq<'a> = JsonReq('a, unknown)

module type Make = (OldHandler: Handler) =>
(
  Converter
    with type oldReq = OldHandler.hReq
    and type newReq = jsonReq<OldHandler.hReq>
    and type oldRes = OldHandler.hRes
    and type newRes = OldHandler.hRes
)

module Make: Make

ExpressSessionHandlerConverter.resi

needs for parsing session data and handle session-effects

open ExpressHandler
open ExpressHandlerChain

type sessionReq<'a> = SessionReq('a, unknown)
type sessionEffect =
  | SetSessionValue(string, unknown)
  | DestroySession
type sessionRes<'a> = SessionRes('a, array<sessionEffect>)

type sessionParam =
  | SessionResave(bool)
  | SessionSaveUnitialized(bool)
  | Cookie(bool, option<int>)

type sessionConfig = SessionConfig(string, array<sessionParam>)

let setSessionValue: (string, 'a) => sessionEffect

module type SessionConfigurator = {
  let getSessionConfig: unit => sessionConfig
}

module DefaultConfigurator: SessionConfigurator

module type Make = (OldHandler: Handler, SessionConfigurator: SessionConfigurator) =>
(
  Converter
    with type oldReq = OldHandler.hReq
    and type newReq = sessionReq<OldHandler.hReq>
    and type oldRes = OldHandler.hRes
    and type newRes = sessionRes<OldHandler.hRes>
)

module Make: Make

ExpressFileHandlerConverter.resi

needs for building routes awaits file uploading and get access to files

open ExpressServer
open ExpressHandler
open ExpressHandlerChain

type fileAwaitField = {
  name: string,
  maxCount: int,
}

type file = {
  fieldname: string,
  originalname: string,
  encoding: string,
  mimetype: string,
  destination: string,
  filename: string,
  path: string,
  size: int,
}
type fileParsedField = {fileName: string, files: array<file>}
type fileReq<'a> = FileReq('a, array<fileParsedField>)

module type FileParseConfig = {
  let getFileAwaitFields: unit => array<fileAwaitField>
  let getDestPath: unit => string
}

module type Make = (OldHandler: Handler, FileParseConfig: FileParseConfig) =>
(
  Converter
    with type oldReq = OldHandler.hReq
    and type newReq = fileReq<OldHandler.hReq>
    and type oldRes = OldHandler.hRes
    and type newRes = OldHandler.hRes
)

module Make: Make

ExpressAuthSessionHandlerConverter.resi

needs for authentification using session. Warning! in case of use with ExpressSessionHandlerConverter use same SessionManager

open ExpressHandler
open ExpressHandlerChain

type authSessReq<'r, 'u> = AuthSessReq('r, option<'u>)

type authSessEffect<'u> =
  | Login('u)
  | Logout

type authSessRes<'r, 'u> = AuthSessRes('r, array<authSessEffect<'u>>)

module type UserManager = {
  type loginData
  type user
  type userCheckAuthData

  let produceAuthData: user => userCheckAuthData
  let checkAuthData: userCheckAuthData => option<user>
  let checkLoginData: loginData => option<user>
  let invalidLoginDataMsg: unit => string
}

type sessionParam =
  | SessionResave(bool)
  | SessionSaveUnitialized(bool)
  | Cookie(bool, option<int>)

type sessionConfig = SessionConfig(string, array<sessionParam>)

module type SessionConfigurator = {
  let getSessionConfig: unit => sessionConfig
}

module DefaultConfigurator: SessionConfigurator

module type Make = (
  OldHandler: Handler,
  UserManager: UserManager,
  SessionConfigurator: SessionConfigurator,
) =>
(
  Converter
    with type oldReq = OldHandler.hReq
    and type newReq = authSessReq<OldHandler.hReq, UserManager.user>
    and type oldRes = OldHandler.hRes
    and type newRes = authSessRes<OldHandler.hRes, UserManager.user>
)

module Make: Make

Author

Anatoly Starodubtsev tostar74@mail.ru

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i rescript-express-server-template

Weekly Downloads

0

Version

1.4.1

License

MIT

Unpacked Size

702 kB

Total Files

121

Last publish

Collaborators

  • pantagruel74