# REST Client

## Introduction

The EJS **REST Client** is a powerful facility for working with REST APIs. With built-in path prefixing, token management, advanced error handling, convenience methods, and a Promise-based architecture, making API requests has never been easier!

Leveraging the strengths of the EJS HTTP Client 'under-the-hood', it is also possible to dig into the details of requests and responses, whether or not the request was successful.

## The RestClient Class <a href="#the-restclient-class" id="the-restclient-class"></a>

### Synopsis <a href="#synopsis" id="synopsis"></a>

```javascript
class RestClient {
    constructor(string basePath, string token);

    api(string method, string path, Object args, Object headers = {}) : Promise<Response>

    get(string path, Object args = {}) : Promise<Response>
    post(string path, Object args = {}) : Promise<Response>
    put(string path, Object args = {}) : Promise<Response>
    delete(string path, Object args = {}) : Promise<Response>

    encodeQuery(Object args = {}) : string
}
```

{% hint style="warning" %}
The **api()** method is intended for low-level operations, requiring that you encode the arguments yourself and provide any necessary headers.

**Always prefer the provided request methods.**
{% endhint %}

## Examples <a href="#examples" id="examples"></a>

### **Instantiate RestClient**

```javascript
// Create a RestClient with a base URL and an initial JWT token
let rest = new RestClient('/api/v2', 'Bearer jwt-token-placeholder');
```

### **Request Methods**

```javascript
// Make GET request
rest.get('/endpoint');

// Make POST request
rest.post('/endpoint');

// Make PUT request
rest.put('/endpoint');

// Make DELETE request
rest.delete('/endpoint');
```

### **Simple Example**

```javascript
// Make a GET request to /users/1
rest.get('/users/1').then(response => {
    let user = response.json();
}).catch(error => {
    console.log(error);
});
```

### **Exhaustive Example**

```javascript
// Make a GET request to /foo?foo=bar&bar=foo
rest.get('/foo', {
    foo: 'bar',
    bar: 'foo'
}).then(response => {
    // Successful response (200 <= status < 400)

    // Check result
    if(response.status === 200) {
        console.log('200 OK! It worked!');
    }
    else if(response.status === 204) {
        console.log('204 OK with no body!  It worked!');
    }
    else if(response.status === 304) {
        console.log('304 Redirected!  It worked!');
    }

    // Get response body
    let bodyText = response.text();
    let bodyObj = response.json();
    let bodyBuffer = response.arrayBuffer();
    let bodyBlob = response.blob();

    // Get the original request
    let originalRequest = response.request;

    // Check for a header in the response
    if(response.headers.has('content-length')) {
        // Get the value of a header in the response
        let contentLength = response.headers.get('content-length'); // case-insensitive header names
    }
}).catch(error => {
    // Failed response (status >= 400)

    // Check reason for failure
    switch(error.constructor) {
        // API rejected the request or threw an error (e.g. 500)
        case RestClient.Errors.RestError:
            console.log('Caught RestError: ', error);

            // Check the reason for rejection
            if(error.response.status === 404) {
                console.log('Page not found!');
            }
            else if(error.response.status === 500) {
                console.log('The server threw an error.');
            }

            // Get the body of the response
            let bodyText = error.response.text();
            let bodyObj = error.response.json();
            let bodyBuffer = error.response.arrayBuffer();
            let bodyBlob = error.response.blob();

            break;

        // HTTP request failed (e.g. could not connect to server, CORS error, etc.)
        case RestClient.Errors.NetworkError:
            console.log('Caught NetworkError: ', error);
            break;
    }
});
```

## Batching requests

Each of the request methods in `RestClient` return a `Promise` object.  You can collect these `Promise` objects into an array, and use the native `Promise.all()` method to wait for all of them to resolve.

```javascript
let api = new RestClient('/path/to/api', 'Bearer my-token');
let promises = [];
let isLoading = true;

// Handle each API response individually...
promises.push(this.api.get('/users/1').then(response => {
    let user = response.json();
}));

promises.push(this.api.get('/files').then(response => {
    let files = response.json();
}));

promises.push(this.api.get('/items').then(response => {
    let items = response.json();
}));

// ... and/or handle all API responses at once.
Promise.all(promises).then(responses => {
    // Note: Responses are in the order passed to Promise.all(), 
    // not the order in which they resolve.
    let user = responses[0].json();
    let files = responses[1].json();
    let items = responses[2].json();
    
    isLoading = false;
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.elentra.org/ejs/1.1/guide/http-library/rest-client.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
