Catalog npm
Back to packageRELEASE DETAILS

1.1.7

npm

90dc-core

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

Published20 Feb 2023
Direct dependencies15
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 15

Native requirements
15 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
winston
Environment
{
  "local_name": "winston",
  "alias": false,
  "peer_metadata": null
}
^3.8.2runtime
sequelize
Environment
{
  "local_name": "sequelize",
  "alias": false,
  "peer_metadata": null
}
^6.28.0runtime
reflect-metadata
Environment
{
  "local_name": "reflect-metadata",
  "alias": false,
  "peer_metadata": null
}
^0.1.13runtime
sequelize-typescript
Environment
{
  "local_name": "sequelize-typescript",
  "alias": false,
  "peer_metadata": null
}
^2.1.5runtime
jest
Environment
{
  "local_name": "jest",
  "alias": false,
  "peer_metadata": null
}
^29.3.1development
tslint
Environment
{
  "local_name": "tslint",
  "alias": false,
  "peer_metadata": null
}
^6.1.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
prettier
Environment
{
  "local_name": "prettier",
  "alias": false,
  "peer_metadata": null
}
^2.8.3development
typescript
Environment
{
  "local_name": "typescript",
  "alias": false,
  "peer_metadata": null
}
^4.9.4development
@types/jest
Environment
{
  "local_name": "@types/jest",
  "alias": false,
  "peer_metadata": null
}
^29.2.6development
@types/node
Environment
{
  "local_name": "@types/node",
  "alias": false,
  "peer_metadata": null
}
^18.13.0development
ts-node-dev
Environment
{
  "local_name": "ts-node-dev",
  "alias": false,
  "peer_metadata": null
}
^2.0.0development
@types/validator
Environment
{
  "local_name": "@types/validator",
  "alias": false,
  "peer_metadata": null
}
^13.7.12development
tslint-config-prettier
Environment
{
  "local_name": "tslint-config-prettier",
  "alias": false,
  "peer_metadata": null
}
^1.18.0development

Distribution files

1 collected
Distribution 1

Source distribution

Source file
Checksums from source
{
  "shasum": "e9c626d75ff1c61a8aa632bc8ae8b3711828e090",
  "tarball": "https://registry.npmjs.org/90dc-core/-/90dc-core-1.1.7.tgz",
  "fileCount": 42,
  "integrity": "sha512-QoAgjAl7HnquHwWS+k46/8n3l5FWmGXCQxpu0SF6xQcETS0Ia0/9YTmDRmS3pc6gEBUQerlePPsnaKZe9/XWng==",
  "signatures": [
    {
      "sig": "MEUCIC/5/xgTqxaTgg+aBmEDtbc/Qp/syYKr8TVl4hWIl/9UAiEAu82yVzTxUlixwsfCb6pFHBROTaz+uT3h7z8oatairIo=",
      "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"
    }
  ],
  "unpackedSize": 42596,
  "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj82kxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrpUg//dVL13mvF78xw6WjMlabxDlqPk3DSxlntYcCQMKdFREaVzBDC\r\nLsHye8GZ+RICsOSyJ/ouGq6sTcaU1E/LV1YBH6mGJbcQhrw5m0vY/mIY3rWL\r\nHxLnZ9AwcLGezfgisebZ7JuLbhPDJSM+3/f4opPT1xW2cGTTsDdFfiBDZnba\r\nWAVW+43+nLFxmC96Ad7Gnh/25xi9to/j4fEmWZdefHgYCjeS8OcmreL1Em57\r\nsfS5ET2rgZExRDrBR8USHGwrIjzDOJk1RqUEOAUQWA3K7SxQqBvekDAIbn6Z\r\nYMx44kyXifDLUmQL/hDywE9rJzNlf0ZI1QkXCjmGjV78o2MRvFh/WSf4WB6L\r\nkZ2JCcuubyzsbppL2OScS9FazpFjtZdxNcvK+40TvImNht/3DY5LMWcf9mnh\r\nS0v6NUxtJQK3hG+L+/cjGIS6RyaT+l2OXhB1Pr94qv552ARcYBD/sgMPvEa1\r\nMfdBaCasfdIA/iieGo0jli5tEIofjpgZlMZt6E4tQG/Ylc/K31XrfVdzWBRC\r\nHUSYWNsejDCetcxP59oRxQ4xKvGI5K6rtQQFeLCALMfUv3BvBoJ3M1DujV6I\r\nOHk5taG8R7+mdoLjTJA1TOE6bvBxt+KvMjMv9sKh5hxV+gmBe2Y/7XCAq2mD\r\ndCOB8OyVvsItujTL5aJrCzqQYZ/UZvqeAVQ=\r\n=KOe5\r\n-----END PGP SIGNATURE-----\r\n"
}

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:29 UTC

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

Record identity

package_version:f9474b8672daa4edecde9b15cb360c87ce23ffa4b341fe3bd61d7a089b91e4dc

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 →