nodecaf
Version:
Nodecaf is a light framework for developing RESTful Apps in a quick and convenient manner.
293 lines (260 loc) • 12.4 kB
TypeScript
import { Server, ServerResponse, IncomingMessage } from 'http'
import WebSocket from 'ws'
declare namespace Nodecaf {
type ConfObject = {
/** Controls logging output. */
log?: {
/** Define fields to be added to all log entries */
defaults?: Record<string, unknown>,
/** Only output log entries with specified `level` or above */
level?: 'debug' | 'info' | 'warn' | 'error' | 'fatal',
/** Only output log entries matching any `type` */
only?: string | string[],
/** Only output log entries not matching any `type` */
except?: string | string[]
},
/** Creates an HTTP server that will be managed on the given port. */
port?: number
} & Record<string, unknown>
type RunOptions = {
/** Single or array of Conf object or file path */
conf: ConfObject | string | (ConfObject | string)[],
/** Path to the nodecaf module to be run */
path: string
}
class Logger {
debug(...args: unknown[]): void
info(...args: unknown[]): void
warn(...args: unknown[]): void
error(...args: unknown[]): void
fatal(...args: unknown[]): void
}
type CookieOpts = {
expires?: Date,
maxAge?: number,
/**
* @deprecated Setting `signed` cookies is deprecated. This option will be dropped on `v0.14.0`. Cookie signing must be done manually instead.
*/
signed?: boolean,
path?: string,
domain?: string
secure?: boolean
httpOnly?: boolean
overwrite?: boolean
sameSite?: "Strict" | "Lax" | "None"
}
class Response extends ServerResponse {
/** In case `cond` is falsy, throws HTTP error with `status` and `message` as body printf-formated with `args` */
assert(status: number, cond: boolean, message?: string, ...args: unknown[]): this
/** In case `cond` is falsy, throws Error 400 with `message` as body printf-formated with `args` */
badRequest(cond: boolean, message?: string, ...args: unknown[]): this
/** In case `cond` is falsy, throws Error 401 with `message` as body printf-formated with `args` */
unauthorized(cond: boolean, message?: string, ...args: unknown[]): this
/** In case `cond` is falsy, throws Error 403 with `message` as body printf-formated with `args` */
forbidden(cond: boolean, message?: string, ...args: unknown[]): this
/** In case `cond` is falsy, throws Error 404 with `message` as body printf-formated with `args` */
notFound(cond: boolean, message?: string, ...args: unknown[]): this
/** In case `cond` is falsy, throws Error 409 with `message` as body printf-formated with `args` */
conflict(cond: boolean, message?: string, ...args: unknown[]): this
/** In case `cond` is falsy, throws Error 410 with `message` as body printf-formated with `args` */
gone(cond: boolean, message?: string, ...args: unknown[]): this
/** In case `cond` is falsy, throws Error 415 with `message` as body printf-formated with `args` */
badType(cond: boolean, message?: string, ...args: unknown[]): this
/** Respond with an HTTP error in `status` and `message` as body printf-formated with `args` */
error(status: string | number, message?: string, ...args: unknown[]): void
/** Respond with an Error 500 */
error(status: unknown): void
/** Append `chunk` to the response stream. */
write(chunk: string | Buffer): this
/** Finishes the request. If set, append `body` to the response stream. */
end(body?: string | Buffer): void
/** Respond with a json body and finishes the request. */
json(data: unknown): void
/** Respond with a text body and finishes the request. */
text(data: number | boolean | string): void
/** Set a request header. */
set(k: string, v: string): this
/** Append the header to the response. Allow sending duplicated header keys */
append(k: string, v: string): this
/** Set content-type header to a know type (json, text, urlencoded) or any mime-type */
type(ct: string): this
/** Send the request status to client. */
status(s: number): this
/** Set a cookie according to options. */
cookie(name: string, value: string, opts?: CookieOpts): this
/** Clear the cookie identified by `name` and `opts`. */
clearCookie(name: string, opts?: CookieOpts): this
}
class RequestBody extends IncomingMessage {
raw(): Promise<Buffer | unknown>
text(): Promise<string>
urlencoded(): Promise<Record<string, string>>
json(): Promise<unknown>
parse(): Promise<unknown>
}
type RouteHandlerArgs = {
/** Object containing request headers as key-values. */
headers: Record<string, string>,
/** Object containing request URL query string as key-values. */
query: Record<string, string>,
/** A logging utility to output JSON lines to stdout. */
log: Logger,
/** Request body object (in case `opts.autoParseBody` is `true`, will contain the parsed data instead). */
body: RequestBody | unknown,
/** Request URL path. */
path: string,
/** Request HTTP method. */
method: 'POST' | 'DELETE' | 'PATCH' | 'PUT' | 'GET',
/** Response object used to compose a response to the client. */
res: Response,
/** Call `fn` with the request handler args as the first parameter and spreading `args`. */
call: <T>(fn: (input: RouteHandlerArgs, ...args: unknown[]) => T, ...args: unknown[]) => T
/** The current app configuration. */
conf: ConfObject,
/** Object containing the request cookies as key-values. */
cookies: Record<string, string>,
/**
* Object containing the request signed cookies as key-values.
* @deprecated `signedCookies` is deprecated. This option will be dropped on `v0.14.0`. Signed cookies must be handled manually instead.
**/
signedCookies: Record<string, string>,
/** Object containing params parsed from URL segments as key-values. */
params: Record<string, string>
/** The remote address of the client performing the request. Standard proxy headers are considered. */
ip: string,
/** Store `value` under the name `key` in the handler args for the lifetime of the request. */
keep: (key: string, value: unknown) => void,
/** Accept WebSocket connection on upgrade. Only available when `opts.websocket` is set. */
websocket: () => Promise<WebSocket.WebSocket>
} & Record<string, unknown>
type RouteHandler = (this: Nodecaf, input: RouteHandlerArgs) => Promise<void> | void
type EndpointBuilders = {
post: (path: string, handler: RouteHandler) => void,
put: (path: string, handler: RouteHandler) => void,
patch: (path: string, handler: RouteHandler) => void,
get: (path: string, handler: RouteHandler) => void,
del: (path: string, handler: RouteHandler) => void,
all: (handler: RouteHandler) => void
}
type Route = {
/** Endpoint HTTP method */
method: string,
/** Endpoint path starting with slash (e.g `/foo/:bar`) */
path: string,
/** Function to be called when endpoint is triggered */
handler: RouteHandler
}
type AppOpts = {
/** An array with your api endpoints */
routes: Route[],
/**
* A function to build your api endpoints
* @deprecated This option will be removed on `v0.14.0`. Use `routes` instead.
**/
api?: (this: Nodecaf, methods: Nodecaf.EndpointBuilders) => void,
/** A function to run whenever the app is starting */
startup?: (args: Nodecaf) => Promise<void> | void,
/** A function to run whenever the app is stopping */
shutdown?: (args: Nodecaf) => Promise<void> | void,
/** App name, mainly used in log entries */
name?: string,
/** App version, mainly used in log entries */
version?: string,
/** Default config object or file path */
conf?: Nodecaf.ConfObject | string,
/** Whether request bodies should be parsed for known mime-types (json, text, urlencoded). Defaults to `false`. */
autoParseBody?: boolean,
/** A function that returns a custom HTTP server to be used by the app */
server?: (args: Nodecaf) => Server,
/** Whether to handle websocket upgrade requests. Defaults to `false`. */
websocket?: boolean
}
}
/**
* A light RESTful App
*
* Example usage:
* ```js
* const app = new Nodecaf({
* api({ get }){
* get('/foo', function({ res }){
* res.text('bar');
* });
* }
* });
* await app.start();
* const { status, body } = await app.trigger('get', '/bar');
* console.log(status, body);
* await app.stop();
* ```
*/
declare class Nodecaf {
/** Define a POST endpoint to `path` that when triggered will run the `handler` function */
static post(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route
/** Define a PUT endpoint to `path` that when triggered will run the `handler` function */
static put(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route
/** Define a PATCH endpoint to `path` that when triggered will run the `handler` function */
static patch(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route
/** Define a GET endpoint to `path` that when triggered will run the `handler` function */
static get(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route
/** Define a DELETE endpoint to `path` that when triggered will run the `handler` function */
static del(path: string, handler: Nodecaf.RouteHandler): Nodecaf.Route
/** Define a fallback `handler` function to be triggered when there are no matching routes */
static all(handler: Nodecaf.RouteHandler): Nodecaf.Route
/**
* Run a given nodecaf app handling uncaught errors and node process signals
* @deprecated This function will be dropped on `v0.14.0`. Use `app.run()` instead.
*/
static run(opts: Nodecaf.RunOptions): void
/** A user controlled object whose properties wil be spread in route handler args. */
global: Record<string, unknown>
/** The current app configuration. */
conf: Nodecaf.ConfObject
/** A logging utility to output JSON lines to stdout. */
log: Nodecaf.Logger
/** Call `fn` with the app global args as the first parameter and spreading `args`. */
call: <T>(fn: (app: Nodecaf, ...args: unknown[]) => T, ...args: unknown[]) => T
/**
* Creates a new instance of an app in standby.
*/
constructor(opts: Nodecaf.AppOpts)
/**
* Run a standby app. The returned `Promise` is resolved after the startup is
* complete.
*/
start(): Promise<'running' | 'starting'>
/**
* Stop a running app. The returned `Promise` is resolved after the shutdown
* is complete.
*/
stop(): Promise<'standby' | 'stopping'>
/**
* Restart a running app, applying configuration if sent. The returned
* `Promise` is resolved once the app is fully started up.
*/
restart(conf: Nodecaf.ConfObject | string): Promise<void>
/**
* Apply configuration from an object or reading from a config file in one
* of the supported formats (JSON, TOML, YAML).
*/
setup(...conf: (Nodecaf.ConfObject | string)[]): void
/**
* Trigger an app endpoint with given input data. Returns a `Promise`
* resolving to the normalized response data.
*/
trigger(method: string, path: string, input?: {
body?: BodyInit,
headers?: Record<string, string>,
query?: Record<string, string>,
cookies?: Record<string, string>
}): Promise<{
status: number,
headers: Record<string, string>,
body: unknown
}>
/**
* Run the app handling uncaught errors and node process signals.
*/
run(opts: Nodecaf.RunOptions): Promise<Nodecaf>
}
export = Nodecaf