# Declarative vs Imperative APIs in JavaScript (and Why Chainable Code Feels So Different)

When people talk about “declarative” APIs, they often mean something vague like *cleaner*, *functional*, or *React-style*. But the real difference between declarative and imperative (non-declarative) APIs is much simpler:

> Imperative code tells the computer **how** to do something.  
> Declarative code tells the computer **what** you want.

This distinction matters a lot when designing or choosing APIs, especially in JavaScript/Typescript, where chainable calls, callbacks, and fluent interfaces are everywhere.

Let’s break it down.

---

## The core idea: how vs what

### Imperative (non-declarative)

You control every step of the process:

```js
let result = [];
for (let i = 0; i < users.length; i++) {
  if (users[i].active) {
    result.push(users[i].name.toUpperCase());
  }
}
```

You explicitly manage:

* loops
    
* conditions
    
* intermediate state
    
* execution order
    

You’re describing the *procedure*.

---

### Declarative

You describe the desired result:

```js
const result = users
  .filter(u => u.active)
  .map(u => u.name.toUpperCase());
```

Now:

* no explicit loop management
    
* no mutation
    
* no temporary variables
    
* no control flow
    

You express *intent*, and the runtime figures out execution.

---

## APIs can be declarative too

This idea applies not only to syntax, but to API design.

### Imperative API

```js
db.connect();
db.selectTable("users");
db.addFilter("age > 18");
db.sort("name");
const result = db.execute();
```

You orchestrate every step.

---

### Declarative API

```js
db.query({
  table: "users",
  where: "age > 18",
  orderBy: "name"
});
```

You describe the outcome. The system decides how to achieve it.

---

## What about chainable APIs?

Chaining *does not automatically* mean declarative. It depends on what the chain represents.

### Declarative chaining

```js
users
  .filter(u => u.active)
  .map(u => u.name)
  .sort();
```

This builds a **transformation pipeline**:

* each call describes a rule
    
* nothing mutates external state
    
* no side effects
    
* execution is abstracted
    

You’re defining *what should happen to the data*.

---

### Imperative chaining

```js
request
  .open()
  .setHeader("Authorization", token)
  .send()
  .onSuccess(handleSuccess);
```

Here:

* each call performs an action immediately
    
* order matters operationally
    
* side effects occur
    
* you are commanding steps
    

It’s still imperative even though it looks fluent.

---

## Callbacks don’t decide “declarativity”

Callbacks themselves are neutral. What matters is *who controls execution*.

### Imperative callback

```js
fs.readFile("file.txt", (err, data) => {
  process(data);
});
```

You start the action and provide instructions.

---

### Declarative callback registration

```js
router.get("/users", {
  auth: true,
  handler: (req, res) => { ... }
});
```

You’re declaring behavior, not triggering execution.

---

## Immediate execution vs building a description

A useful mental model:

> If method calls execute immediately → more imperative  
> If method calls build a description to run later → more declarative

Example:

```js
anim.move(10, 0);
anim.rotate(45);
anim.scale(2);
anim.play();
```

Imperative: actions happen now.

Versus:

```js
anim
  .move(10, 0)
  .rotate(45)
  .scale(2)
  .build();
```

Declarative: steps are recorded, not executed yet.

---

## Real-world JavaScript examples

### Mostly declarative

* React JSX
    
* SQL query builders (Prisma, Knex)
    
* CSS
    
* GraphQL queries
    
* Array methods (`map`, `filter`, `reduce`)
    
* RxJS pipelines
    
* Lodash chaining
    

### Mostly imperative

* DOM manipulation
    
* Canvas drawing
    
* Node.js file system APIs
    
* Fetch with manual control flow
    
* jQuery classic chaining
    

---

## A quick checklist

Ask yourself:

| Question | Declarative | Imperative |
| --- | --- | --- |
| Do I manage loops? | ❌ | ✅ |
| Do I manage state? | ❌ | ✅ |
| Do I describe intent? | ✅ | ❌ |
| Is execution hidden? | ✅ | ❌ |

---

## Why this matters

Declarative APIs:

* are easier to reason about
    
* reduce bugs from state management
    
* compose better
    
* are easier to optimize internally
    
* are more readable at scale
    

Imperative APIs:

* offer fine control
    
* are sometimes necessary for performance
    
* fit low-level tasks
    

Most production systems combine both, but understanding the difference helps you design cleaner APIs and recognize why some code “feels” simpler.

---

## Final takeaway

> Declarative: *“Here is what I want.”*  
> Imperative: *“Here is how to do it.”*

Once you start seeing this distinction, you’ll notice it everywhere, from React components to database queries to method chains.
