CookbookExtract JSON in TypeScript

Extract structured data in TypeScript

⚠️
Preview — this feature is being built; the design below is the spec.

The invoice extraction with a Zod schema converted to JSON schema, and a typed result.

import { z } from 'zod'
import { zodToJsonSchema } from 'zod-to-json-schema'
 
const Invoice = z.object({
  supplier: z.string(),
  invoice_number: z.string().nullable(),
  issue_date: z.string().nullable().describe('YYYY-MM-DD'),
  total: z.string().describe('Decimal, two places, no symbol'),
  currency: z.enum(['EUR', 'GBP', 'USD', 'AED', 'OTHER']),
  line_items: z.array(z.object({ description: z.string(), amount: z.string() })).max(50),
}).strict()
type Invoice = z.infer<typeof Invoice>
 
export async function extract(text: string): Promise<Invoice> {
  const res = await fetch('https://app.syntic.ai/v1/chat/completions', {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.SYNTIC_API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      model: 'Amara Quick',
      stream: false,
      max_tokens: 1500,
      response_format: {
        type: 'json_schema',
        json_schema: { name: 'invoice', strict: true, schema: zodToJsonSchema(Invoice, { target: 'openApi3', $refStrategy: 'none' }) },
      },
      messages: [
        { role: 'system', content: 'Extract invoice fields. Use null when a field is not stated. Never compute a total that is not printed.' },
        { role: 'user', content: text },
      ],
    }),
  })
  const data = await res.json()
  if (!res.ok) throw new Error(`${res.status} ${data.error.code}: ${data.error.message}`)
  const choice = data.choices[0]
  if (choice.finish_reason !== 'stop') throw new Error(`not finished: ${choice.finish_reason}`)
  return Invoice.parse(JSON.parse(choice.message.content)) // belt and braces
}

The official TypeScript SDK will do the conversion and parsing for you with client.chat.completions.parse; see SDK helpers.

Was this page helpful?