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

noren

v0.4.7

Published

A minimal HTTP server with good developer-experience and performance, for Node and beyond.

Downloads

53

Readme

Noren

A minimal HTTP server with good developer-experience and performance, for Node and beyond.

Features

  • Familar: it works pretty similarly to Express, with influences from Hono.
  • Lightweight: zero third-party dependencies are used, though I haven't written a multipart form data parser yet, so you'll need a middleware for that.
  • Adaptable: built-in adapters for Node and the Edge are provided, but others can be written quickly for any runtime.
  • Performant: unless you have a very large number of routes (the router is not a trie, yet) performance will be very good.

Install

npm install --save noren

Usage

Noren is divided into 4 main objects:

  • Server: the server routes requests and turns them into responses.
  • Req: the req object is the custom object for interacting with the network request.
  • Res: the res object is the custom object for generating a network response.
  • Middlewares: middlewares are chained functions that will be called by the router on requests.

Server

The server provides the following APIs:

// import Router from 'noren/router'' // Import just the low-level router
// import Server, {Req, Res} from 'noren/abstract'; // To write your own adapter
// import Server from 'noren/edge'; // The built-in Edge adapter
import Server from 'noren/node'; // The built-in Node adapter

// Create a new Node server

const app = new Server ();

// Register a custom global error handler

app.onError ( ( error, req, res ) => {
  // Do something...
});

// Register a custom global not-found handler

app.onNotFound ( ( req, res ) => {
  // Do something...
});

// Register a middleware for every route
// Each middleware can define an optional third argument, for reading the "next" function:
//   - If it does, the next middleware will be called when the next function is called
//   - If it doesn't, the next middleware will be called when the return of the current middleware resolves
// In any case middlewares are always fully awaited before the response is returned to the client

app.use ( async ( req, res, next ) => {
  const start = Date.now ();
  await next ();
  const elapsed = Date.now () - start;
  console.log ( `Request "${req.url.pathname}" served in: ${elapsed}ms` );
});

// Add a shorthand route

app.get ( '/foo', ( req, res ) => {
  // Req and Res are described in another section below
});

// Add a route for every verb

app.all ( '/all', ( req, res ) => {} );

// Add a route more manually, with a custom list of verbs, paths, and route-level middlewares

app.on ( ['POST', 'PUT'], ['/article/new', '/articles/new'], middleware1, middleware2, ( req, res ) => {} );

// Supported path features

app.get ( '/', handle ); // Root match
app.get ( '/:param([0-9]+)', handle ); // Named parameter with custom regex
app.get ( '/:param', handle ); // Named parameter
app.get ( '/:user/:section?', handle ) // Optional named parameter
app.get ( '/rpc/*', handle ); // Wildcard match
app.get ( '/a*b', handle ) // Zero-or-more modifier
app.get ( '/a+b', handle ) // One-or-more modifier
app.get ( '/a?b', handle ) // Zero-or-one modifier
app.get ( '/some-(foo|bar)', handle ) // Alternation

// Listening on a port

app.listen ( 4000 );

// Get the used port -- useful when setting the port to 0, which lets the OS pick one

app.port ();

// Stopping listening

app.close ();

Req

The req object provides the following APIs:

// Instance variables

req.body; // Uint8Array containing the payload of the request
req.cookies; // Object containing all received cookies
req.credentials; // Object containing username and password, if any
req.environment; // Object containing environment variables
req.headers; // Object containing all request headers
req.ip; // String containing the first "X-Forwared-For" address, if any
req.ips; // Array containing all "X-Forwared-For" addresses
req.method; // String of the HTTP verb used
req.params; // Object containing path parameters
req.path; // The requested pathname
req.signal; // AbortSignal for the request, if any
req.url; // URL object for the request

// Methods for reading details of the request

req.cookie ( key ); // Get a cookie by name, if any
req.env ( key ); // Get an environment variable by name, if any
req.header ( key ); // Get a header by name, if any
req.param ( key ); // Get a path parameter by name, if any
req.query ( key ); // Get a search query parameter by name, if any
req.queries ( key ); // Get all search query parameters by name

// Methods for reading the body of the request

req.arrayBuffer (); // Read the body as an ArrayBuffer
req.blob (); // Read the body as a Blob
req.formData (); // Read the body as a FormData
req.json (); // Read the body as a JSON value
req.text (); // Read the body as a string

Res

The res object provides the following APIs:

// Instance variables

res.ended; // Whether the response has been manually .end()-ed or not
res.headers; // Object containing all current response headers
res.statusCode; // Number representing the returned status code
res.body; // String or Uint8Array containing the response payload, if any

// Methods for logging, efficiently

res.log.error ( 'Message' ); // Log an error-level message
res.log.warn ( 'Message' ); // Log a warning-level message
res.log.info ( 'Message' ); // Log an info-level message
res.log.debug ( 'Message' ); // Log a debug-level message

// Methods for writing details of the response

res.append ( key, value ); // Append a header
res.code ( code ); // Set the status code
res.cookie ( key, value, options ); // Set a cookie
res.header ( key, value ); // Set a header
res.redirect ( url, status ); // Redirect to a url with a status code
res.status ( code, message ); // Set the status code and a textual response body
res.type ( type ); // Set the MIME type of the response body

// Methods for writing the body of the response

res.html ( value ); // Set an HTML string as the response body, with proper MIME type
res.json ( value ); // Set a JSON value as the response body, with proper MIME type
res.text ( value ); // Set a plain string as the response body, with proper MIME type
res.send ( value ); // Set a string or Uint8Array as the response body, with no automatic MIME type

// Other methods

res.end (); // This method stops execution of middlewares, even if the `next()` function is called

Middlewares

Middlewares are used like this:

import Server from 'noren/node';

const app = new Server ();

// Creating a custom middleware

const customMiddleware = () => {
  return async ( req, res, next ) => {
    // Maybe do something before executing the other middlewares...
    await next (); // Waiting for the other middlewares to execute
    await next ( new Error () ); // Exiting immediately, unsuccessfully, with an error
    await next ( true ); // Exiting immediately, successfully, skipping other middlewares
    // Maybe do something after executing the other middlewares...
  };
};

// Registering a global middleware

app.use ( customMiddleware () );

// Register a route-level middleware

app.get ( '/foo', customMiddleware (), ( req, res ) => {
  // Handle the request...
});

The following built-in middlewares are provided:

import {basicAuth, cors, etag, favicon, logger, poweredBy, serveStatic} from 'noren/middlewares';
import Server from 'noren/node';

const app = new Server ();

// Putting routes under Basic authentication

app.use ( basicAuth ({
  users: [
    {
      username: 'aladdin',
      password: 'opensesame'
    },
    {
      username: 'hermione',
      password: 'alohomora'
    }
  ]
}));

// Allowing CORS requests (no options are implemented at the moment, this middleware is super basic)

app.use ( cors () );

// Returning ETag headers, only strong etags are supported, which are somewhat expensive to generate

app.use ( etag () );

// Serve requests for "/favicon.ico" quickly from memory

app.use ( favicon ( './public/favicon.ico' ) );

// Log requests to the console, with very little overhead

app.use ( logger () );

// Add the X-Powered-By header, with "Noren" as the value, to every response

app.use ( poweredBy () );

// Serve static files from a folder (this middleware is pretty basic, not intended for production use)

app.use ( serveStatic ( '/public', {
  dotfiles: true, // Serve dot files, and files in dot folders, also
  fallback: true, // Pass the request to the next middleware instead of failing if the file is not found
  immutable: true, // Mark the returned response as immutable
  maxAge: 1000 // Set the "Cache-Control" header to "max-age=1000"
}));

License

MIT © Fabio Spampinato