@jentic/problem-details
v1.0.1
Published
RFC 9457 Problem Details types for Jentic APIs
Downloads
182
Readme
@jentic/problem-details (TypeScript)
RFC 9457 Problem Details types for Jentic APIs.
Installation
npm install @jentic/problem-detailsQuick Start
Type Definitions
import type { ProblemDetail, ErrorItem } from '@jentic/problem-details';
// Use as response types
interface ApiError extends ProblemDetail {
// Add custom fields if needed
}
// Type-safe error handling
function handleError(error: ProblemDetail) {
console.log(`${error.status}: ${error.title}`);
console.log(error.detail);
if (error.errors) {
error.errors.forEach(err => {
if (err.pointer) {
console.log(` - ${err.pointer}: ${err.detail}`);
}
});
}
}Creating Problem Details
import { createProblemDetail, createErrorItem } from '@jentic/problem-details';
// Simple error
const notFound = createProblemDetail.notFound('User not found', {
instance: '/api/users/123',
});
// Validation error with multiple fields
const validation = createProblemDetail.validationError(
'Multiple validation errors occurred',
{
instance: '/api/users',
errors: [
createErrorItem("Field 'name' is required", { pointer: '#/name' }),
createErrorItem("Field 'email' must be valid", { pointer: '#/email' }),
],
}
);Error Handling with Fetch
import { ProblemDetailError } from '@jentic/problem-details';
async function fetchUser(id: string) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw await ProblemDetailError.fromResponse(response);
}
return response.json();
}
// Usage
try {
const user = await fetchUser('123');
} catch (err) {
if (err instanceof ProblemDetailError) {
console.error('API Error:', err.problemDetail);
// Access structured error data
if (err.problemDetail.status === 404) {
console.log('User not found');
}
// Show validation errors to user
if (err.problemDetail.errors) {
err.problemDetail.errors.forEach(error => {
console.log(`${error.pointer}: ${error.detail}`);
});
}
} else {
console.error('Unexpected error:', err);
}
}Type Guards
import { isProblemDetail, isErrorItem } from '@jentic/problem-details';
// Check if response is a ProblemDetail
const response = await fetch('/api/users');
const data = await response.json();
if (isProblemDetail(data)) {
console.error('API returned error:', data.detail);
if (data.errors && data.errors.every(isErrorItem)) {
// TypeScript knows these are ErrorItems
data.errors.forEach(err => console.log(err.detail));
}
}React Example
import { useState } from 'react';
import { ProblemDetailError, type ProblemDetail } from '@jentic/problem-details';
function UserForm() {
const [error, setError] = useState<ProblemDetail | null>(null);
async function handleSubmit(data: FormData) {
try {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(data),
});
if (!response.ok) {
throw await ProblemDetailError.fromResponse(response);
}
// Success
} catch (err) {
if (err instanceof ProblemDetailError) {
setError(err.problemDetail);
}
}
}
return (
<form onSubmit={handleSubmit}>
{error && (
<div className="error">
<h3>{error.title}</h3>
<p>{error.detail}</p>
{error.errors && (
<ul>
{error.errors.map((err, i) => (
<li key={i}>
{err.pointer && <strong>{err.pointer}:</strong>} {err.detail}
</li>
))}
</ul>
)}
</div>
)}
{/* Form fields */}
</form>
);
}API Reference
Types
ProblemDetail
interface ProblemDetail {
type?: string; // URI identifying problem type (default: "about:blank")
status?: number; // HTTP status code
title?: string; // Short summary of the problem
detail: string; // Human-readable explanation (required)
instance?: string; // URI reference to this occurrence
code?: string; // Provider-specific error code
errors?: ErrorItem[]; // Array of granular error details
}ErrorItem
interface ErrorItem {
detail: string; // Human-readable error explanation (required)
pointer?: string; // JSON Pointer to request body property
parameter?: string;// Query/path parameter name
header?: string; // Request header name
code?: string; // Provider-specific code
}Functions
createProblemDetail
Factory functions for common HTTP errors:
createProblemDetail.badRequest(detail, options?)— 400createProblemDetail.unauthorized(detail, options?)— 401createProblemDetail.forbidden(detail, options?)— 403createProblemDetail.notFound(detail, options?)— 404createProblemDetail.conflict(detail, options?)— 409createProblemDetail.validationError(detail, options?)— 422createProblemDetail.tooManyRequests(detail, options?)— 429createProblemDetail.serverError(detail, options?)— 500createProblemDetail.serviceUnavailable(detail, options?)— 503
createErrorItem(detail, options?)
Helper to create validation error items.
isProblemDetail(obj)
Type guard to check if an object is a ProblemDetail.
isErrorItem(obj)
Type guard to check if an object is an ErrorItem.
Classes
ProblemDetailError extends Error
Custom error class carrying RFC 9457 Problem Details data.
Constructor:
new ProblemDetailError(problemDetail: ProblemDetail)Properties:
problemDetail: ProblemDetail— The problem detail data
Static Methods:
ProblemDetailError.fromResponse(response: Response): Promise<ProblemDetailError>— Create from a fetch Response
Methods:
toJSON(): ProblemDetail— Serialize to JSON
Development
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Type checking
npm run typecheck
# Coverage
npm run test:coverageStandards
License
@jentic/problem-details is licensed under Apache 2.0 license. @jentic/problem-details comes with an explicit NOTICE file containing additional legal notices and information.
