90dc-core
v1.12.27
Published
Shared utilities, models, and middleware for 90 Day Challenge microservices.
Readme
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 zodQuick 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: string2. 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 configurationCommonSchemas.redis- Redis configurationCommonSchemas.jwt- JWT authenticationCommonSchemas.googleOAuth- Google OAuthCommonSchemas.appleOAuth- Apple OAuthCommonSchemas.serviceUrls- Inter-service URLsCommonSchemas.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
Install dependencies:
npm install 90dc-core@latest zodAdd config validation:
import { BaseConfigSchema, ConfigValidator } from '90dc-core'; const config = new ConfigValidator(BaseConfigSchema);Add error middleware:
import { createErrorMiddleware } from '90dc-core'; app.use(createErrorMiddleware());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'); }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:buildChangelog
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.
