Catalog npm
Back to packageRELEASE DETAILS

1.10.5

npm

90dc-core

A package that contains utils and interfaces used to create 90dc

Published6 Nov 2025
Direct dependencies29
Transitive dependenciesNot calculated

README

Current package documentation

90dc-core

Shared utilities, models, and middleware for 90 Day Challenge microservices.

Version 1.11.0 - NEW Features ✨

This version adds configuration validation and error handling middleware to improve code quality across all services.

What's New

  • ✅ Type-safe configuration with Zod validation
  • ✅ Consistent error handling across all services
  • ✅ Request validation middleware with Zod
  • ✅ Comprehensive error classes (404, 400, 401, 403, 500, etc.)
  • ✅ Fail-fast validation on startup

Installation

npm install 90dc-core@latest zod

Quick Start

1. Configuration Validation

import { BaseConfigSchema, ConfigValidator } from '90dc-core';
import { z } from 'zod';

// Extend base config with service-specific settings
const ConfigSchema = BaseConfigSchema.extend({
  GOOGLE_CLIENT_ID: z.string().min(1),
  APPLE_TEAM_ID: z.string().min(1),
  MOLLIE_API_KEY: z.string().min(1)
});

// Create validated config (fails fast on startup if invalid)
const config = new ConfigValidator(ConfigSchema);

// Use with type safety
const port = config.get('PORT'); // Type: number
const jwtSecret = config.get('JWT_SECRET'); // Type: string
const googleClientId = config.get('GOOGLE_CLIENT_ID'); // Type: string

2. Error Handling Middleware

import Koa from 'koa';
import { createErrorMiddleware } from '90dc-core';

const app = new Koa();

// Add error middleware FIRST
app.use(createErrorMiddleware({
  exposeErrorDetails: config.isDevelopment()
}));

// Add your routes
app.use(router.routes());

3. Throwing Errors

import {
  NotFoundError,
  ValidationError,
  ForbiddenError,
  AuthenticationError
} from '90dc-core';

class UserController {
  async getUser(ctx: Context) {
    const user = await User.findByPk(ctx.params.id);

    if (!user) {
      throw new NotFoundError('User'); // Returns 404 with consistent format
    }

    ctx.body = { user };
  }

  async createUser(ctx: Context) {
    const existing = await User.findOne({ where: { email } });

    if (existing) {
      throw new ConflictError('Email already registered'); // Returns 409
    }

    // ... create user
  }
}

4. Request Validation

import { validateRequest } from '90dc-core';
import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
  firstName: z.string().min(1).max(100)
});

router.post('/users',
  validateRequest({ body: CreateUserSchema }),
  async (ctx) => {
    // ctx.request.body is now validated and typed
    const user = await User.create(ctx.request.body);
    ctx.body = { user };
  }
);

Error Response Format

All errors return consistent JSON:

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "User not found"
  }
}

With details for validation errors:

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "validationErrors": [
        {
          "path": "email",
          "message": "Invalid email",
          "code": "invalid_string"
        }
      ]
    }
  }
}

Available Error Types

import {
  ValidationError,         // 400 - Invalid data
  AuthenticationError,     // 401 - Not authenticated
  ForbiddenError,          // 403 - No permission
  NotFoundError,           // 404 - Resource not found
  ConflictError,           // 409 - Resource conflict
  UnprocessableEntityError,// 422 - Semantic error
  RateLimitError,          // 429 - Too many requests
  InternalServerError,     // 500 - Server error
  ServiceUnavailableError, // 503 - Service down
  DatabaseError,           // 500 - Database issue
  ExternalAPIError,        // 502 - External API failed
} from '90dc-core';

Common Config Schemas

Reusable schemas for common configurations:

import { CommonSchemas } from '90dc-core';

const ConfigSchema = BaseConfigSchema
  .extend(CommonSchemas.redis.shape)
  .extend(CommonSchemas.jwt.shape)
  .extend(CommonSchemas.googleOAuth.shape);

Available common schemas:

  • CommonSchemas.database - PostgreSQL configuration
  • CommonSchemas.redis - Redis configuration
  • CommonSchemas.jwt - JWT authentication
  • CommonSchemas.googleOAuth - Google OAuth
  • CommonSchemas.appleOAuth - Apple OAuth
  • CommonSchemas.serviceUrls - Inter-service URLs
  • CommonSchemas.featureFlags - Feature flags

Full Documentation

See USAGE_EXAMPLES.md for:

  • Complete usage examples
  • Migration guide from old code
  • Best practices
  • Testing examples
  • All available features

Exports

Configuration

import {
  ConfigValidator,
  BaseConfigSchema,
  CommonSchemas,
  createConfig,
  ConfigurationError
} from '90dc-core';

Errors

import {
  AppError,
  ValidationError,
  AuthenticationError,
  ForbiddenError,
  NotFoundError,
  ConflictError,
  UnprocessableEntityError,
  RateLimitError,
  InternalServerError,
  ServiceUnavailableError,
  DatabaseError,
  ExternalAPIError,
  isAppError,
  isOperationalError,
  toAppError
} from '90dc-core';

Middleware

import {
  createErrorMiddleware,
  validateRequest,
  asyncHandler,
  type ErrorMiddlewareConfig
} from '90dc-core';

Database Models

import {
  PersistedUser,
  Program,
  Workout,
  Exercise,
  Badge,
  // ... many more
} from '90dc-core';

Utilities

import {
  AuthenticationUtil,
  NotificationsUtil,
  NotificationClient
} from '90dc-core';

Migration Steps

  1. Install dependencies:

    npm install 90dc-core@latest zod
    
  2. Add config validation:

    import { BaseConfigSchema, ConfigValidator } from '90dc-core';
    const config = new ConfigValidator(BaseConfigSchema);
    
  3. Add error middleware:

    import { createErrorMiddleware } from '90dc-core';
    app.use(createErrorMiddleware());
    
  4. Replace error handling:

    // Before
    if (!user) {
      ctx.status = 404;
      ctx.body = { message: 'Not found' };
      return;
    }
    
    // After
    import { NotFoundError } from '90dc-core';
    if (!user) {
      throw new NotFoundError('User');
    }
    
  5. Add request validation:

    import { validateRequest } from '90dc-core';
    router.post('/users',
      validateRequest({ body: CreateUserSchema }),
      handler
    );
    

See USAGE_EXAMPLES.md for detailed migration guide.


Building & Publishing

# Install dependencies
npm install

# Build
npm run build

# Publish to npm
npm run publish:build

Changelog

See CHANGELOG.md for version history.


License

ISC


Contributing

This is part of the 90 Day Challenge microservices architecture. See the main refactor plan for contribution guidelines.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 29

Native requirements
29 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
apn
Environment
{
  "local_name": "apn",
  "alias": false,
  "peer_metadata": null
}
^2.2.0runtime
axios
Environment
{
  "local_name": "axios",
  "alias": false,
  "peer_metadata": null
}
1.12.0runtime
debug
Environment
{
  "local_name": "debug",
  "alias": false,
  "peer_metadata": null
}
^4.3.4runtime
dotenv
Environment
{
  "local_name": "dotenv",
  "alias": false,
  "peer_metadata": null
}
^16.4.5runtime
eslint
Environment
{
  "local_name": "eslint",
  "alias": false,
  "peer_metadata": null
}
^9.39.0runtime
winston
Environment
{
  "local_name": "winston",
  "alias": false,
  "peer_metadata": null
}
^3.8.2runtime
bcryptjs
Environment
{
  "local_name": "bcryptjs",
  "alias": false,
  "peer_metadata": null
}
^3.0.3runtime
jwks-rsa
Environment
{
  "local_name": "jwks-rsa",
  "alias": false,
  "peer_metadata": null
}
^3.2.0runtime
sequelize
Environment
{
  "local_name": "sequelize",
  "alias": false,
  "peer_metadata": null
}
^6.37.4runtime
googleapis
Environment
{
  "local_name": "googleapis",
  "alias": false,
  "peer_metadata": null
}
^140.0.0runtime
jsonwebtoken
Environment
{
  "local_name": "jsonwebtoken",
  "alias": false,
  "peer_metadata": null
}
^9.0.2runtime
google-auth-library
Environment
{
  "local_name": "google-auth-library",
  "alias": false,
  "peer_metadata": null
}
^9.14.1runtime
sequelize-typescript
Environment
{
  "local_name": "sequelize-typescript",
  "alias": false,
  "peer_metadata": null
}
2.1.6runtime
@typescript-eslint/parser
Environment
{
  "local_name": "@typescript-eslint/parser",
  "alias": false,
  "peer_metadata": null
}
^5.53.0runtime
@google-cloud/secret-manager
Environment
{
  "local_name": "@google-cloud/secret-manager",
  "alias": false,
  "peer_metadata": null
}
^6.1.1runtime
jest
Environment
{
  "local_name": "jest",
  "alias": false,
  "peer_metadata": null
}
^29.4.3development
ts-jest
Environment
{
  "local_name": "ts-jest",
  "alias": false,
  "peer_metadata": null
}
^29.0.5development
ts-node
Environment
{
  "local_name": "ts-node",
  "alias": false,
  "peer_metadata": null
}
^10.9.1development
@swc/cli
Environment
{
  "local_name": "@swc/cli",
  "alias": false,
  "peer_metadata": null
}
^0.1.62development
prettier
Environment
{
  "local_name": "prettier",
  "alias": false,
  "peer_metadata": null
}
^2.8.4development
@swc/core
Environment
{
  "local_name": "@swc/core",
  "alias": false,
  "peer_metadata": null
}
^1.3.36development
typescript
Environment
{
  "local_name": "typescript",
  "alias": false,
  "peer_metadata": null
}
^4.9.5development
@types/jest
Environment
{
  "local_name": "@types/jest",
  "alias": false,
  "peer_metadata": null
}
^29.4.0development
@types/node
Environment
{
  "local_name": "@types/node",
  "alias": false,
  "peer_metadata": null
}
^18.14.1development
ts-node-dev
Environment
{
  "local_name": "ts-node-dev",
  "alias": false,
  "peer_metadata": null
}
^2.0.0development
@types/debug
Environment
{
  "local_name": "@types/debug",
  "alias": false,
  "peer_metadata": null
}
^4.1.12development
@types/validator
Environment
{
  "local_name": "@types/validator",
  "alias": false,
  "peer_metadata": null
}
^13.7.12development
@types/jsonwebtoken
Environment
{
  "local_name": "@types/jsonwebtoken",
  "alias": false,
  "peer_metadata": null
}
^9.0.6development
@typescript-eslint/eslint-plugin
Environment
{
  "local_name": "@typescript-eslint/eslint-plugin",
  "alias": false,
  "peer_metadata": null
}
^5.53.0development

Distribution files

1 collected
Distribution 1

Source distribution

Source file
Checksums from source
{
  "shasum": "3a2b33aede7669fb8342c2874147dc7138581bf2",
  "tarball": "https://registry.npmjs.org/90dc-core/-/90dc-core-1.10.5.tgz",
  "fileCount": 311,
  "integrity": "sha512-lMIkZSGhN+CJCHDOTAIFzttjmxEXvyodmp/ugW4NukjVmzq7Bfh2JyDBPd6AjOmO1RFOt+vkmsOVt5pboueq8Q==",
  "signatures": [
    {
      "sig": "MEUCIClIVsggxaZ+JUaKmIY0vi3WyHExZybwEhSQVoihqsKrAiEA4FaTAk8YwDt4M1lEZEEduoMe9d5PmAof1q01ygbTeEg=",
      "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
    }
  ],
  "unpackedSize": 598730
}

Repository

PENDING
romapchel/90dc-core

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 21:30 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:bb57cb55d555e65b285a136cdebc2d8b84bf3537b0c3aa5fc29bcb47c975abb1

Other collected versions

1.19.1012 Apr1.19.931 Mar1.19.831 Mar1.19.731 Mar1.19.631 Mar1.19.531 MarView all collected versions →