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

ohz

v0.1.2

Published

An Observable-based HTTP server

Downloads

3

Readme

Obvi

An HTTP server based on Observables

Usage

To see an example server running, install the repo ( yarn install ), and then run the server ( yarn dev ). This will start the server at the example.ts file.

Below is a stripped down version of using ohz with a vanilla server:

const { router, json, server: createServer } = require('ohz')

const server = createServer()

server
    .use(json())
    .use(router.route('/posts/:id', ctx => ({
        ...ctx,
        body: {
            query: ctx.request.query,
            param: ctx.request.params
        }
    })))
    .listen(5000, () => console.log('listening'))

Below is a vanilla JavaScript usage example with detailed comments:

// We are going to be using the map operator
// to make our middleware easier to write
const { of } = require('rxjs')
const { map } = require('rxjs/operators')
// We also want to import some middleware (router, json), 
// along with our server creator
const { router, json, server: createServer } = require('ohz')

// We create a server instance
// It has some basic generics that
// can get us up and running quickly
// so if you just want to see how it works
// you can do
//
// server = createServer()
// 
// without passing a configuration object
const server = createServer({
    // but if you want to get specific, we can
    // inject our own handlers into the system.
    // 
    // createContext is the first lego piece in our
    // system. It takes in a Subject<Context> and 
    // returns a function that takes in (http.ClientRequest, http.ServerResponse)
    // and emits an event into the Subject, with a value of Context.
    //
    // The `headers` value is not setting the _incoming_ headers
    // but setting the _outgoing_ headers.
    //
    // Below, we take in a generic subject and emit a generic context
    // but if you need to do more here, be sure that you include the
    // parsing of url and query or else router and other downstream
    // values will not behave as expected!
    createContext: sub => (request, response) => {
        const parsedUrl = url.parse(request.url)

        Object.assign(request, {
            url: parsedUrl, // { pathname: '/path/without/query' }
            query: parsequery(parsedUrl.search) // { query: args }
        })

        sub.next({
            request,
            response,
            headers: {},
            body: null
        })
    },
    // This is mostly a placeholder. As of now, the default
    // is the return an empty object. The idea is that you
    // can add values to the `server` that is returned
    // each time you call `server.use` or `server.remove`
    // or to override `use` and `remove` yourself.
    // 
    // I probably wouldn't use this but it's nice to have the
    // option to!
    createResult: () => ({})
})


server
    // Just like connect, we can call `use` to add middleware
    // to our server instance. They take a different signature
    // though! Instead of `(ctx, next) => void` or
    // `(req, res, next)` => void,
    // this middleware is of type
    // (ctx) => Observable<Context>.
    //
    // We have included some basic middlewares in order to 
    // get some basic boilerplate taken care of. `json` is
    // a module that takes in nothing and returns middleware
    // that handles incoming JSON data along with outgoing
    //
    // It does assume that the end-user is calling `send`
    // added by this middleware. If you change your subscribeFn
    // when you `listen`, you might get different results.
    .use(json())
    // We also have a way to add routes based on express-like urls
    // router.route takes in an express-like url and a handler. The
    // handler can either return a new Context value or return an 
    // Observable
    //
    // router.route will handle _all_ methods to that route.
    .use(router.route('/users', (ctx) => {
        // Here we return `of(...)` to show
        // that you can return Observables.
        // Remove `of` and just return a new
        // context value. It will work the same!
        return of({
            ...ctx,
            // We set the `body`
            // of the Context for the downstream
            // handlers to care about
            body: {
                data: [
                    {
                        _id: 1,
                        // We can also read from the incoming
                        // requests. This is assuming that the
                        // client has added a JSON body and it
                        // has a user value
                        name: ctx.request.body.user
                    }
                ]
            }
        })
    }))
    // If you want to only respond to get requests,
    // we can use router.get, which also takes a 
    // route and handler but will only respond
    // to get requests to that endpoint
    .use(router.get('/posts', (ctx) => ({
        ...ctx,
        body: {
            data: true
        }
    })))
    // Just like express, we can use named parameters
    // in our routes. This will be called when we have
    // the url /posts/1234.
    // 
    // As you can see in the body of this handler, we
    // have access to the params ( such as id ), along
    // with they query (  )
    .use(router.get('/posts/:id', (ctx) => ({
        ...ctx,
        body: {
            data: 'you did it!',
            meta: {
                params: ctx.request.params,
                query: ctx.request.query
            }
        }
    })))
    // We can also add generic Pipe-able functions
    // by using rxjs/operators. map(fn) is the same
    // as writing a function of Obs => Obs.map(fn)
    .use(map(({ body, headers, ...args }) => {
        if (!body) {
            return ({
                ...args,
                body: JSON.stringify({
                    error: {
                        message: 'Route not found'
                    }
                }),
                headers: {
                    ...headers,
                    code: 404,
                    'Content-Type': 'application/json'
                }
            })
        }

        return ({
            body,
            headers,
            ...args,
        })
    }))
    // Once we call listen, we can no longer add/remove middleware
    // 
    // We give listen a port, callback function, and subscribe function
    // 
    // port is the port to listen on
    // callback is the function to be called when the server is
    //      actually listening on that port
    // subscribe is a function that takes a Context and responds
    //      to the eventual value
    .listen(
        // Some random port will do, as long as it's not in use
        5000,
        // We just want to know when the server is ready!
        () => console.log('Listening at http://localhost:5000'),
        // This is the default subscription function.
        // 
        // We just take the context, try to use some `send`
        // method if the upstream set it ( like json() does )
        // or we write the headers, set the code, and write the 
        // body to the client, ending the conneciton.
        //
        // If you want to do something differently, you can change this
        // subscription function and handle the response however
        // you need to. If you just want to be able to set a string as
        // the value to be sent, don't include anything. If you want
        // to set JSON-able data, use the `json()` middleware as above.
        ({ request, response, body, headers = {} }) => {
            // the user has a custom handler
            if (response.send) {
                response.send(headers, body)
            } else {
                // default to string
                // Set headers
                response.writeHead(headers.code || 200, headers)
                // Assume middleware took care of the body
                response.end(body)
            }
        }
    )