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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xitkov/core

v2.7.1

Published

TypeScript backend meta-framework on Express. Convention-over-configuration REST APIs, dual database dialect (MongoDB + Postgres), microservice HTTP clients, hooks, auth, cache, queue, sockets — one dependency.

Readme

Xitkov

TypeScript backend meta-framework on Express. Convention-over-configuration REST APIs, dual database dialect (MongoDB + Postgres), microservice HTTP clients, hooks, auth, cache, queue, sockets — one dependency.

Install

npm install xitkov

Quickstart

Project layout:

src/
├─ index.ts
├─ services/
│  └─ users/
│     ├─ service.ts     # extends XitkovService
│     ├─ model.ts       # mongoose or sequelize model
│     ├─ validator.ts   # optional Joi schemas
│     └─ hooks.ts       # optional per-method hooks
└─ micro-services/
   └─ billing.ts        # optional external service client

Nested services

A service folder inside another is served beneath it. Position is the only declaration needed:

services/
├─ skills/
│  ├─ service.ts        # /skills
│  └─ versions/
│     └─ service.ts     # /skills/:parentId/versions
└─ conversations/
   ├─ service.ts        # /conversations
   └─ messages/
      └─ service.ts     # /conversations/:parentId/messages

Read the parent from req.params.parentId, and address the service by its path: Services.get('skills.versions').

A folder with no service module is a grouping folder, not a service; the services inside it are still found.

src/index.ts:

import express from 'express';
import { Xitkov } from 'xitkov';

const app = express();

Xitkov.create({
  app,
  database: { dialect: 'mongodb', instance: mongooseConnection },
  auth: { required: true, baseURL: 'https://auth.example.com' },
  caching: { redisUrl: 'redis://localhost:6379', keyPrefix: 'app:' },
  socket: { enable: true, socketCorsOrigin: '*' },
});

src/services/users/service.ts:

import { XitkovService } from 'xitkov';
import Model from './model';

export default class UserService extends XitkovService<User> {
  constructor() {
    super({ Model, dialect: 'mongodb' });
  }
}

Boots with REST routes:

| Method | Path | Handler | |--------|------|---------| | GET | /users | find | | GET | /users/:id | get | | POST | /users | create | | PATCH | /users/:id | patch | | PUT | /users/:id | update | | DELETE | /users/:id | remove |

Core APIs

Xitkov

Bootstrapper. Registers services, wires cache/socket/logger, listens on PORT.

new Xitkov(config)              // fire-and-forget, use `xitkov.ready`
await Xitkov.create(config)     // async factory, resolves after boot

Config

| Field | Type | Purpose | |-------|------|---------| | app | Application | Existing Express app | | database.dialect | 'mongodb' \| 'postgres' | Backing store | | database.instance | any | Sequelize instance (postgres) | | auth.required | boolean | Enforce JWT verify on every route | | auth.baseURL | string | Verify endpoint (POST /verify) | | caching.redisUrl | string | Redis URL for Cache | | caching.keyPrefix | string | Key namespace | | socket.enable | boolean | Boot socket.io | | socket.redis | { url?, redisOptions? } | Redis pub/sub adapter | | socket.socketCorsOrigin | string | Socket CORS | | logger.express | LoggerOptions | express-winston passthrough | | express.limit | string | Body-parser limit (default 50mb) |

XitkovService<T>

Base class every service extends. Wraps dialect-specific implementation.

Methods: find, get, create, patch, update, remove — plus underscored bypass variants _find … _remove (skip hooks + validators when called internally).

Instance flags:

  • pagination?: boolean — postgres findAndCountAll
  • filtering?: boolean — toggle addQuerySupport translation (default on)
  • auth?: boolean — set false to disable auth on this service

MicroService

Typed Axios client with the same CRUD shape. Auto-created for every entry in /micro-services.

export default class BillingService {
  name = 'billing'
  baseURL = process.env.BILLING_URL!
  services = ['invoices', 'customers']
}

// then anywhere:
const invoice = await MicroServices.get('billing', 'invoices').get('inv_1');

Per-call options:

await MicroServices.get('billing', 'invoices').get(id, undefined, {
  axiosConfig: { timeout: 5000 },
  exception: { handle: true, defaultValue: null },
});

Hooks

Drop hooks.ts next to a service; export default class with method arrays:

import { setBusinessId, loggedIn } from 'xitkov';

export default class UserHooks {
  all = [loggedIn]
  find = [setBusinessId]
  create = [setBusinessId]
}

Built-in hooks: disallow, externalDisallow, loggedIn, setBusinessId, addQuerySupport.

Errors

All extend Error. Throw them; framework maps to HTTP:

import { BadRequest, NotFound, Unauthorized } from 'xitkov';

throw new NotFound('User not found');

Classes: BadRequest (400), NoContent (204), Unauthorized (401), PaymentRequired (402), Forbidden (403), NotFound (404), InternalServer (500).

Cache

Redis wrapper (ioredis).

import { Cache } from 'xitkov';

await Cache.set('key', value, 300);   // TTL in seconds
const value = await Cache.get('key');
await Cache.delete('key');

Queue

Bull wrapper with env-scoped names (name_dev, name_staging, bare in prod).

import { Queue } from 'xitkov';

const emails = new Queue<{ to: string }>('emails', process.env.REDIS_URL!);
await emails.add({ to: '[email protected]' });
emails.consume(async job => sendEmail(job.data));

Socket

Socket.io helper. Redis adapter attached when socket.redis provided.

import { Socket } from 'xitkov';

Socket.emit('event', payload);
Socket.client(socket => {
  socket.on('ping', () => socket.emit('pong'));
});

Logger

Winston-backed static class. Local dev logs to console; prod to structured JSON.

import { Logger } from 'xitkov';

Logger.info('boot', { port });
Logger.error('failed', err);

Events

import { XitkovEvents, EVENTS } from 'xitkov';

XitkovEvents.on(EVENTS.SERVICES_REGISTERED, () => {});
XitkovEvents.on(EVENTS.SERVER_STARTED, port => {});

Query operators (postgres)

addQuerySupport translates $op shorthand → Sequelize Op:

GET /users?age[$gte]=18&status[$in]=["active","pending"]

Reserved: $limit, $offset for pagination.

Environment variables

| Var | Default | Purpose | |-----|---------|---------| | PORT | 3000 | HTTP listen port | | NODE_ENV | — | Set local to disable Winston, use console | | ENVIRONMENT | dev | Bull queue name suffix (prod → bare) | | INTERNAL_TOKEN | internal_token | Shared secret for microservice-to-microservice calls (bypasses auth) |

Type augmentation

Register your services for typed access:

declare module 'xitkov' {
  interface ServiceTypes {
    users: UserService
  }
  interface MicroServiceNames {
    billing: {
      invoices: MicroService
      customers: MicroService
    }
  }
}

License

MIT