This content originally appeared on DEV Community and was authored by Andrey Smolko
When working with REST APIs, having a clean, expressive client can greatly simplify how you write code. Imagine calling your API like this:
const api = createApi('localhost');
api.users.get().then(console.log);
api.posts.post({ title: 'Hello' }).then(console.log);
This syntax is not only clean but reads almost like a natural language. The secret behind this elegant interface? It’s all about dynamic property access powered by JavaScript’s Proxy object.
- Resources like
users
andposts
are not predefined properties of theapi
object. - HTTP methods like
get
andpost
are also dynamically resolved.
This means you don’t have to write boilerplate code to explicitly declare every resource or method. Instead, these identifiers are resolved at runtime!
The core enabler is Proxy
A Proxy lets you intercept fundamental operations on objects, such as property access or function calls, and define custom behaviors.
Here’s the minimal implementation of createApi
:
const createApi = (domain, path = '') => {
return new Proxy(() => {}, {
get(target, key) {
return createApi(domain, `${path}/${key.toString()}`);
},
apply(target, thisArg, args) {
const i = path.lastIndexOf('/');
const method = path.slice(i + 1).toUpperCase();
const url =
domain + (i === -1 ? '/' : path.slice(0, i));
const options = {
method,
headers: {
'Content-Type': 'application/json',
},
};
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) && args[0]) {
options.body = JSON.stringify(args[0]);
}
return fetch(url, options).then((res) => {
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
return res.json();
});
},
});
};
const api = createApi('localhost');
api.users.get().then(console.log);
api.posts.post({ title: 'Hello' }).then(console.log);
How It Works: get
and apply
traps explained
The get
Trap — Capturing Property Access
- Every time you access a property on the proxy — like
api.users
orapi.posts
— theget
trap intercepts that access. - Instead of returning a fixed value, it calls
createApi
recursively, appending the accessed property name (users
,posts
, or HTTP methods likeget
,post
, etc.) to the URL path argument. - This recursive design enables infinitely deep property chains without needing to define them explicitly upfront.
- The recursion ends when the proxy is invoked as a function (via the
apply
trap), which triggers the actual HTTP request.
The apply
Trap — Handling Function Calls
- When you finally call the proxy as a function —
api.users.get()
—theapply
trap intercepts the call. - It extracts the HTTP method from the last part of the path (like
get
,post
, etc.), builds the full URL, and executes the HTTP request usingfetch
.
By combining the get
trap for dynamic property access, the apply
trap for function invocation, and recursion for building URL paths, this approach enables the creation of a powerful, flexible, and elegant API client.
Note: In JavaScript, a trap is a special method defined in a
Proxy
handler object. These traps intercept fundamental operations—like property access, function calls, or property assignments—allowing you to customize how those operations behave.
This post was inspired by the API design found in elysiajs/eden.
This content originally appeared on DEV Community and was authored by Andrey Smolko

Andrey Smolko | Sciencx (2025-06-29T11:49:17+00:00) Dynamic Fluent Interface for API calls (Powered by JS Proxy). Retrieved from https://www.scien.cx/2025/06/29/dynamic-fluent-interface-for-api-calls-powered-by-js-proxy/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.