DEV Community

Yash Garudkar
Yash Garudkar

Posted on

I built a 1.73 KB axios alternative with zero dependencies - glyde on npm

I got mass an email from npm in March 2026 about the axios supply chain attack. A North Korean state actor compromised a maintainer and injected malicious code into the most popular HTTP client on npm. Millions of projects affected overnight.

That's when I looked at what axios actually does: it merges config, builds URLs, adds headers, runs interceptors, and wraps the response. All of that is maybe 200 lines of TypeScript on top of native fetch. But axios ships 53 KB gzipped with 2 dependencies that have their own dependencies.

So I built glyde - a TypeScript-first HTTP client with zero runtime dependencies. 1.73 KB gzipped.

What it looks like

import plane from "glyde"

const api = plane({ baseURL: "https://api.example.com" })

// Typed GET
const { data } = await api.get<User[]>("/users")

// POST with body
await api.post("/users", { name: "Yash", role: "admin" })

// Query params
await api.get("/search", { params: { q: "glyde", page: 1 } })
Enter fullscreen mode Exit fullscreen mode

plane() creates an independent instance with its own config and interceptors. No shared global state.

Typed errors instead of status code checking

This is what error handling looks like with most HTTP clients:

// The old way
try {
  await axios.get("/data")
} catch (err) {
  if (err.response?.status === 404) { /* maybe? */ }
  // What type is err? Who knows.
}
Enter fullscreen mode Exit fullscreen mode

With glyde, errors have a type hierarchy with type guards:

import { isHttpError, isTimeoutError, isGlydeError } from "glyde"

try {
  await api.get("/data")
} catch (err) {
  if (isHttpError(err)) {
    // TypeScript knows: err.status, err.response, err.config
    console.log(err.status)        // 404
    console.log(err.response?.data) // parsed body
  }

  if (isTimeoutError(err)) {
    // request exceeded timeout
  }
}
Enter fullscreen mode Exit fullscreen mode

The error hierarchy:

GlydeError (base)
+-- HttpError      - non-2xx response (has status, response, config)
+-- TimeoutError   - request exceeded timeout
+-- NetworkError   - fetch failed (DNS, offline, CORS)
Enter fullscreen mode Exit fullscreen mode

Async interceptors

Unlike most libraries that only support synchronous transforms, glyde interceptors are fully async:

// Refresh a token before every request
api.interceptors.request.use(async (config) => {
  const token = await getToken()
  return {
    ...config,
    headers: { ...config.headers, Authorization: `Bearer ${token}` },
  }
})

// Unwrap nested API responses
api.interceptors.response.use((response) => ({
  ...response,
  data: response.data.result,
}))
Enter fullscreen mode Exit fullscreen mode

Next.js App Router pattern

glyde works anywhere fetch exists, but I designed it with Next.js in mind. The recommended pattern:

Server-side (tower):

import plane from "glyde"
import { cookies } from "next/headers"

export async function tower() {
  const api = plane({ baseURL: process.env.API_BASE_URL })
  const cookieStore = await cookies()

  api.interceptors.request.use((config) => {
    const token = cookieStore.get("access_token")?.value
    if (token) {
      config.headers = { ...config.headers, Authorization: `Bearer ${token}` }
    }
    return config
  })

  return api
}
Enter fullscreen mode Exit fullscreen mode

Client-side (passenger):

"use client"
import plane from "glyde"

export const passenger = plane({ baseURL: "/api/proxy" })

passenger.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error?.status === 401) window.location.href = "/login"
    throw error
  }
)
Enter fullscreen mode Exit fullscreen mode

Token refresh? Handle it in Next.js middleware where you can actually write cookies. Don't fight the framework.

The numbers

glyde axios
Size (gzipped) 1.73 KB 53 KB
Dependencies 0 2
TypeScript Written in TS Types bolted on
Engine Native fetch Legacy XHR

Install

npm install glyde
Enter fullscreen mode Exit fullscreen mode

Works in browsers, Node.js 18+, Bun, Deno, and Cloudflare Workers.


I'm actively working on retry plugins, request deduplication, and more interceptor examples. If you have feedback or feature requests, open an issue on GitHub.

Top comments (0)