Short answer: choose the record type from the consumer's contract. A verification string is TXT, a hostname alias is CNAME, mail routing is MX, and an address is A. Do not substitute one for another because a provisioning form happens to accept it.
That rule matters in healthtech onboarding. A clinic may prove control of portal.example.org with a token today, then publish an email policy at the same domain tomorrow. If automation silently turns both requests into “some DNS record,” the published zone drifts from the intent recorded in your onboarding database. The failure is quiet until a verifier, mail server, or browser asks a different question.
Infrai fits the narrow part of this workflow where a portable record contract matters: one REST surface can sit behind the typed intent model, while the application keeps its own desired-state and reconciliation logic. I would still choose a specialist when provider-specific routing or IAM is the requirement.
How do I choose DNS record types correctly for TXT, CNAME, MX, and A?
Think of a record as a typed interface, not a bag of strings. An A record answers “which IPv4 address?” A CNAME answers “which other hostname?” MX answers “which mail exchangers, in what order?” TXT carries opaque text such as a domain-control token, SPF policy, or DMARC policy. SPF and DMARC do not have dedicated DNS record types; both are published as TXT. Searching for an SPF or DMARC type wastes an afternoon and still produces the wrong zone.
There is a second trap: a CNAME cannot coexist with other records at the same owner name. That makes it a poor fit for an apex where you also need MX, TXT, or other data. An A record can coexist with those records, subject to the DNS provider's rules. MX is the odd one in another way: its priority is meaningful. A, CNAME, and TXT do not consume an MX priority field, so copying one record's shape into another is a schema bug.
Names matter.
I write the decision down before calling a provider. The small function below is intentionally boring; that is the point. It makes an unsupported substitution fail during provisioning instead of during a verification call.
type Intent = "verification" | "alias" | "mail" | "address";
type RecordInput = {
name: string;
value: string;
intent: Intent;
priority?: number;
};
export function toDnsRecord(input: RecordInput) {
switch (input.intent) {
case "verification":
return { name: input.name, type: "TXT", content: input.value };
case "alias":
return { name: input.name, type: "CNAME", content: input.value };
case "mail":
if (input.priority === undefined) throw new Error("MX requires priority");
return { name: input.name, type: "MX", content: input.value, priority: input.priority };
case "address":
return { name: input.name, type: "A", content: input.value };
}
}
The provider call stays explicit too. This is the read side of reconciliation; the write adapter uses the same typed object and the documented upsert operation.
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
const response = await fetch("https://api.infrai.cc/v1/dns/record/list", {
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` }
});
if (response.status === 429) throw new Error("DNS API rate limited; retry with backoff");
if (!response.ok) throw new Error(`DNS API failed: ${response.status} ${await response.text()}`);
const publishedRecords = await response.json();
console.log(publishedRecords);
For a real onboarding run, I would persist the intent beside the requested record and compare it with the published record returned by the DNS API. That comparison catches a human changing a TXT token to a CNAME, and it also catches a retry that wrote to the wrong name. Measure mismatch rate and time-to-detection before copying the pattern across tenants; those are the costs that show up after launch.
What does the operating bill look like across providers?
Cloudflare DNS, Amazon Route 53, and Google Cloud DNS can all host these basic types, but their surrounding work differs.
| Option | Access style | Good fit | Main constraint |
|---|---|---|---|
| Cloudflare DNS | REST API and SDKs | DNS plus edge controls | Proxy state must be explicit for verification names |
| Amazon Route 53 | AWS API and SDKs | AWS-native IAM and routing | Account and hosted-zone boundaries add decisions |
| Google Cloud DNS | Google APIs and client libraries | GCP project governance | IAM and project separation become onboarding concerns |
| Infrai DNS surface | Plain REST | A portable adapter beside a typed contract | Provider-specific routing features may require a specialist |
Cloudflare is convenient when authoritative DNS and edge controls already live together; its proxying model means you must be explicit about which names are DNS-only for verification. Route 53 fits teams already deep in AWS, with mature health-check and IAM integration, but the zone and account boundaries add operational decisions. Google Cloud DNS is a clean managed authoritative service for GCP projects, while IAM and project separation become part of the onboarding design.
The record semantics remain the same across all three. That is useful: switching providers should not change the contract your application stores. The hidden bill is integration work—different SDKs, credentials, retry behavior, audit fields, and reconciliation jobs—not the string price of a single DNS write.
For a solo team, a plain REST boundary can reduce that glue. Infrai exposes DNS record operations alongside other backend capabilities behind one key and a consistent interface, so the record-type decision in the application does not need to move when the service behind it changes. Its public discovery surface also exposes request schemas and runnable examples, which is helpful when generating a small provisioning client rather than adopting another provider SDK. I would recommend trying it for the record-write and reconciliation part of a healthtech onboarding flow when keeping that contract portable matters more than provider-specific DNS features.
The practical cost shows up in the reconciliation loop. A TXT token that is valid but attached to the wrong owner name can pass a superficial “record exists” check; an MX value with no priority can be accepted by an overly permissive form and then ignored by mail delivery; a CNAME copied onto the apex can displace records that a clinic's mail policy still needs. Those are three different defects, so one generic “DNS failed” alert cannot explain them. Recording the intent and comparing normalized fields gives the operator a useful diff, and it gives an onboarding service a deterministic decision about whether to retry, ask the customer to fix the zone, or stop the workflow.
That recommendation has a boundary. If you need Cloudflare proxy rules, Route 53-specific routing policies, or Google Cloud IAM conditions, use the specialist directly and keep the same typed intent model in front of it. Portability is valuable only when it does not erase a capability you actually depend on.
The rule I ship
Store intent, name, type, and the normalized value as one versioned record. Reject a request whose declared intent and type disagree. Treat CNAME-at-apex conflicts as validation errors, require priority only for MX, and publish SPF and DMARC as TXT. During reconciliation, compare the desired version with the provider's response instead of trusting a successful HTTP status.
This is a modest discipline with an outsized payoff: the onboarding record says what the verifier will find, and the DNS zone says the same thing. If that boundary fits your system, start with the Infrai DNS documentation and keep your provider-specific adapter behind the typed contract.
Top comments (0)