npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

rescript-express-server-template

v1.4.1

Published

rescript express server

Downloads

6

Readme

rescript-express-server-template

extemdable rescript server and routes builder

Depends

  • rescript-web-types: https://github.com/mnemesong/rescript-web-types

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 [email protected]

License

MIT