Catalog npm
Back to packageRELEASE DETAILS

1.0.2

npm

90dc-core

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

Published22 Jan 2023
Direct dependencies7
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 7

Native requirements
7 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
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
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
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": "053108393cc8a0359d56c5b18fd50c91f0686551",
  "tarball": "https://registry.npmjs.org/90dc-core/-/90dc-core-1.0.2.tgz",
  "fileCount": 14,
  "integrity": "sha512-SfVuekH23Hlugg3t+aimlP01XXy5JkcLtIMeqOnfeV8x6Qaz2efAPdP7epQlp9gOACh9l4rfHGLuy5UIBc9KUg==",
  "signatures": [
    {
      "sig": "MEQCIDhx/rOePgs+XuPsDIGo0tJp7qVl1+JSoMqYuSFemYcBAiBd1nTPu1bwiWDOCTGI03CHvnzWV9otQDPbLgMCOKc5iw==",
      "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"
    }
  ],
  "unpackedSize": 4890,
  "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjzan3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrLnQ//TODIKv9j8E4CEPPnHw2CkX3LrChbcDCR+Kq5h+5bYUfTmQvs\r\n8/VV7e8IZTeqqenchDx4xfnIzICYjF5DdMGIVs+Igi42Qap8EJJb9fYNla9h\r\nn218i4iIhwOhirdQw/ZFwl/zw81aTvpjLt4IXDzJI3oE+9UlRpIFs46ZsBwU\r\n8vH74YSjLhSu/I39nq0KXiaC9RBnjeEFXX64R9JJmmGJFnnmN0zZ9xgRGeMx\r\nCd5Dr61cB/roPImJ5oEj2P0kDnXQCt0CUdBWvR6Zg/UiFySYspapjsQNDBkb\r\nSFHOK7MNSrz0qXjdOYptKpqbHrCcAV+9rljnv+2ufjZal9wJ1mwyZDDBYzaf\r\nwzfALnltNbH34AQdRQjwpBAq43ifEaeHLTr0IisoamJJ5cd1m9zvs19pnyF0\r\n8B2nVlolRym/Hnh+5pPNkRr/t7T+w7AOJTLOy2HtH3UDXOALd6Mx3ciNhP8B\r\n+frcnOxH0/K6Fz+IswT729strwjrF8iYj2nERHPIGcmif8gsDzWjj2V/k+VW\r\nFaGX0hGjrxQMYobl5hH9lIRi8TlGYBh9omy3sQr2K8bz0mMyPfSA+7IKkbhM\r\nvibQa+gVo5Utc9QCW2snjwddTEKWiTUOT06p5rZsXugMsE/upmprlPBdI8YG\r\nMXIxTw3lYseplHwCnw9AgJj/3STmk1/nrJE=\r\n=e+UD\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:2380dad5de517214359ebda581c22fa39221edd5a0d0349cbaccf96ae8433a4d

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 →