Generate API Types: JSON to TypeScript and SQL to Entities

2026-10-156 min readDeveloper Tools5 tools covered

Hand-writing type definitions from an API response is slow and quietly wrong: a missing field or a wrong type compiles fine and fails at runtime. This covers generating types from real data, and what you must fix by hand afterwards.

Key takeaways
  • The real risk of hand-written types is that a missing field never fails to compile — it only breaks when that code path runs, often in production
  • JSON has one number type, so integers and floats both become number; generating number for integer keys matches JavaScript runtime behaviour
  • When a field value is null, generators can only infer null or any, so these fields need a manual union type or the inferred type will be too narrow
  • Generating entities from DDL drops length and precision constraints, while nullability does come through from NOT NULL
  • Mixed naming conventions across layers (camelCase in the API, snake_case in the database) are the most common source of trivial bugs, and converting them all at once is the fix
  • JSON to XML turns arrays into repeated tags and keeps attributes as @-prefixed keys — both conventions are required for round-trip conversion to match

#The problem with hand-written types is not speed

The real risk is that a missing field never fails to compile. Writing one fewer property produces no warning — you only find out when that code path runs, which is often in production.

Generating from real data removes that blind spot: the field set comes straight from a sample, so nothing is missing. What you are left with is correcting types the generator could not infer, which is both faster and more accurate than writing them from scratch.

InputOutputTool
API responseFront-end typesJSON to TypeScript
CREATE TABLE statementBackend entitySQL to Entity
JSON structureMulti-language modelsJSON to Java Bean
Inconsistent namesSeven naming stylesCase Converter

#Generating TypeScript from an API response

JSON numbers are all inferred as number, and that is correct: the JavaScript runtime does not distinguish integers from floats.

A typical run with JSON to TypeScript:

  1. Copy a real API response — including every field that can appear (see the warning below)
  2. Paste it in; interfaces generate live
  3. Choose whether nested objects become separate interfaces or inline types
  4. Pick T[] or Array<T> for arrays, depending on your team convention
  5. Set the root interface name, then copy or download the .ts file
Warning

Your sample determines the quality. If an optional field happens to be absent from the response you copied, the generator cannot know it exists — and when a field's value is literally null, the inferred type may be null or any, neither of which is what you want. Review those fields afterwards and change them to a union such as string | null.

Knowing the inference rules lets you predict what needs fixing:

JSON valueGenerated typeWhen to change it
"abc"stringDate strings may warrant a comment or Date
123 / 1.5numberIDs beyond 2^53 precision should be string
truebooleanUsually fine as-is
nullnull or anyAlmost always needs a union type
Empty array []any[] or never[]Element type must be supplied manually
{...}Separate interface or inline objectDepends on nesting depth

#Generating entity classes from CREATE TABLE

DDL carries more information than JSON, so the generated entities are usually better: nullability, primary keys and column comments all map across.

Paste the full statement into SQL to Entity and it parses the table name, columns, types and comments, with three output targets:

  • Java — fields with @Id primary key annotations, types mapped by table
  • C# — property style with nullable markers
  • TypeScript — for isomorphic projects
Note

Storage-level constraints like VARCHAR(255) are not carried into the entity, because entities rarely care about storage width. Nullability is mapped, from NOT NULL — verify that carefully. Date and time types also differ per language: Java typically uses LocalDateTime while TypeScript uses string, so unify them by hand after generating.

#Inconsistent naming is a hidden cost

camelCase in the API, snake_case in the database, CONSTANT_CASE for constants — this cross-layer mismatch is the most common source of trivial bugs, and it is scattered across many files.

Case Converter takes a name in any style, detects the word boundaries, and returns seven styles:

StyleExampleTypical use
camelCaseuserNameJavaScript / Java variables and fields
PascalCaseUserNameClass names, React components
snake_caseuser_namePython, database columns
kebab-caseuser-nameCSS classes, URL paths
CONSTANT_CASEUSER_NAMEConstants, environment variables
dot.caseuser.nameConfig keys, i18n keys
Title CaseUser NameDisplay text and labels

Its splitter handles underscores, dashes, spaces, camelCase boundaries and digit boundaries, so HTTPServerURL, user_name and user-name all split correctly.

Tip

Before a bulk rename, confirm the split is right in Case Converter first. Acronyms such as ID and URL are treated differently by different tools, and checking one sample can save you a rollback.

#Two conventions for JSON and XML conversion

Arrays become repeated tags and attributes are marked with an @ prefix — those two conventions are what make a round trip work.

With JSON to XML:

JSON
{
  "user": {
    "@id": "1001",
    "name": "Alice",
    "tags": ["frontend", "backend"]
  }
}

becomes:

XML
<user id="1001">
  <name>Alice</name>
  <tags>frontend</tags>
  <tags>backend</tags>
</user>

That is all you need to remember: @id marks an attribute rather than a child element, and the tags array became two same-named tags.

Converting back restores the array — but if the original XML held only one <tags>, it returns as a plain string rather than an array. That ambiguity belongs to XML itself, not to the converter.

#Which tool for which job

Your inputDesired outputTool
API response JSONTypeScript typesJSON to TypeScript
CREATE TABLE statementJava / C# / TS entitySQL to Entity
JSON structureMulti-language beansJSON to Java Bean
A single variable nameSeven naming stylesCase Converter
JSON dataXML, or the reverseJSON to XML
Messy JSONReadable indented outputJSON Formatter

Before generating, confirm the sample is valid JSON. Otherwise the generator receives something that fails to parse, and the error can look like a generator problem when it is not.

#Frequently asked questions

#Can the generated types be used as-is?

Yes, but nullable fields must be reviewed by hand. This is the one thing a generator cannot infer reliably: when a field's value is exactly null, the result may be null or any, and neither is the ideal type for an optional field. Change it to a union such as string | null, then enable strict null checks in TypeScript to confirm.

#Why do integers become number in TypeScript?

Because JavaScript has a single numeric type, backed by a double-precision float — there is no separate integer. Generating number for JSON integers matches runtime behaviour. Be aware that integers beyond 2^53 lose precision, so fields such as snowflake IDs should be serialised as strings at the API layer.

#Why does an empty array generate any[]?

An empty array contains nothing to infer from. That is missing information rather than a tool defect. Either include one realistic element in your sample, or set the type manually. Keep samples close to real data instead of hand-trimming them.

#Are database types mapped completely?

Length and precision constraints are not, so VARCHAR(255) becomes string or String. Nullability is mapped from NOT NULL, and primary keys are detected and annotated. Date and time types vary most between languages, so normalise them per project convention after generating.

#Can case conversion handle all-caps acronyms?

It splits them, but conventions differ between tools. For something like HTTPServerURL, some tools produce http, server, url while others keep HTTP intact. Check one sample before a global rename so you do not discover the mismatch afterwards.

Tools used in this guide

Related guides