
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
@crashbytes/typed-api
Advanced tools
Type-safe API client builder with full TypeScript inference. Define routes with schemas, get compile-time safety. Zero dependencies.
Type-safe API client builder with full TypeScript inference. Define routes with schemas, get compile-time safety. Zero dependencies.
npm: https://www.npmjs.com/package/@crashbytes/typed-api
Building API clients usually means one of two things: writing verbose, repetitive fetch calls with manual type annotations, or pulling in heavyweight code-generation tools. Both approaches have drawbacks -- manual clients drift out of sync with your API, and codegen adds build complexity and dependencies.
typed-api takes a different approach. You define your API routes once with lightweight schemas, and the TypeScript compiler infers everything -- request types, response types, and method signatures. No code generation, no heavy dependencies, no runtime overhead beyond a thin fetch wrapper.
npm install @crashbytes/typed-api
import { createClient, defineRouter, route, s } from '@crashbytes/typed-api'
// 1. Define your API routes with schemas
const router = defineRouter({
getUser: route({
method: 'GET',
path: '/users/:id',
output: s.object({
id: s.string(),
name: s.string(),
email: s.string(),
}),
}),
createUser: route({
method: 'POST',
path: '/users',
input: s.object({
name: s.string(),
email: s.string(),
}),
output: s.object({
id: s.string(),
name: s.string(),
email: s.string(),
}),
}),
listUsers: route({
method: 'GET',
path: '/users',
output: s.array(
s.object({
id: s.string(),
name: s.string(),
}),
),
}),
deleteUser: route({
method: 'DELETE',
path: '/users/:id',
output: s.object({ ok: s.boolean() }),
}),
})
// 2. Create a type-safe client
const api = createClient(router, {
baseUrl: 'https://api.example.com',
headers: {
Authorization: 'Bearer my-token',
},
})
// 3. Use it -- everything is fully typed
const user = await api.createUser({ name: 'Alice', email: 'alice@example.com' })
// user: { id: string, name: string, email: string }
const users = await api.listUsers()
// users: { id: string, name: string }[]
const health = await api.getUser()
// No input required -- TypeScript knows this route has no input schema
The s object provides lightweight schema builders for defining input and output shapes:
| Builder | Type | Description |
|---|---|---|
s.string() | string | Validates strings |
s.number() | number | Validates numbers |
s.boolean() | boolean | Validates booleans |
s.literal(value) | literal type | Validates exact value match |
s.object(shape) | { ... } | Validates object with typed fields |
s.array(schema) | T[] | Validates array of items |
s.optional(schema) | T | undefined | Makes a schema optional |
Use the Infer type helper to extract the TypeScript type from any schema:
import { s, type Infer } from '@crashbytes/typed-api'
const UserSchema = s.object({
id: s.string(),
name: s.string(),
age: s.number(),
admin: s.boolean(),
tags: s.array(s.string()),
nickname: s.optional(s.string()),
})
type User = Infer<typeof UserSchema>
// { id: string; name: string; age: number; admin: boolean; tags: string[]; nickname: string | undefined }
The client throws ApiError for non-2xx responses:
import { createClient, ApiError } from '@crashbytes/typed-api'
try {
const user = await api.getUser()
} catch (err) {
if (err instanceof ApiError) {
console.error(err.status) // HTTP status code
console.error(err.body) // Response body
}
}
Schema validation errors throw TypeError for invalid inputs or outputs:
// Input validation -- throws before the request is sent
await api.createUser({ name: 123 }) // TypeError: Expected string, got number
// Output validation -- throws after receiving malformed response
// If the server returns { status: 123 } instead of { status: "ok" }
// TypeError: Expected string, got number
s.string() / s.number() / s.boolean()Create primitive schemas with runtime type checking.
s.literal(value)Create a schema that matches an exact value.
const role = s.literal('admin')
role.parse('admin') // 'admin'
role.parse('user') // throws TypeError
s.object(shape)Create an object schema from a shape of named schemas.
const user = s.object({ name: s.string(), age: s.number() })
user.parse({ name: 'Alice', age: 30 }) // { name: 'Alice', age: 30 }
s.array(schema)Create an array schema that validates each item.
const nums = s.array(s.number())
nums.parse([1, 2, 3]) // [1, 2, 3]
s.optional(schema)Wrap a schema to accept undefined or null (returns undefined).
const maybe = s.optional(s.string())
maybe.parse('hello') // 'hello'
maybe.parse(undefined) // undefined
maybe.parse(null) // undefined
route(config)Define a single API route with method, path, optional input schema, and output schema.
defineRouter(routes)Group routes into a router object. Returns the routes with full type information preserved.
createClient(router, options)Create a type-safe client from a router definition.
Options:
baseUrl -- Base URL for all requestsheaders -- Default headers included in every requestfetch -- Custom fetch implementation (defaults to globalThis.fetch)ApiErrorError class thrown for non-2xx HTTP responses.
Properties:
status -- HTTP status codebody -- Response body (parsed from text)message -- "API error {status}"MIT
FAQs
Type-safe API client builder with full TypeScript inference. Define routes with schemas, get compile-time safety. Zero dependencies.
The npm package @crashbytes/typed-api receives a total of 29 weekly downloads. As such, @crashbytes/typed-api popularity was classified as not popular.
We found that @crashbytes/typed-api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.