# Why the SEO data provider sits behind a port

> netweft's real data comes from DataForSEO, but nothing above the port knows it. The mock implementation is not a test convenience; it makes the product run.

Source: https://netweft.com/blog/the-provider-sits-behind-a-port
Updated: 2026-10-11

**netweft's SEO data provider is an interface with two implementations: DataForSEO for real data, and a mock serving deterministic fixtures with no credentials. Nothing above the port knows which answered, so the product runs offline, every test is deterministic, and credit accounting is identical on both paths.**

There is exactly one module in netweft that knows DataForSEO exists. Everything
else — the routes, the worker, the credit accounting, the dashboard — talks to
an interface with a handful of methods on it.

That is not architectural decoration. It buys four specific things.

## The product runs with no vendor account

The mock implementation serves deterministic fixtures and needs no credentials.
`bun run dev` produces a working netweft: projects, keyword research, rank
checks, audits, the dashboard, the assistant. Nobody needs a DataForSEO key to
run the thing, review a change, or reproduce a bug.

A product that requires a paid third-party account to start is a product with a
contributor ceiling, and more immediately, one where every developer's
afternoon costs money.

## Every test is deterministic

Fixtures do not rate limit, do not return a different SERP on Tuesday, and do
not go down. A test suite that talks to a real SERP provider is a test suite
that fails for reasons unrelated to the change under test, and the usual
response — retries, tolerances, skipped assertions — erodes the suite until it
stops catching anything.

## The credit path is the same on both sides

This is the subtle one, and the reason the port sits where it does rather than
one layer lower. Reservation and settlement happen *above* the port. A mock
call reserves credits at the rate card price and settles against a fixture's
reported cost exactly as a real call reserves and settles against the vendor's.

So the accounting is exercised on every run in development, not only in
production against real money. The path that decides whether a workspace is
refused is the path everybody runs all day.

## Switching providers is a module, not a migration

netweft is not planning to leave DataForSEO. But a provider that changes its
pricing, its coverage or its terms is a thing that happens to products, and the
version of that event where one module is rewritten is survivable in a way that
the version where the vendor's response shape is spread through forty route
handlers is not.

The same reasoning applies one shelf down: [the cache and the rate limiter fail
open](/blog/redis-is-optional). Without them every lookup misses and every
request is allowed, which is the intended degraded behaviour rather than a
failure mode. A dependency that can be absent is a dependency that cannot take
the product down.
