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

meseret

v1.9.2

Published

Opinionated, feature-packed professional node.js server framework.

Downloads

151

Readme

meseret

meseret npm downloads npm code style: prettier

Opinionated, feature-packed professional node.js server framework.

Meseret is a framework designed from the ground up to allow easy professional-grade node.js server configuration setup (code-wise).

Here are some of its features:

Server Setup:

  • Koa server with preconfigured compression, static serving & caching, body parsing (JSON and forms), direct JSON response, console logging and session support.
  • Support for more Koa middleware, and koa-router routes.
  • Listening on multiple HTTP and/or HTTPS servers.
  • Static-serving (hosting) multiple public directories.

Database Models:

  • MongoDB connection and Mongoose models.
  • A ModelFactory for type enabled Mongoose schema paths, methods and statics: bringing static-type support (and IDE auto-complete) to the data schema.
  • GridFS support to store small and large files in MongoDB.

WebSocket Support:

  • Socket.io integration support (connects SocketIO.Servers to the ServerApp).

Single Page Application Support:

  • Serves any SPA file.
  • Serves build files of SPA front-end projects, built using frameworks such as Angular and React, even when in different packages.
  • All 404 responses can be redirected to an SPA file, if specified.

Coding Style:

  • TypeScript everywhere.
  • Configuration-based architecture (using TypeScript code).

Installation

Inside a Node.js package, install meseret using:

Yarn:

yarn add meseret

or NPM:

npm install meseret --save

Language

Your application code is recommended to be written in TypeScript when you use this framework.

A Simple ServerApp

A simple app that makes Koa listen on port 3000 looks like:

import { ServerApp } from 'meseret'

new ServerApp({
  name: 'App Name',
  httpServers: [{ port: 3000 }]
}).start() // returns a Promise

A new ServerApp receives a configuration object (called IServerAppConfig) as a parameter. The start() method launches the server application on http://localhost:3000; it returns a Promise.

A Realistic Example

We are now going to create the back-end of a small "task organization" application using meseret. Its main source file looks something like:

// src/app.ts

import { ServerApp } from 'meseret'
import { join } from 'path'

import { TasksModel } from './models/tasks.model'
import { TaskRouter } from './routers/task.router'

const taskOrganizer = new ServerApp({
  name: 'Task Organizer',

  mongoUris: process.env.MONGO_URI || 'mongodb://localhost/task-organizer',
  models: [TasksModel],

  publicDirs: [join(process.cwd(), 'react', 'build')],
  spaFileRelativePath: join('react', 'build', 'index.html'),

  routers: [TaskRouter],

  httpServers: [
    {
      hostname: process.env.HOSTNAME || '127.0.0.1',
      port: Number(process.env.PORT) || 3000
    }
  ]
})

taskOrganizer.start().catch(console.error)

export { taskOrganizer }

// optionally, you may...
export const dbConn = () => taskOrganizer.dbConn // to access the mongoose connection
export const gfs = () => taskOrganizer.grid // to access GridFS from other files
export const app = () => taskOrganizer.app // the Koa application instance

In this code, we imported a ServerApp from meseret and created an instance (taskOrganizer) by passing it a (config: IServerAppConfig) as its first parameter. Then we called start() on taskOrganizer to launch our application based on the config we provided it. This configuration we passed to the ServerApp is the most important piece of code here. The job the ServerApp performs when start() is called will be discussed later in detail at the end of this example project.

Now we are moving our attention to the mongoose database model that's imported and used in taskOrganizer (the TasksModel). When it comes to models, it is recommended that we use the ModelFactory from meseret. Although this method is optional and relatively verbose when compared to pure mongoose, it provides support for static-typing and auto-completing mongoose models in IDEs, even deep down to the data schema level. The code for TasksModel looks something like:

// src/models/tasks.model

import { ModelFactory, FunctionsType } from 'meseret'

export interface ITasksSchemaPaths {
  desc: string
  done: boolean
}

export interface ITasksSchemaMethods extends FunctionsType {
  tickToggle: () => Promise<boolean>
}

export interface ITasksSchemaStatics extends FunctionsType {} // empty for now

const factory = new ModelFactory<
  ITasksSchemaPaths,
  ITasksSchemaMethods,
  ITasksSchemaStatics
>({
  name: 'tasks', // collection/model name

  paths: {
    desc: { type: String, required: true, trim: true },
    done: { type: Boolean, required: true, default: false }
  },

  methods: {
    async tickToggle(): Promise<boolean> {
      const task = factory.documentify(this) // for static-type support of the `this` in this document's context
      task.done = !task.done
      await task.save()
      return task.done
    }
  },

  statics: {
    // empty for now
    // `factory.modelify(this)` is available in functions here, for static-type support of the `this` in this model's context
  }
})

// optionally, you may manually also access the built schema
export const TasksSchema = factory.schema

// finally, create & export the model
export const TasksModel = factory.model
TasksModel.collection.createIndex({ '$**': 'text' }).catch(console.error)

In the code above, the ModelFactory is imported from meseret and used to create an instance called factory. It receives three types to support type-checks and auto-complete of the data schema here and elsewhere in the project. These types represent the mongoose schema's paths, methods and statics, respectively. In the code above, these types are interfaces, namely ITasksSchemaPaths, ITasksSchemaMethods and ITasksSchemaStatics in order.

As you can see above the methods' and statics' type interfaces extend FunctionsType from meseret. This is essential to guarantee that the mongoose method and static functions defined in the factory have valid signatures as their interface/type definitions above.

However, meseret does not currently support this guaranteed match between the type definition and actual implementation for schema paths (it's in the works... shh!). Until this gets support, developers should manually check if their path interfaces match their paths definition.

TIP: If you have an empty paths, methods or statics you may pass just {} to the ModelFactory. Therefore, our code above could have eliminated the ITasksSchemaStatics and the factory would have been defined as:

const factory = new ModelFactory<ITasksSchemaPaths, ITasksSchemaMethods, {}>({

Just like the ServerApp, meseret's ModelFactory receives an object (this time, whose type is IModelFactoryConfig). This object configures the name of the mongoose model (which is required) and, optionally, the paths, methods and statics for the model.

Inside the mongoose method and static function definitions, the this keyword represents the document and model, respectively. Meseret adds static-type support to these thiss using factory.documentify(this) and factory.modelify(this), respectively.

Finally, we see the factory.model code at the very last line. The ModelFactory's .model is a getter that generates a normal mongoose model based on the IModelFactoryConfig provided earlier. It is to be used elsewhere in our project, just like a normal mongoose model would have been.

Moving on...

Below is how we create the koa-routers used in the ServerApp. Nothing out of the ordinary here.

// src/routers/task.router

import * as Router from 'koa-router'
import { TasksModel } from '../models/tasks.model'

const TaskRouter = new Router({ prefix: '/api/task' })

// POST /api/task/new
TaskRouter.get('/new', async ctx => {
  ctx.body = await TasksModel.create(ctx.request.body)
})

// GET /api/task/all
TaskRouter.get('/all', async ctx => {
  ctx.body = await TasksModel.find({})
})

// GET /api/task/one/:_id
TaskRouter.get('/one/:_id', async ctx => {
  ctx.body = await TasksModel.findById(ctx.params._id)
})

// ... more route definitions

export { TaskRouter }

To recap, the above router (TaskRouter) and the model (TasksModel) are included in the ServerApp (taskOrganizer). When the taskOrganizer is started, it:

  1. connects to a MongoDB server at a specified MONGO_URI environment variable (or defaults to mongodb://localhost/task-organizer),
  2. loads configured Mongoose database models (TasksModel),
  3. launches an HTTP Koa server at a specified HOSTNAME and PORT environment variables (or defaults to http://127.0.0.1:3000),
  4. serves the static directory ./react/build/,
  5. serves an SPA from ./react/build/index.html, and
  6. handles requests that match definitions in TaskRouter.

Based on this and the default configuration, the started ServerApp implicitly takes care of:

In addition, a keys option can be provided to set Koa ctx.keys for signing cookies. If the keys are set, session support will be enabled automatically (using koa-session).

These features can be explicitly turned off (or modified) inside the config parameter of the ServerApp instance.

Besides the above built-in feature middleware packages, you may specify your own Koa middleware in the config to be used for each HTTP and/or HTTPS requests. You can also find the original Koa application instance using taskOrganizer.app, among many other variables.

Whew!

API

IServerAppConfig Options

The name option is the only required of all the IServerAppConfig options. Below is a list of all the available options:

| Option Name | Data Type | Description | | --------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | bodyParser? | boolean | Support form, JSON and text request body parsing? Defaults to true. | | bodyParserEnableForm? | boolean | If bodyParser is enabled, enable form parsing? Defaults to true. | | bodyParserEnableJson? | boolean | If bodyParser is enabled, enable JSON parsing? Defaults to true. | | bodyParserEnableText? | boolean | If bodyParser is enabled, enable text parsing? Defaults to true. | | bodyParserEncoding? | string | Encoding to use, if bodyParser is enabled. Defaults to 'utf-8'. | | bodyParserFormLimit? | string | Form size limit, if bodyParser is enabled. Defaults to '56kb'. | | bodyParserJsonLimit? | string | JSON size limit, if bodyParser is enabled. Defaults to '1mb'. | | bodyParserMultipart? | boolean | If bodyParser is enabled, enable multipart/form-data parsing to support standard file upload? Defaults to false. | | bodyParserTextLimit? | string | Text size limit, if bodyParser is enabled. Defaults to '1mb'. | | cacheControl? | string | Cache control to be used. Defaults to 'private'. | | cacheFiles | { [path: string]: staticCache.Options } | Set static cache options per file path. Optional. | | cacheOptions | staticCache.Options | Set global static cache options. Optional. May override IServerAppConfig.cacheOptions.cacheControl overrides IServerAppConfig.cacheControl. | | compress? | boolean | Compress responses? Defaults to true. | | httpServers? | { hostname?: string, port: number }[] | HTTP server configurations. | | httpsServers? | { opts: https.ServerOptions, hostname?: string, port: number }[] | HTTPS server configurations. | | json? | boolean | Support direct JSON response parsing? Defaults to true. | | jsonPretty? | boolean | If json is enabled, send pretty responses? Default to true only if app is in 'development' mode. | | jsonPrettyParam? | string | Optional query-string param for pretty responses, if json is enabled. | | jsonSpaces? | number | JSON spaces, if json is enabled and pretty. Defaults to 2. | | keys? | string[] | Sets Koa app.keys. | | log? | boolean | Log requests and responses? Defaults to true. | | middleware? | Koa.middleware[] | More Koa middleware to use. | | models? | mongoose.Model<mongoose.Document>[] | Mongoose models, optionally built using meseret's ModelFactory. Requires IServerApp.mongoUris. | | mongooseConnectionOptions | mongoose.ConnectionOptions | Set moongoose connection options. Optional. | | mongoUris? | string | MongoDB connection URIs. | | name | string | Name of the server application. It is required. | | publicDirs? | string[] | Directory paths to serve statically. | | routers? | KoaRouter[] | An array of koa-router routers used in the servers. | | spaFileRelativePath? | string | A relative path to an SPA file (e.g. an Angular or React build's index.html file). If this is unspecified (or null) the ServerApp will not have an SPA-like behavior of rerouting 404 Not Found pages. | | session? | boolean | Session support using cookies? Requires IServerAppConfig.keys. Defaults to true if some IServerAppConfig.keys are provided. | | sessionCookieKey? | string | Session cookie key, if session is enabled. Defaults to the name of the ServerApp in "snake_case". | | sessionHttpOnly? | boolean | If session is enabled, use it for HTTP only? Defaults to true. | | sessionMaxAge? | number or 'session' | Maximum valid age of the session in milliseconds, if session is enabled. Defaults to 86400000 (1 day). If it is set to the string value: 'session', the cookie expires when the session/browser is closed. | | sessionOverwrite? | boolean | If session is enabled, allow overwriting? Defaults to true. | | sessionRenew? | boolean | If session is enabled, renew it when nearing its expiry? Defaults to false. | | sessionRolling? | boolean | If session is enabled, force a session identifier cookie to be set on every response? Defaults to false. | | sessionSigned? | boolean | If session is enabled, should it be signed? Defaults to true. | | sockets? | SocketIO.Server[] | Socket.io servers used in the http servers. |

P.S. more API documentation is coming soon.

Licence

Made with ♥ in Addis Ababa.

MIT License © 2017-2018 Kaleab S. Melkie.