Shared utilities, models, and middleware for 90 Day Challenge microservices.
npm install 90dc-coreShared utilities, models, and middleware for 90 Day Challenge microservices.
This version adds configuration validation and error handling middleware to improve code quality across all services.
- ✅ 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
---
``bash`
npm install 90dc-core@latest zod
---
`typescript
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
`
`typescript
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());
`
`typescript
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
}
}
`
`typescript
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 };
}
);
`
---
All errors return consistent JSON:
`json`
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "User not found"
}
}
With details for validation errors:
`json`
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": {
"validationErrors": [
{
"path": "email",
"message": "Invalid email",
"code": "invalid_string"
}
]
}
}
}
---
`typescript`
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';
---
Reusable schemas for common configurations:
`typescript
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
---
See USAGE_EXAMPLES.md for:
- Complete usage examples
- Migration guide from old code
- Best practices
- Testing examples
- All available features
---
typescript
import {
ConfigValidator,
BaseConfigSchema,
CommonSchemas,
createConfig,
ConfigurationError
} from '90dc-core';
`$3
`typescript
import {
AppError,
ValidationError,
AuthenticationError,
ForbiddenError,
NotFoundError,
ConflictError,
UnprocessableEntityError,
RateLimitError,
InternalServerError,
ServiceUnavailableError,
DatabaseError,
ExternalAPIError,
isAppError,
isOperationalError,
toAppError
} from '90dc-core';
`$3
`typescript
import {
createErrorMiddleware,
validateRequest,
asyncHandler,
type ErrorMiddlewareConfig
} from '90dc-core';
`$3
`typescript
import {
PersistedUser,
Program,
Workout,
Exercise,
Badge,
// ... many more
} from '90dc-core';
`$3
`typescript
import {
AuthenticationUtil,
NotificationsUtil,
NotificationClient
} from '90dc-core';
`---
Migration Steps
1. Install dependencies:
`bash
npm install 90dc-core@latest zod
`2. Add config validation:
`typescript
import { BaseConfigSchema, ConfigValidator } from '90dc-core';
const config = new ConfigValidator(BaseConfigSchema);
`3. Add error middleware:
`typescript
import { createErrorMiddleware } from '90dc-core';
app.use(createErrorMiddleware());
`4. Replace error handling:
`typescript
// 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:
`typescript
import { validateRequest } from '90dc-core';
router.post('/users',
validateRequest({ body: CreateUserSchema }),
handler
);
`See USAGE_EXAMPLES.md for detailed migration guide.
---
Building & Publishing
`bash
Install dependencies
npm installBuild
npm run buildPublish to npm
npm run publish:build
``---
See CHANGELOG.md for version history.
---
ISC
---
This is part of the 90 Day Challenge microservices architecture. See the main refactor plan for contribution guidelines.