Files
accounted/lib/api/validate.ts
T
Jakob Wennberg acb85edf4a feat: wire Zod validation into API routes, improve types and components
- Add 8 new Zod schemas (UpdateCustomer, UpdateSupplier, UpdateSupplierInvoice,
  UpdateAccount, BankUnlink, RunReconciliation, CorrectJournalEntry,
  EvaluateMappingRules) and wire validateBody() into 24 JSON-body API routes
- Remove redundant manual validation checks replaced by Zod
- Add comprehensive schema tests (222 tests)
- Improve type definitions in types/index.ts with expanded interfaces
- Refactor extension types (push-notifications, receipt-ocr) for cleaner imports
- Update transaction components (BatchCategorySelector, SwipeCategorizationView,
  QuickReviewDialog, VatTreatmentSelect) and invoice inbox workspace
- Add invoice-inbox utilities and type decoupling tests
- Fix NE-bilaga, SRU export, and invoice PDF template type usage
- Update CLAUDE.md with expanded architecture documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 16:00:38 +01:00

115 lines
2.5 KiB
TypeScript

import { z } from 'zod'
import { NextResponse } from 'next/server'
export interface ValidationSuccess<T> {
success: true
data: T
}
export interface ValidationFailure {
success: false
response: NextResponse
}
export type ValidationResult<T> = ValidationSuccess<T> | ValidationFailure
/**
* Validate a request body against a Zod schema.
*
* Returns `{ success: true, data }` on valid input, or
* `{ success: false, response }` with a 400 NextResponse on failure.
*
* Usage in an API route:
* ```ts
* const result = await validateBody(request, CreateInvoiceSchema)
* if (!result.success) return result.response
* const { data } = result
* ```
*/
export async function validateBody<T>(
request: Request,
schema: z.ZodType<T>,
): Promise<ValidationResult<T>> {
let body: unknown
try {
body = await request.json()
} catch {
return {
success: false,
response: NextResponse.json(
{
error: 'Invalid JSON in request body',
type: 'validation_error',
},
{ status: 400 },
),
}
}
const result = schema.safeParse(body)
if (!result.success) {
const errors = result.error.issues.map((issue) => ({
field: issue.path.join('.'),
message: issue.message,
code: issue.code,
}))
return {
success: false,
response: NextResponse.json(
{
error: 'Validation failed',
type: 'validation_error',
errors,
},
{ status: 400 },
),
}
}
return { success: true, data: result.data }
}
/**
* Validate query parameters (from URL searchParams) against a Zod schema.
*
* Usage:
* ```ts
* const params = validateQuery(request, VatDeclarationQuerySchema)
* if (!params.success) return params.response
* const { data } = params
* ```
*/
export function validateQuery<T>(
request: Request,
schema: z.ZodType<T>,
): ValidationResult<T> {
const url = new URL(request.url)
const raw = Object.fromEntries(url.searchParams.entries())
const result = schema.safeParse(raw)
if (!result.success) {
const errors = result.error.issues.map((issue) => ({
field: issue.path.join('.'),
message: issue.message,
code: issue.code,
}))
return {
success: false,
response: NextResponse.json(
{
error: 'Invalid query parameters',
type: 'validation_error',
errors,
},
{ status: 400 },
),
}
}
return { success: true, data: result.data }
}