# Generate types from JSON

> Turn a captured JSON response or request body into TypeScript, Swift Codable, Python dataclass, Go or Kotlin types, named from the URL and ready to copy.

Source: https://docs.subcloak.com/cloak-proxy/inspect/generate-types/

Added in Cloak Proxy 0.2.0.

Generate Types reads a JSON body your app really sent or received and writes matching type definitions in TypeScript, Swift, Python, Go or Kotlin. Use it to model an API you don't have a schema for, to start the client code for a new endpoint, or to check what a response actually contains against the types you already have.

## Generate types from a body

1. Right-click a request in the [traffic list](https://docs.subcloak.com/cloak-proxy/inspect/traffic-list/) and choose **Generate Types from Response…**.

   For a request that sent JSON, such as a `POST`, choose **Generate Types from Request Body…** instead. Each item only appears when that body is JSON. Compressed bodies are decompressed first.

2. Choose a **Language**.

3. Check the name of the top-level type. It's taken from the URL; type another one in **Root type name** to change it.

4. Click **Copy** (or press <kbd>↩</kbd>) and paste the code into your project. The button reads **Copied** until you change the language or name.

*[Screenshot: The Generate Types sheet with TypeScript selected and three interfaces, User, Address and Geo, generated from a JSON response. TypeScript generated from a users response. Nested objects become their own types.]*

### The Generate Types sheet

| Control | What it does |
|---|---|
| **Language** | **TypeScript**, **Swift (Codable)**, **Python (dataclass)**, **Go** or **Kotlin (data class)**. Cloak Proxy remembers your choice. |
| **Root type name** | The name of the top-level type. Leave it empty to use the name suggested from the URL. Whatever you type is turned into a type name, so `user profile` becomes `UserProfile`. |
| Code | The generated types. You can select and copy part of it. |
| **Copy** | Copies all the code. |
| **Close** | Closes the sheet (<kbd>Esc</kbd>). |

Under the code, the sheet reminds you that types are inferred from this one body. If the body can't be read as JSON, the sheet says **This body isn't JSON.**

## Languages

The same body in each language. This response comes from `GET https://api.example.com/v1/users/42`:

```json title="Response body"
{
  "id": 42,
  "name": "Ada Park",
  "email": "ada@example.com",
  "score": 9.5,
  "created_at": "2026-01-15T10:00:00Z",
  "tags": ["admin"],
  "teams": [
    { "id": 1, "name": "Mobile" },
    { "id": 2, "name": "Web", "lead": "Ada Park" }
  ],
  "manager": null
}
```

**TypeScript**

```ts
export interface User {
  id: number;
  name: string;
  email: string;
  score: number;
  created_at: string;
  tags: string[];
  teams: Team[];
  manager?: null;
}

export interface Team {
  id: number;
  name: string;
  lead?: string;
}
```

**Swift**

```swift
import Foundation

struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let score: Double
    let createdAt: String
    let tags: [String]
    let teams: [Team]
    let manager: String?

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case email
        case score
        case createdAt = "created_at"
        case tags
        case teams
        case manager
    }
}

struct Team: Codable {
    let id: Int
    let name: String
    let lead: String?
}
```

**Python**

```python
from dataclasses import dataclass
from typing import Any, List, Optional

@dataclass
class User:
    id: int
    name: str
    email: str
    score: float
    created_at: str
    tags: List[str]
    teams: List["Team"]
    manager: Optional[Any] = None

@dataclass
class Team:
    id: int
    name: str
    lead: Optional[str] = None
```

**Go**

```go
type User struct {
	Id int64 `json:"id"`
	Name string `json:"name"`
	Email string `json:"email"`
	Score float64 `json:"score"`
	CreatedAt string `json:"created_at"`
	Tags []string `json:"tags"`
	Teams []Team `json:"teams"`
	Manager *interface{} `json:"manager,omitempty"`
}

type Team struct {
	Id int64 `json:"id"`
	Name string `json:"name"`
	Lead *string `json:"lead,omitempty"`
}
```

**Kotlin**

```kotlin
@Serializable
data class User(
    @SerialName("id") val id: Long,
    @SerialName("name") val name: String,
    @SerialName("email") val email: String,
    @SerialName("score") val score: Double,
    @SerialName("created_at") val createdAt: String,
    @SerialName("tags") val tags: List<String>,
    @SerialName("teams") val teams: List<Team>,
    @SerialName("manager") val manager: JsonElement? = null
)

@Serializable
data class Team(
    @SerialName("id") val id: Long,
    @SerialName("name") val name: String,
    @SerialName("lead") val lead: String? = null
)
```

### What each language produces

| Language | Each object becomes | Optional fields | Field names |
|---|---|---|---|
| **TypeScript** | An `export interface` | `field?: type` | The JSON keys as they are. Keys that aren't valid identifiers, such as `2fa` or `user-id`, are quoted. |
| **Swift (Codable)** | A `struct … : Codable`, after `import Foundation` | `let field: Type?` | camelCase. When any name differs from its key, a `CodingKeys` enum maps every property to its key. Swift keywords such as `default` and `class` are wrapped in backticks. |
| **Python (dataclass)** | A `@dataclass` class, after the `dataclasses` and `typing` imports | `field: Optional[type] = None`, placed after the required fields, because a dataclass can't have a field with a default before one without | snake_case. There's no mapping back to the original keys, so convert camelCase keys when you load the data. |
| **Go** | A `type … struct` | A pointer with `omitempty`: `*string` and `json:"lead,omitempty"` | PascalCase, with the original key in the `json` tag. `id` becomes `Id`. |
| **Kotlin (data class)** | A `@Serializable data class` for kotlinx.serialization | `Type? = null` | camelCase, with the original key in `@SerialName`. |

The Go output has no `package` line and isn't aligned; run `gofmt` on it. The Kotlin output has no imports; add the kotlinx.serialization imports for `Serializable`, `SerialName` and, if it's used, `JsonElement`. Only Swift output escapes keywords: if a key is a keyword in another language, such as `class` in Python, rename that field yourself.

### JSON values and their types

| JSON value | TypeScript | Swift | Python | Go | Kotlin |
|---|---|---|---|---|---|
| String | `string` | `String` | `str` | `string` | `String` |
| Whole number, such as `42` | `number` | `Int` | `int` | `int64` | `Long` |
| Number with a decimal point or exponent, such as `9.5` | `number` | `Double` | `float` | `float64` | `Double` |
| `true` or `false` | `boolean` | `Bool` | `bool` | `bool` | `Boolean` |
| Object | A named type | A named type | A named type | A named type | A named type |
| Array | `Item[]` | `[Item]` | `List[Item]` | `[]Item` | `List<Item>` |
| `null` only, or mixed types | `null` or `unknown` | `String` | `Any` | `interface{}` | `JsonElement` |

## How types are inferred

Cloak Proxy works only from the one body you picked, so the result is as good as the example:

- **Objects become named types.** Each nested object gets its own type, named from its key: `address` becomes `Address`, `geo` becomes `Geo`. If two different objects would get the same name, the second gets a number: `Address2`.
- **Array items are merged.** All the items in an array are combined into one item type, named from the singular of the key: `teams` gives `Team`, `categories` gives `Category`, `addresses` gives `Address`.
- **Missing and null fields are optional.** A field that's missing from some items in an array, or that's `null`, becomes optional.
- **Whole and decimal numbers.** A number without a decimal point or exponent is an integer type. If some items have `1` and others `1.5` for the same field, it becomes a decimal type.
- **Conflicting types fall back to a catch-all.** A field that's a string in one item and a number in another, or that's only ever `null`, gets the catch-all type in the last row of the table above.
- **Empty arrays** have an unknown item type, such as `unknown[]` in TypeScript.
- **Top-level arrays and values.** A body that's an array of objects produces the item type. A body that's an array of strings or numbers, or a single value, becomes a type alias, such as `export type Tag = string[];` in TypeScript.
- **Names come from keys.** Keys are split into words at punctuation and lowercase-to-uppercase changes, then joined in each language's style. A name that would start with a digit gets a prefix: `T` for type names and Go fields, and `_` for Swift, Python and Kotlin fields. TypeScript quotes the key instead.

> **Check the result against more than one response:**
> A field that happens to be filled in this response may be `null` or missing in others, and an empty array tells Cloak Proxy nothing about its items. Generate types from a full example, compare a few responses with [Compare requests](https://docs.subcloak.com/cloak-proxy/inspect/compare-requests/), and adjust optional fields by hand before you rely on them.

## The suggested type name

When **Root type name** is empty, the top-level type is named after the last meaningful part of the URL's path, in singular:

| Request | Suggested name |
|---|---|
| `GET /v1/users` | `User` |
| `GET /v1/users/42` | `User` |
| `GET /v1/users/3f2a9c1e-7b4d-4e8a-9c1f-0a2b3c4d5e6f` | `User` |
| `GET /api/categories` | `Category` |
| `GET /v2/user-profiles` | `UserProfile` |
| `POST /v1/orders` (request body) | `OrderRequest` |
| `GET /` | `Root` |

Numbers, long hexadecimal IDs and UUIDs, and version segments such as `v1` are skipped. Types generated from a request body get `Request` added to the name.

## Generate types from an AI assistant

The [MCP server](https://docs.subcloak.com/cloak-proxy/automation/mcp-server/) has a read-only `generate_types` tool. It takes a captured request's `id`, and optionally `side` (`response`, the default, or `request`), `language` (`typescript`, `swift`, `python`, `go` or `kotlin`) and `root_name`. See the [MCP tools reference](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/).

## Related

  - [Body viewers](https://docs.subcloak.com/cloak-proxy/inspect/body-viewers/): Explore JSON with the tree, JSONPath and jq before you generate types.
  - [Copy as code](https://docs.subcloak.com/cloak-proxy/inspect/copy-as-code/): Copy the request itself as cURL or code in many languages.
  - [Endpoints](https://docs.subcloak.com/cloak-proxy/insights/endpoints/): A catalog of every API endpoint your app called.
  - [Turn traffic into API docs](https://docs.subcloak.com/cloak-proxy/guides/turn-traffic-into-api-docs/): Go from captured requests to an OpenAPI spec and docs.
