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

@zimyo/api

v0.0.1

Published

Fastify-based framework with file-based routing, auth middleware, database adapters, and production-ready plugins

Readme

@zimyo-api

Production-ready Fastify framework with file-based routing, Express migration support, database adapters, and built-in security/performance plugins.

Quick Start

pnpm add @zimyo-api
import { createApp } from "@zimyo-api";
import path from "path";

const app = await createApp({
  port: 3000,
  routing: "controller",
  routesDir: path.join(__dirname, "routes"),
  cors: true,
  helmet: true,
  compress: true,
  swagger: true,
});

await app.start();

Table of Contents


Routing Modes

File-Based Routing

Auto-discovers controllers from a directory. File paths map to URL paths.

createApp({
  routing: "file",
  controllersDir: path.join(__dirname, "controllers"),
});
controllers/
  users/
    index.ts       → GET/POST /users
    [id].ts        → GET/PUT/DELETE /users/:id
  health.ts        → GET /health

Each file exports HTTP method handlers:

// controllers/users/index.ts
import type { RouteModule } from "@zimyo-api";

export const GET: RouteModule["GET"] = async (request, reply) => {
  return { users: [] };
};

export const POST: RouteModule["POST"] = async (request, reply) => {
  return { created: true };
};

Controller-Based Routing

Scans *.routes.ts files using defineRoutes():

createApp({
  routing: "controller",
  routesDir: path.join(__dirname, "routes"),
});
// routes/user.routes.ts
import { defineRoutes } from "@zimyo-api";

export default defineRoutes("/users", [
  { method: "GET",  path: "/",    handler: getAll },
  { method: "GET",  path: "/:id", handler: getById },
  { method: "POST", path: "/",    handler: create, config: { isPublic: true } },
]);

Express Compatibility

Loads existing Express route files without rewriting:

import { createApp, expressAdapter } from "@zimyo-api";

const app = await createApp(expressAdapter({
  port: 8082,
  routeEntry: path.join(__dirname, "src/routes/index.js"),
  appConfig: require("./src/config/server.config"),
}));

Plugins

All plugins are opt-in via config flags. Pass true for defaults or an object for custom config. Each requires installing its Fastify plugin package.

CORS

pnpm add @fastify/cors
createApp({
  // ... routing config
  cors: true,                          // defaults: all origins, all methods
  // or
  cors: {
    origin: ["https://app.zimyo.com"],
    credentials: true,
    methods: ["GET", "POST", "PUT", "DELETE"],
    maxAge: 86400,
  },
});

Rate Limiting

pnpm add @fastify/rate-limit
createApp({
  rateLimit: true,                     // 100 req/min per IP
  // or
  rateLimit: {
    max: 200,
    timeWindow: 60_000,                // 1 minute
    allowList: ["/health"],
    keyGenerator: (req) => req.headers["x-tenant-id"] || req.ip,
    errorMessage: "Too many requests, please try again later.",
  },
});

Helmet (Security Headers)

pnpm add @fastify/helmet
createApp({
  helmet: true,                        // sensible defaults
  // or
  helmet: {
    contentSecurityPolicy: false,       // disable CSP (common for APIs)
    hsts: { maxAge: 31536000, includeSubDomains: true },
    frameguard: { action: "deny" },
  },
});

Response Compression

pnpm add @fastify/compress
createApp({
  compress: true,                      // brotli > gzip > deflate
  // or
  compress: {
    encodings: ["gzip", "deflate"],    // disable brotli
    threshold: 2048,                   // min 2KB to compress
  },
});

File Uploads (Multipart)

pnpm add @fastify/multipart
createApp({
  multipart: true,                     // 10MB max, 10 files max
  // or
  multipart: {
    limits: {
      fileSize: 50 * 1024 * 1024,      // 50MB
      files: 5,
    },
    attachFieldsToBody: true,
  },
});

In route handlers:

export const POST = async (request, reply) => {
  const file = await request.file();
  await file.toBuffer(); // or pipe to disk
};

Swagger / OpenAPI Docs

pnpm add @fastify/swagger @fastify/swagger-ui
createApp({
  swagger: true,                       // serves at /docs
  // or
  swagger: {
    title: "My API",
    description: "API documentation",
    version: "2.0.0",
    routePrefix: "/api-docs",
    contact: { name: "Team", email: "[email protected]" },
    servers: [
      { url: "https://api.zimyo.com", description: "Production" },
      { url: "http://localhost:3000", description: "Local" },
    ],
    securityDefinitions: {
      token: { type: "apiKey", name: "token", in: "header" },
    },
  },
});

Add schemas to routes for auto-documentation:

export default defineRoutes("/users", [
  {
    method: "GET",
    path: "/",
    handler: getAll,
    config: {
      schema: {
        querystring: {
          type: "object",
          properties: {
            page: { type: "integer", default: 1 },
            limit: { type: "integer", default: 20 },
          },
        },
        response: {
          200: {
            type: "object",
            properties: {
              data: { type: "array", items: { type: "object" } },
            },
          },
        },
      },
    },
  },
]);

Logging

Three modes: "pretty" (colored console, default in dev), "json" (structured, default in production), "silent".

createApp({
  logMode: "json",   // structured JSON logs for ELK/CloudWatch/Datadog
});

Pretty mode (development):

14:32:01.234   POST    200  /api/users  12.3ms  127.0.0.1

JSON mode (production):

{"level":"info","time":"2026-06-04T14:32:01.234Z","msg":"POST /api/users","type":"request","method":"POST","url":"/api/users","statusCode":200,"durationMs":12.3,"ip":"127.0.0.1","reqId":"a1b2c3d4-..."}

Use the logger directly:

import { log } from "@zimyo-api";

log.info("Server starting...");
log.success("Connected to database");
log.warn("Deprecated API called", { endpoint: "/v1/old" });
log.error("Payment failed", { orderId: 123 });
log.debug("Cache miss for key: user:42");

HTTP Client

Built-in fetch-based HTTP client with retry logic and circuit breaker.

Basic Usage

import { http } from "@zimyo-api";

const { data } = await http.get("https://api.example.com/users");
const { data } = await http.post("https://api.example.com/users", { name: "John" });
const { data } = await http.put("https://api.example.com/users/1", { name: "Jane" });
const { data } = await http.patch("https://api.example.com/users/1", { name: "Jane" });
const { data } = await http.delete("https://api.example.com/users/1");

// With headers and timeout
const { data } = await http.get(url, {
  headers: { Authorization: "Bearer token123" },
  timeout: 5000,
});

Retry Logic

Automatic retry with exponential backoff for transient failures:

// Simple: retry 3 times
const { data } = await http.get(url, { retry: 3 });

// Custom retry config
const { data } = await http.get(url, {
  retry: {
    attempts: 5,                                  // max retries
    delay: 1000,                                  // base delay (ms)
    backoff: 2,                                   // exponential multiplier
    retryOn: [408, 429, 500, 502, 503, 504],     // status codes to retry
    retryOnNetworkError: true,                    // retry on ECONNREFUSED etc.
  },
});

Retry timeline with defaults (delay: 1000, backoff: 2):

Attempt 1 → fails → wait 1s
Attempt 2 → fails → wait 2s
Attempt 3 → fails → wait 4s
Attempt 4 → success!

Circuit Breaker

Prevent cascading failures when a downstream service is down:

const loginApi = http.create({
  baseURL: "https://login-api.zimyo.com",
  timeout: 5000,
  retry: 2,
  circuitBreaker: {
    threshold: 5,        // open circuit after 5 consecutive failures
    resetTimeout: 30000, // try again after 30 seconds
  },
});

// All requests through this client share the circuit breaker
const { data } = await loginApi.get("/validate-token");
const { data } = await loginApi.post("/authenticate", { token });

// Check circuit state
const state = loginApi.getCircuitState?.();
// { state: "CLOSED", failures: 0 }
// { state: "OPEN", failures: 5 }
// { state: "HALF_OPEN", failures: 5 }

Circuit breaker states:

  • CLOSED — normal operation, requests pass through
  • OPEN — too many failures, requests rejected instantly with 503
  • HALF_OPEN — after resetTimeout, one request allowed through to test recovery

Custom Client Instances

const paymentApi = http.create({
  baseURL: "https://payments.zimyo.com/api/v1",
  headers: { "X-API-Key": process.env.PAYMENT_API_KEY },
  timeout: 10_000,
  retry: { attempts: 3, retryOn: [502, 503, 504] },
  circuitBreaker: { threshold: 3, resetTimeout: 60_000 },
});

const { data } = await paymentApi.post("/charge", { amount: 100 });

Database Adapters

Single Database

import { createApp } from "@zimyo-api";
import { sequelizeAdapter } from "@zimyo-api/adapters/sequelize";

const app = await createApp({
  routing: "controller",
  routesDir: "./routes",
  database: sequelizeAdapter({
    dialect: "mysql",
    host: "localhost",
    database: "mydb",
    username: "root",
    password: "secret",
  }),
});

// In route handlers: ctx.db is the Sequelize instance

Available adapters:

import { sequelizeAdapter } from "@zimyo-api/adapters/sequelize";
import { drizzleAdapter } from "@zimyo-api/adapters/drizzle";
import { redisAdapter } from "@zimyo-api/adapters/redis";
import { mongoAdapter } from "@zimyo-api/adapters/mongo";
import { legacySequelizeAdapter } from "@zimyo-api/adapters/legacy-sequelize";

Multiple Databases

Pass an array of adapters. Each must have a unique name. Route handlers receive a map:

import { sequelizeAdapter } from "@zimyo-api/adapters/sequelize";
import { redisAdapter } from "@zimyo-api/adapters/redis";

const app = await createApp({
  routing: "controller",
  routesDir: "./routes",
  database: [
    sequelizeAdapter({ name: "mysql", dialect: "mysql", ... }),
    redisAdapter({ host: "localhost", port: 6379 }),
  ],
});

// In route handlers:
// ctx.db = { mysql: SequelizeInstance, redis: RedisClient }

Authentication

API Key Auth

createApp({
  auth: {
    headerName: "x-api-key",
    validateKey: async (key) => key === process.env.API_KEY,
    publicUrls: ["/health", "/docs"],
  },
});

JWT / Token Auth

import { createApp, authenticateUser } from "@zimyo-api";

const app = await createApp({
  routing: "controller",
  routesDir: "./routes",
  setup: async (fastify) => {
    authenticateUser(fastify, {
      whitelistUrls: ["guest/login", "user/forgot-password"],
      errorCode: 401,
      // Custom auth function:
      authenticate: async (request, token) => {
        const user = await verifyToken(token);
        return user; // attached to request.auth
      },
      // Or use built-in Login API (reads LOGIN_API_BASE_URL env):
      // loginApiBaseUrl: "https://login.zimyo.com",
      // appName: "performance",
    });
  },
});

Role-based access on routes:

defineRoutes("/admin", [
  {
    method: "GET",
    path: "/dashboard",
    handler: adminDashboard,
    config: { role_id: [1, 2] },  // only roles 1 and 2 can access
  },
]);

Monitoring Dashboard

Built-in request metrics and Sentry-style error tracking with a live dashboard UI. Persistent storage via MongoDB.

createApp({
  monitoring: true,                // dashboard at /monitoring
  // or with MongoDB persistence:
  monitoring: {
    routePrefix: "/monitoring",    // dashboard URL
    maxDataPoints: 120,            // time-series points (2 min @ 1s)
    maxErrors: 500,                // recent errors buffer size
    mongo: {                       // MongoDB persistence
      uri: "mongodb://localhost:27017",
      dbName: "monitoring",
    },
  },
});

Dashboard Features

  • Request Metrics — Total requests, req/s, status code breakdown, per-route stats
  • Response Times — Percentiles (p50/p95/p99) with live chart
  • Error Tracking — Auto-captures errors with stack traces, request context, fingerprint grouping
  • System Health — Memory (RSS, heap), active connections, uptime

API Endpoints

| Endpoint | Description | |----------|-------------| | GET /monitoring | Live dashboard UI | | GET /monitoring/api/metrics | JSON metrics snapshot | | GET /monitoring/api/errors | Error groups and recent errors | | POST /monitoring/api/reset | Clear all metrics and errors |

Programmatic Access

import { metrics, errorTracker } from "@zimyo-api";

// Get metrics snapshot
const snapshot = metrics.getSnapshot();
console.log(snapshot.totalRequests, snapshot.p95Ms);

// Manually capture an error
errorTracker.capture(new Error("Payment failed"), {
  method: "POST",
  url: "/api/payments",
  statusCode: 500,
  metadata: { orderId: 12345 },
});

Health Check

Built-in GET /health endpoint with database status and memory info:

{
  "status": "ok",
  "uptime": 12345.67,
  "timestamp": 1717500000000,
  "memory": {
    "rss": 52428800,
    "heapUsed": 20971520,
    "heapTotal": 35651584
  },
  "databases": {
    "sequelize": "connected",
    "redis": "connected"
  }
}

Returns "status": "degraded" if any database is disconnected.


Express Migration

Migrate existing Express apps to Fastify without rewriting routes:

import { createApp, expressAdapter } from "@zimyo-api";
import { legacySequelizeAdapter } from "@zimyo-api/adapters/legacy-sequelize";

const app = await createApp(expressAdapter({
  port: 8082,
  routeEntry: path.join(__dirname, "src/routes/index.js"),
  appConfig: require("./src/config/server.config"),
  utilModule: require("./system/util"),
  srcDir: path.join(__dirname, "src"),
  rootDir: __dirname,

  database: legacySequelizeAdapter({ ... }),

  auth: {
    whitelistUrls: ["guest/login", "user/getToken"],
    // authenticate: customAuthFunction,  // or uses Login API by default
  },

  mocks: {
    "../middlewares": (options) => mockMiddleware,
  },
}));

await app.start();

Your existing Express route files work unchanged — require('express') is intercepted and routed through Fastify.


Full Config Reference

createApp({
  // ── Server ──
  port: 3000,                    // default: 3000
  host: "0.0.0.0",              // default: "0.0.0.0"
  basePath: "/api/v1",          // prefix for all routes
  bodyLimit: 5 * 1024 * 1024,   // max request body (5MB)
  logMode: "json",              // "pretty" | "json" | "silent"
  fastifyOptions: {},           // raw Fastify constructor options

  // ── Routing (pick one) ──
  routing: "file",
  controllersDir: "./controllers",
  // routing: "controller",
  // routesDir: "./routes",
  // routing: "express",
  // routeEntry: "./src/routes/index.js",

  // ── Database ──
  database: adapter,             // single adapter or array

  // ── Auth ──
  auth: { validateKey, publicUrls },

  // ── Plugins ──
  cors: true | { origin, credentials, ... },
  helmet: true | { hsts, frameguard, ... },
  rateLimit: true | { max, timeWindow, ... },
  compress: true | { threshold, encodings, ... },
  multipart: true | { limits, ... },
  swagger: true | { title, routePrefix, ... },
  monitoring: true | { routePrefix, maxDataPoints, maxErrors },

  // ── Custom Setup ──
  setup: async (fastify, ctx) => {
    // Register custom plugins, hooks, decorators
  },
});

Graceful Shutdown

The framework automatically handles SIGTERM and SIGINT signals, draining active connections and closing database pools before exit.

Request ID

Every request gets a UUID (request.id) for log correlation. Override with fastifyOptions.genReqId.


Install Plugins

Only install the plugins you need:

# Security
pnpm add @fastify/cors @fastify/helmet @fastify/rate-limit

# Performance
pnpm add @fastify/compress

# File uploads
pnpm add @fastify/multipart

# API docs
pnpm add @fastify/swagger @fastify/swagger-ui

# Database (pick what you use)
pnpm add sequelize mysql2        # MySQL/Postgres via Sequelize
pnpm add drizzle-orm pg          # Postgres via Drizzle
pnpm add ioredis                 # Redis
pnpm add mongodb                 # MongoDB

License

MIT