degu

a small rodent that digs through your JSON

infer types from
sample JSON.

A tiny Rust CLI that reads JSON — from a file or a pipe — and emits a JSON Schema, a TypeScript interface, or a Zod schema. Merge several samples and it figures out which fields are optional.

Copied to clipboard
pure rust zero config reads stdin MIT

what it does

Pipe in JSON.
Get real types.

degu parses one or more sample JSON documents, walks the tree, and merges every observation into a single shape. Fields present in some samples but missing in others become optional. A field that's sometimes null becomes nullable. An integer stays an integer.

Then it emits your format of choice — JSON Schema Draft 7, a TypeScript interface, or a Zod schema — straight to stdout.

$ curl -s https://api.example.com/users/1 \
    | degu -f typescript --name User

export interface User {
  id: number;
  name: string;
  email?: string;
  tags: string[];
  profile: {
    age: number;
    city: string;
  };
}

what degu sees

Three things degu
figures out for you.

001 / merge semantics

Optional fields, detected automatically.

Feed degu several samples of the same resource. A field that shows up in some samples but not others gets the ? treatment. No more shipping interfaces where every property is required because you only looked at one response.

$ cat a.json b.json
{"id":1,"name":"Ada","email":"ada@x.com"}
{"id":2,"name":"Grace"}

$ degu -f typescript --name User a.json b.json
export interface User {
  id: number;
  name: string;
  email?: string;
}
$ echo '{"id":42,"tags":["rust","cli"]}' \
    | degu -f zod --name Post

import { z } from "zod";

export const Post = z.object({
  id: z.number().int(),
  tags: z.array(z.string()),
});
export type Post = z.infer<typeof Post>;

002 / three formats

JSON Schema, TypeScript, or Zod.

Pick your target with -f. JSON Schema Draft 7 for validators, a plain export interface for a TS codebase, or a full Zod schema with .int(), .nullable(), and a matching z.infer type alias.

003 / pipeable

From curl to types in one line.

degu reads stdin. Point curl at any undocumented endpoint, pipe it through degu, and paste the result into your editor. It never writes files and never asks for config.

$ curl -s https://api.github.com/users/octocat \
    | degu -f json-schema

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "login": { "type": "string" },
    "id": { "type": "integer" },
    ...
  }
}

quick start

Install and infer
in under a minute.

01.

Install from crates.io

$ cargo install degu

Requires Rust 1.70+. Single binary, no runtime.

02.

Or build from source

$ git clone https://github.com/iamkorun/degu.git
$ cd degu
$ cargo install --path .
03.

Run it

# infer a TypeScript interface from a file
$ degu -f typescript --name User user.json

# read from stdin
$ curl -s https://api.example.com/users/1 | degu -f typescript --name User

# merge multiple samples for optional fields
$ degu -f zod --name User sample1.json sample2.json

real usage

Wire it into
your workflow.

Strict mode

Treat every observed field as required and lock the shape down with additionalProperties: false. Useful when you want the schema to reject any field degu didn't see.

$ degu --strict -f json-schema user.json

Drop into your project

Save a Zod schema straight into your source tree — now you have runtime validation and a TypeScript type from one command.

$ curl -s $API/users/1 \
    | degu -f zod --name User \
    > src/schemas/user.ts

→ full flag reference in the README