Home / Use cases / Next.js

Next.js

Next.js VAT API Integration: Server Components & API Routes

Integrate VAT number validation into Next.js 14 applications using both App Router API routes (server-side proxy) and React Server Components. Covers client-side UX and server-side billing flows.

Next.js 14's App Router introduces two distinct integration points for VAT validation: API routes for client-side form validation, and React Server Components for server-side data fetching during page renders. Understanding when to use each is important. Client-side form validation — where the user types a VAT number and sees immediate feedback — belongs in a Client Component backed by an API route. Pre-filling a billing page with known company data belongs in a Server Component.

For checkout flows, create a POST API route at app/api/validate-vat/route.ts. This route receives the VAT number from the frontend, calls the TaxID API using the TAXID_API_KEY environment variable (which is only available server-side in Next.js), and returns the filtered result. The frontend never sees the API key. For form state, use useActionState or a standard useState hook with a fetch call — whichever fits your form architecture.

Server Actions (introduced in Next.js 14) offer a third option: define the validation as a server action and call it directly from a Client Component with the 'use server' directive. This eliminates the need for an explicit API route and reduces boilerplate. The downside is that server actions are not cacheable by the browser and add a network round-trip that is less visible than an explicit API route.

For recurring billing flows in Next.js, validation typically happens in a server-side webhook handler rather than in the UI. When Stripe fires an invoice.created webhook, the handler calls the TaxID API to validate the customer's stored VAT number, updates the Stripe customer's tax_exempt status if the validation result has changed, and logs the result for audit compliance. This is the same pattern as the Stripe SaaS guide, implemented in a Next.js Route Handler.

Middleware in Next.js can also play a role: if you have a /dashboard/billing route where users update their VAT number, you can add middleware that re-validates the VAT number on every visit to that route and updates the stored validation status. This keeps your records current without requiring a background job.

Implementation steps

  1. 1

    Create the API route proxy

    Add app/api/validate-vat/route.ts. Parse the vatNumber from the JSON body, extract the 2-letter country prefix, and call GET https://taxid.dev/api/v1/validate/:country/:vat with the server-side TAXID_API_KEY environment variable. Return only { valid, status, companyName, address } to the client — never the raw response or the request_id (unless you need it for client-side display).

  2. 2

    Build the useVatField client hook

    Create a hooks/useVatField.ts file. The hook manages input value, debounced fetch to /api/validate-vat, loading state, and the validation result. Wire it to a controlled input. Expose onValidVat(vatNumber, companyName) callback for the parent form to capture the validated VAT details.

  3. 3

    Integrate into a Server Action form (optional)

    For forms that use Server Actions, add a validateVat server action in actions/vat.ts marked with 'use server'. Accept FormData, extract vatNumber, call the TaxID API directly (server-side), and return the result. Invoke with useActionState in the Client Component. This replaces the API route approach and is cleaner for progressive-enhancement forms.

  4. 4

    Server Component: pre-fill billing from stored VAT

    In a Server Component (no 'use client' directive), you can fetch VAT validation data directly on render. Read the customer's stored vatNumber from your database, call the TaxID API to get the latest status, and pass the result as props to the billing form Client Component. This pattern is useful for billing settings pages where you want to show the current validation status without a client-side API call on load.

  5. 5

    Webhook handler for subscription re-validation

    Add app/api/webhooks/stripe/route.ts. On invoice.upcoming events, read the Stripe customer's vat_number metadata, call the TaxID API, and if the status has changed (e.g., went from active to inactive), update the customer's tax_exempt flag in Stripe and notify your finance team via email or Slack. Log every validation with request_id for audit.

Code example

Node.js

const res = await fetch(
  'http://localhost:3000/api/v1/validate/DE/DE123456789',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { valid, status, company_name, company_address } = await res.json();

if (valid) {
  console.log(`Valid EU business: ${company_name}`);
} else if (status === 'service_unavailable') {
  // VIES is temporarily down — retry or allow with manual check
  console.log('VIES unavailable — check back in a few minutes');
} else {
  console.log('Invalid VAT number — charge local tax rate');
}

Python

import requests

res = requests.get(
    "http://localhost:3000/api/v1/validate/DE/DE123456789",
    headers={"Authorization": "Bearer YOUR_API_KEY"}
)
data = res.json()

if data["valid"]:
    print(f"Valid: {data['company_name']}")
elif data["status"] == "service_unavailable":
    print("VIES temporarily unavailable")
else:
    print("Invalid VAT number")

API response

The TaxID API returns a consistent JSON response for every validation request:

200 OK (active)valid
{
  "valid": true,
  "status": "active",
  "country_code": "DE",
  "vat_number": "123456789",
  "company_name": "Example GmbH",
  "company_address": "Musterstraße 1, 10115 Berlin",
  "request_date": "2026-05-10T00:00:00.000Z",
  "cached": false,
  "request_id": "req_01j..."
}

Error handling

The API uses a consistent Stripe-style error format. Always handle service_unavailable separately — VIES has occasional downtime and you should not reject valid customers during outages.

active

VAT number is valid and the business is registered

invalid

VAT number format is wrong or not registered in VIES

service_unavailable

VIES or the national system is temporarily down — retry later

Frequently asked questions

Should I use an API route or a Server Action for VAT validation?

Use an API route if you need the validation endpoint to be callable from outside Next.js (e.g., from a mobile app or external service). Use a Server Action if the validation is only ever called from within your Next.js application — it reduces boilerplate. For checkout flows, either approach works; the API route is more explicit and easier to test with curl.

How do I handle TAXID_API_KEY in Next.js so it's never exposed to the browser?

Define it in .env.local as TAXID_API_KEY=vat_xxxx (without the NEXT_PUBLIC_ prefix). Next.js only exposes environment variables prefixed with NEXT_PUBLIC_ to the browser. Any variable without that prefix is only available in server-side code — API routes, Server Components, Server Actions, and middleware.

Can I use Edge Runtime for the API route?

Yes. The TaxID API is a standard REST endpoint that works with the Fetch API available in Edge Runtime. Add export const runtime = 'edge' to your validate-vat/route.ts. Edge Runtime is faster globally but does not support Node.js-specific APIs — since the validation only needs fetch and basic string manipulation, Edge is fully compatible.

Further reading

Evaluating EU VAT APIs? Compare TaxID with:

Ready to implement?

Get your free API key and start validating EU VAT numbers today.

Get free API key

Related use cases

Stripe EU VAT: Validate Tax IDs Before Charging Customers

Stripe EU VAT integration guide: validate EU VAT numbers server-side before applying zero-rate B2B e...

UK VAT validation in Shopify B2B

Validate UK VAT numbers for B2B customers in Shopify. Required under UK Making Tax Digital rules for...

WooCommerce Spain NIF/CIF validation

Validate Spanish NIF and CIF numbers in WooCommerce checkout. Automatically apply B2B tax exemptions...