pyRPC
← Back to Blog

The npx daemon: 715× faster type generation

·10 min read

Before v0.8.0, every codegen run called npx json-schema-to-typescript as a subprocess. That took 3.3 seconds per call. With a file watcher regenerating types on every save, that was unacceptable. The fix: keep npx running in the background.

The problem

npx is a Node.js wrapper that resolves packages, downloads them if needed, and runs the binary. Every invocation pays the startup cost: resolve the package, load V8, parse json-schema-to-typescript, compile TypeScript schemas. That is 3.3 seconds on a cold start.

The solution: a persistent process

jsonschema-ts v0.3.0 spawns a single Node.js process that stays alive. It loads json-schema-to-typescript once and keeps it in V8's code cache. Subsequent conversions read from stdin, write to stdout:

# First call (cold): 3.3s
# Second call (warm): 4.6ms
# 715× faster

How it works in pyrpc

The pyrpc dev file watcher triggers codegen on Python file changes. The codegen step calls jsonschema-ts to convert Pydantic models to TypeScript interfaces. With the daemon:

  1. First save: 3.3s (daemon starts, cold load)
  2. Every save after: ~4.6ms (warm, daemon is alive)

The daemon is shared across pyrpc codegen, pyrpc dev, and the @pyrpc/types postinstall script. One process, many callers.

Windows fix

On Windows, npx is a script file, not an executable. subprocess.run(["npx", ...]) fails with [WinError 2]. The fix in jsonschema-ts v0.2.1: use "npx.cmd" when os.name == "nt".

File watcher debounce

Alongside the daemon, the file watcher debounce dropped from 1.6s to 200ms. Combined, the feedback loop from saving a Python file to seeing updated TypeScript types is now sub-second.

Codegen template internals · Client docs