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

curie-server

v2.4.5

Published

A modular Node.js http/1.0 framework

Downloads

3

Readme

Curie

A modular Node.js http/1.0 framework

Table of Content

  1. Config
  2. File Structure
    1. Main File
    2. Database File
    3. Listeners
    4. Middleware
    5. Route Parser
    6. Template Files
    7. Single File Alternative
  3. Classes
    1. DBridge
    2. Listener
    3. Middleware
  4. Types
    1. Request
    2. Response
    3. Server Parameters
    4. CallbackReturnType
  5. Helpers
    1. Logging
    2. PreRun commands

Config

Curie-server accepts a number of configuration options of the type:

interface ServerParams {
  routes: string
  routeParser: ClassConstructor<RouteParser>
  public: string
  port: number
  listeners: [string, string | RegExp]
  middleware: [string, string | RegExp]
  database: string
  preRun: string[]
  root: string
}

Keep in mind that all the paths are relative to the main file

File structure

curie-server supports a multi file structure. An example file structure:

|public/
|--css/
|----main.css
|--js/
|----main.js
|routes/
|--index.pug
|listeners/
|--index.list.ts
|middleware/
|--logger.mdw.ts
|index.ts
|database.ts

Main file

The main file is the file, which is responsible for starting your application.

import { initApp } from "curie-server"
initApp({
  port: 8000,
  public: "../public",
  routes: "../routes",
  listeners: ["./listeners", "list.[jt]s"],
  middleware: ["./middleware", "mdw.[jt]s"],
  database: ""
})

Database file

The database file is responcible for connecting with your database. It should export a class which extends the DBridge class. You may create your own class or use the so-called out of the box PotgreSQL DBridge. It should look something like it:

import { PostDBridge, database } from "curie-server"

@database("postgres://postgres:[email protected]:5432/postgres")
export default class extends PostDBridge {}

Listeners

Listeners are responsible for responding to incoming http requests. Both their location and extension are specified in the Server constructor parameters in the main file. A listener should extend the Listener class, implement onGET and/or onPOST method(s), such that they return CallbackReturnType. Example:

import c, { Listener, hookup } from "curie-server";

@hookup("/")
export default class extends Listener {
  async onGET(req: c.Request, res: c.Response): c.CallbackReturnType {
    this.server.routeParser.render(res, "index")
    return [null, false]
  }
}

Middleware

Middleware is responcible for interrupting incoming requests and can even reject them. Middleware should extends the Middleware class and return the CallbackReturnType. It should look something like this:

import { CallbackReturnType, Middleware, withTime, Request, Response, c_log, use } from "curie-server";

@use()
export default class extends Middleware {
  async intercept(req: Request, res: Response) {
    c_log(withTime(`[LOGGER]> ${req.method}: ${req.url || ""}`))
    return [null, true] as CallbackReturnType
  }
}

RouteParser

The RouteParser is responsible for parsing and rendering template files. If you want to use the lamplating language of your choice, you should provide a class that extends the RouteParser.

abstract class RouteParser<RouteType = any> {
  path: string
  routes: LooseObject<RouteType>
  server: Server
  public constructor(path: string, server: Server) {/*...*/}
  abstract compile(route: string): CallbackReturnType
  abstract async compileAll(): Promise<CallbackReturnType>
  abstract async render(res: Response, route: string, locals?: LooseObject): Promise<CallbackReturnType>
}

Out of the box curie-server is providing support for the pug templating lang.

Routes

Routes are themplates rendered by the RouteParser. Out of the box you get the PugParser, which compiles .pug files and allows you to query items from the database (template: //# $<variable_name>: <query>).

//# $posts: SELECT * FROM posts

<!DOCTYPE html>
html(lang="en")
  head 
    // ...
  body
    ul.posts
      for post in posts
        li.posts__post
          h2.posts__post__title= post.title
          p.posts__post__body= post.body

Single file approach

While I highly advise you to take the advatnage of the multi file structure, around which the curie-server was built, you can fit everything into a single file.

import c, { Server, PostDBridge, Listener, Middleware, c_log, withTime, initApp, database, hookup, use } from "curie-server";

(async () => {
  await initApp({
   port: 8000,
   public: "../public",
   routes: "../routes",
   listeners: ["./listeners", "list.[jt]s"],
   middleware: ["./middleware", "mdw.[jt]s"],
   database: ""
  })

  @database("postgres://postgres:[email protected]:5432/postgres")
  class Db extends PostDBridge {}

  @hookup("/")
  class IndexListener extends Listener {
    async onGET(req: c.Request, res: c.Response) {
      await this.render(res, "index")
      return [null, false] as c.CallbackReturnType
    }
  }
  @use()
  class Logger extends Middleware {
    async intercept(req: Request, res: Response) {
      c_log(withTime(`[LOGGER]> ${req.method}: ${req.url || ""}`))
      return [null, true] as c.CallbackReturnType
    }
  }
})()

Classes

DBridge

@database("<connection_uri>")
class MyDBridge extends DBridge<DBInterface, QueryType> {
  async get(query: QueryType): Promise<LooseObject[] | any> {
    // Fetch the query! Good boy!
    return getResponse
  }
  async update(query: QueryType): Promise<UpdateResponse | any> {
    // Update something...
    return updateResponse
  }
  async delete(query: QueryType): Promise<DeleteResponse | any> {
    // Delete something...
    return deletionResponse  
  }
  async create<T extends ClassConstructor>(model: T, data: ConstructorParameters<T>): Promise<CreateResponse | any> {
    // Create something...
    return creationResponse
  }
}

Listener

@hookup("/")
class IndexListener extends Listener {  
  async onGET(
    req?: Request, 
    res?: Response
    ): Promise<CallbackReturnType | undefined> {
      return [null, true]
    }
  async onPOST(
    req?: Request, 
    res?: Response
    ): Promise<CallbackReturnType | undefined> {
      return [null, true]
    }
}

Middleware

@use()
export default class Intercepter extends Middleware {
  async intercept(req?: Request, res?: Response) {
    // Do something
    return [null, true] as CallbackReturnType
  }
}

Interfaces and types

LooseObject

An implementation of a key-value map.

interface LooseObject<T = any> {
  [key: string]: T
}

Request

interface Request extends http.IncomingMessage {
  query: LooseObject<string>
  cookies: cookies
  body: LooseObject
}

Response

interface Response extends http.ServerResponse {
  cookies: cookies
}

ServerParams

It is a configuration object passed to the Server constructor.

interface ServerParams {
  routes: string,
  routeParser: ClassConstructor<RouteParser>
  public: string
  port: number
  listeners: [string, string | RegExp] // [path_to_dir, extension]
  middleware: [string, string | RegExp] // [path_to_dir, extension]
  database: string
  root: string
  preRun: string[]
  [key: string]: any | any[]
}

Server.DEFAULT_SERVER_OPTIONS: ServerParams = {
    routes: "./routes",
    routeParser: PugParser,
    public: "./public",
    port: 8000,
    listeners: ["./", "list.[jt]s"],
    middleware: ["./", "mdw.[jt]s"],
    database: '',
    preRun: [],
    root: path.dirname(require.main.filename)
  }

CallbackReturnType

CallbackReturnType is a value returned by many curie-server functions, but is used especially in the Listener and the Middleware classes. The first part of the tuple is the Error, and the 2nd one is the ShouldContinue boolean, which tells the inner loop whether is should send the Response to the client or continue.

type CallbackReturnType = [Error | null, boolean]

Helpers

Logging

curie-server comes with a plethora of tools to help you log info.

const log = (text: any, color: keyof ChalkColors) =>
  console.log(
    ((chalk[color] as any) as Executable<string>)(
      typeof text === "object" ? JSON.stringify(text) : text
    )
  )
const c_log = (text: string) => log(text, "yellowBright")

const initLogger = 
  (name: string, color: keyof ChalkColors): LoggerFunction 
  => (text: string) 
  => log(withTime(`[${name}]> ${text}`), color)

PreRun commands

initApp accepts a parameter called preRun. It's simply an array of commands to run before the rest of initialization (loading files, setting up events etc.). Errors won't terminate the main process, but only propagate the error message to the console (same with stdout).

initApp({
  // ...
  preRun: [
    "tsc"
  ]
})

In the example, the tsc command will run before loading files to a memory, thus allowing you to compile i.e. typescript files.

CLI

curie-server listens for a console input and evaluates it upon pressing Enter. Errors won't terminate the main process. this refers to the Server instance.