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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@sens-tools/base-server

v0.0.4

Published

A reusable Express server base with Swagger documentation support

Readme

Sens Node.js Base Server

A powerful Express server package that provides a robust foundation for building secure and well-documented APIs with Swagger integration, authentication, rate limiting, and more.

Features

  • 🛡️ Security Features

    • Helmet for security headers
    • CORS support with configurable options
    • Rate limiting to prevent abuse
    • Basic authentication for Swagger UI
    • Toobusy protection against server overload
  • 📚 Swagger Integration

    • Multiple theme support (classic, dark, material, monokai, muted, newspaper, outline, shadow)
    • Instance-specific documentation
    • Basic authentication protection
    • Customizable base URL and API path
  • 🚀 Express Enhancements

    • Instance-based routing with createRouter function
    • Standardized response helper
    • Error handling middleware
    • Morgan logging with configurable formats
    • Body parsing middleware

Installation

pnpm add @sens-tools/base-server

Quick Start

import { SensServer } from '@sens-tools/base-server';
import { Router } from 'express';

// Create your router function
const createRouter = (router: Router): Router => {
  // Add your routes here
  router.get('/users', (req, res) => {
    console.log('Instance:', req.api.iota.instance);
    console.log('Token:', req.api.iota.token);
    res.api.ok({ users: [] });
  });

  router.post('/users', (req, res) => {
    res.api.created({ id: 1, name: 'John Doe' });
  });

  return router;
};

// Create your Swagger documentation
const swaggerJson = {
  info: {
    title: 'Your API Server',
    version: '1.0.0',
  },
  openapi: '3.0.0',
  paths: {
    '/users': {
      get: {
        responses: {
          '200': {
            description: 'List of users',
          },
        },
      },
      post: {
        responses: {
          '201': {
            description: 'User created',
          },
        },
      },
    },
  },
};

// Initialize the server with configuration
const server = new SensServer({
  createRouter,
  swaggerJson,
  iotaBaseUrl: 'your-domain.com',
  apiPath: '/api',
  swaggerPath: '/docs',
  swaggerUsername: 'admin',
  swaggerPassword: 'secret',
  morgan: 'dev',
  rateLimitWindowMs: 15 * 60 * 1000, // 15 minutes
  rateLimitMaxRequests: 100,
  toobusyMaxLag: 100,
  toobusyInterval: 500,
  corsOptions: {
    origin: ['https://your-frontend.com'],
    methods: ['GET', 'POST'],
  },
});

// Start the server
server.start(3000);

Available Components

import SensServer, { handleEndpointResult } from '@sens-tools/base-server';

Exported Types

The package exports the following TypeScript types:

import type {
  SensConfig, // Complete server configuration with all required fields
  SensConfigInput, // Input configuration interface for SensServer constructor
  InstanceConfig, // Configuration for API instances
  EndpointResultType, // Type for endpoint result data (string | number | Record<string, unknown> | Array<unknown>)
  EndpointFunction, // Type for endpoint functions that return a Result type
  ErrorResponse, // Standard error response format
} from '@sens-tools/base-server';

Configuration Options

The SensConfigInput interface accepts the following configuration:

interface SensConfigInput {
  createRouter: (router: Router) => Router; // Function to create and configure your router
  swaggerJson: {
    info: {
      title: string;
      version: string;
    };
    openapi: string;
    paths: Record<string, unknown>;
  }; // Your OpenAPI/Swagger documentation
  iotaBaseUrl: string; // Base URL for your API (e.g., 'api.example.com')
  corsOptions?: CorsOptions; // Optional CORS configuration
  swaggerUsername?: string; // Username for Swagger UI basic auth (default: 'swagger')
  swaggerPassword?: string; // Password for Swagger UI basic auth (default: 'secret')
  morgan?: 'combined' | 'common' | 'dev' | 'short' | 'tiny'; // Logging format (default: 'dev')
  toobusyMaxLag?: number; // Maximum event loop lag in milliseconds (default: 100)
  toobusyInterval?: number; // Check interval in milliseconds (default: 500)
  rateLimitWindowMs?: number; // Rate limit window in milliseconds (default: 300000 - 5 minutes)
  rateLimitMaxRequests?: number; // Maximum requests per window (default: 1000)
  apiPath?: string; // API path prefix (default: '/api')
  swaggerPath?: string; // Swagger UI path (default: '/swagger')
}

SensServer Components and Functionality

After initializing a new SensServer instance, you have access to several powerful components and methods:

Available Methods

// Get the router to add your routes (deprecated - use createRouter instead)
const router = server.getRouter();

// Get the Express app instance for advanced customization
const app = server.getApp();

// Get the current server configuration
const config = server.getConfig();

// Start the server
await server.start(port);

// Shutdown the server gracefully
await server.shutdown();

// Restart the server
await server.restart(port);

Request Context

Each request automatically includes instance and authentication information through req.api:

req.api.iota.instance; // The instance identifier from the URL
req.api.iota.token; // The authentication token from headers

Response Helpers

The server provides a rich set of response helpers through res.api:

res.api.ok(data); // 200 OK
res.api.created(data); // 201 Created
res.api.accepted(data); // 202 Accepted
res.api.noContent(); // 204 No Content
res.api.badRequest(error); // 400 Bad Request
res.api.unauthorized(error); // 401 Unauthorized
res.api.forbidden(error); // 403 Forbidden
res.api.notFound(error); // 404 Not Found
res.api.methodNotAllowed(error); // 405 Method Not Allowed
res.api.notAcceptable(error); // 406 Not Acceptable
res.api.requestTimeout(error); // 408 Request Timeout
res.api.conflict(error); // 409 Conflict
res.api.gone(error); // 410 Gone
res.api.unprocessableEntity(error); // 422 Unprocessable Entity
res.api.internalServerError(error); // 500 Internal Server Error
res.api.serviceUnavailable(error); // 503 Service Unavailable
res.api.tooBusy(error); // 503 Too Busy
res.api.gatewayTimeout(error); // 504 Gateway Timeout

Error Handling with handleEndpointResult

The SensServer provides a powerful error handling system through the handleEndpointResult utility. This utility helps standardize error handling across your API endpoints and ensures consistent error responses.

Basic Usage

import { handleEndpointResult } from '@sens-tools/base-server';
import { ok, err } from 'neverthrow';

// Define your endpoint function
const getUsers = async (req: Request, res: Response) => {
  const users = await getUsersFromDatabase();
  if (users.isErr()) {
    return err(users.error);
  }
  return ok(users.value);
};

// Use handleEndpointResult in your route
router.get('/users', (req, res) => {
  handleEndpointResult(req, res, getUsers);
});

Error Response Format

All errors follow a consistent format:

{
  error: string,             // Error code/type
  error_description: string, // Human-readable error message
  details?: any              // Optional additional error details (e.g., validation errors)
}

Error Types Handled

The system automatically handles various types of errors:

  1. Validation Errors (Zod)

    {
      error: 'Validation Error',
      error_description: 'The request data failed validation',
      details: [
        {
          path: 'user.email',
          message: 'Invalid email format',
          code: 'invalid_string'
        }
      ]
    }
  2. API Errors

    {
      error: 'API Error',
      error_description: 'Custom error message',
      statusCode: 400 // Optional status code
    }
  3. Standard Errors

    {
      error: 'Internal Server Error',
      error_description: 'An unexpected error occurred'
    }

Example with Error Handling

import { handleEndpointResult } from '@sens-tools/base-server';
import { ok, err } from 'neverthrow';

const createUser = async (req: Request, res: Response) => {
  try {
    // Validate input
    const validation = userSchema.safeParse(req.body);
    if (!validation.success) {
      return err(validation.error);
    }

    // Create user
    const user = await db.users.create(validation.data);
    if (!user) {
      return err({
        error: 'User Creation Failed',
        error_description: 'Failed to create user in database',
      });
    }

    // Return success
    return ok(user);
  } catch (error) {
    return err(error);
  }
};

router.post('/users', (req, res) => {
  handleEndpointResult(req, res, createUser);
});

Advanced Usage Examples

Complete Server Setup

import { SensServer } from '@sens-tools/base-server';
import { Router } from 'express';
import { z } from 'zod';

// Define your schemas
const userSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

// Create your router function
const createRouter = (router: Router): Router => {
  // User routes
  router.get('/users', (req, res) => {
    res.api.ok({ users: [] });
  });

  router.post('/users', (req, res) => {
    const validation = userSchema.safeParse(req.body);
    if (!validation.success) {
      return res.api.badRequest({
        error: 'Validation Error',
        error_description: 'Invalid user data',
        details: validation.error.errors,
      });
    }

    res.api.created({ id: 1, ...validation.data });
  });

  // Product routes
  router.get('/products', (req, res) => {
    res.api.ok({ products: [] });
  });

  return router;
};

// Swagger documentation
const swaggerJson = {
  info: {
    title: 'My API Server',
    version: '1.0.0',
    description: 'A comprehensive API server',
  },
  openapi: '3.0.0',
  servers: [
    {
      url: 'https://api.example.com',
      description: 'Production server',
    },
  ],
  paths: {
    '/users': {
      get: {
        summary: 'Get all users',
        responses: {
          '200': {
            description: 'List of users',
            content: {
              'application/json': {
                schema: {
                  type: 'object',
                  properties: {
                    users: {
                      type: 'array',
                      items: {
                        type: 'object',
                        properties: {
                          id: { type: 'integer' },
                          name: { type: 'string' },
                          email: { type: 'string' },
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        },
      },
      post: {
        summary: 'Create a new user',
        requestBody: {
          required: true,
          content: {
            'application/json': {
              schema: {
                type: 'object',
                properties: {
                  name: { type: 'string' },
                  email: { type: 'string' },
                },
                required: ['name', 'email'],
              },
            },
          },
        },
        responses: {
          '201': {
            description: 'User created successfully',
          },
          '400': {
            description: 'Invalid input data',
          },
        },
      },
    },
    '/products': {
      get: {
        summary: 'Get all products',
        responses: {
          '200': {
            description: 'List of products',
          },
        },
      },
    },
  },
};

// Initialize server
const server = new SensServer({
  createRouter,
  swaggerJson,
  iotaBaseUrl: 'api.example.com',
  swaggerUsername: 'admin',
  swaggerPassword: 'secure-password',
  corsOptions: {
    origin: ['https://my-frontend.com'],
    credentials: true,
  },
  rateLimitMaxRequests: 500,
  morgan: 'combined',
});

// Start server
server.start(3000).then((port) => {
  console.log(`🚀 Server running on port ${port}`);
  console.log(`📚 Swagger docs available at http://localhost:${port}/swagger`);
});

Custom Middleware Integration

import { SensServer } from '@sens-tools/base-server';
import { Router } from 'express';

const createRouter = (router: Router): Router => {
  // Add custom middleware to specific routes
  router.use('/admin', (req, res, next) => {
    if (req.api.iota.token !== 'admin-token') {
      return res.api.forbidden({ error: 'Admin access required' });
    }
    next();
  });

  router.get('/admin/users', (req, res) => {
    res.api.ok({ adminUsers: [] });
  });

  return router;
};

const server = new SensServer({
  createRouter,
  swaggerJson: {
    /* your swagger config */
  },
  iotaBaseUrl: 'api.example.com',
});

// Add global middleware
const app = server.getApp();
app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
});

Swagger Documentation

Access your API documentation through multiple endpoints:

  • Default theme: /${swaggerPath}
  • Theme-specific: /${swaggerPath}/{theme}
  • Instance-specific JSON: /${swaggerPath}/{theme}/{instance}/swagger.json

Available themes:

  • classic (default)
  • dark
  • material
  • monokai
  • muted
  • newspaper
  • outline
  • shadow

Development

  1. Clone the repository

  2. Install dependencies:

    pnpm install
  3. Build the project:

    pnpm build

Version Management

This project uses Bumpp for version management. See BUMPP.md for detailed instructions.

Contributing

  1. Fork the repository
  2. Create your feature branch
  3. Commit your changes
  4. Push to the branch
  5. Create a new Pull Request