UNPKG

reso.js

Version:

A robust, Typescript-first Node.js client designed for interacting with RESO Web API services, fully aligned with the current RESO Web API specification.

244 lines (184 loc) 6.85 kB
# reso.js **`reso.js`** is a robust, Typescript-first `Node.js` client designed for interacting with RESO Web API services. It is _fully compliant_ with the current [RESO Web API specification](https://github.com/RESOStandards/transport) and provides commonly used features out-of-the-box, including: - **Automatic Pagination** - **Authentication** - **Built-in Rate Limiting** It includes features like custom request/response hooks, automatic authentication token refresh, and built-in rate limiting. ## Table of Contents - [Installation](#installation) - [Compatibility](#compatibility) - [Usage](#usage) - [Getting Started](#getting-started) - [Bearer Token Authentication](#bearer-token-authentication) - [Client Credentials Authentication](#client-credentials-authentication) - [Querying the API](#querying-the-api) - [readByQuery](#readbyquery) - [readById](#readbyid) - [Fetching $metadata](#fetching-metadata) - [Rate Limiting](#rate-limiting) - [Hooks](#hooks) - [Error Handling](#error-handling) - [Contributing](#contributing) - [License](#license) ## Installation Install with your preferred package manager: ```bash pnpm add reso.js # or npm install reso.js # or yarn add reso.js ``` ## Compatibility This library works with any RESO-compliant server following the [RESO Web API spec](https://github.com/RESOStandards/transport). Additionally, it provides official support for: - [Spark Platform](https://sparkplatform.com) - [Trestle](https://trestle.corelogic.com) - [Bridge Interactive](https://www.bridgeinteractive.com) - [MLS Grid](https://www.mlsgrid.com) ## Usage ### Getting Started `reso.js` supports both **Bearer Token** and **Client Credentials** authentication strategies. Refer to your feed's documentation for your the supported strategy and how to obtain credentials. #### Bearer Token Authentication This strategy is used when you have a long-lived, non-expiring token. ```js import { createFeed } from 'reso.js'; const feed = createFeed({ http: { baseURL: 'http://my-reso-api.com', }, auth: { type: 'token', credentials: { token: 'MY_TOKEN', }, }, }); ``` > The bearer token provided must not expire. #### Client Credentials Authentication This strategy is required when the provider uses the OAuth 2.0 Client Credentials flow, it will autemtically refresh the token before it expires. ```js import { createFeed } from 'reso.js'; const feed = createFeed({ http: { baseURL: 'http://my-reso-api.com', }, auth: { type: 'credentials', credentials: { tokenURL: 'http://my-reso-api.com/token', clientId: 'MY_CLIENT_ID', clientSecret: 'MY_CLIENT_SECRET', // Optional: grantType and scope }, // Optional: Custom refresh buffer (default is 30,000ms / 30 seconds) // refreshBuffer: 40000, }, }); ``` ### Types Safety (Typescript) For an enhanced development experience, you can pass a generic type to `createFeed` to auto type the client to your resources shapes, enabling full type-safety for all operations. ```ts import { createFeed } from 'reso.js'; // 1. Define the TypeScript interface for your RESO resources. // This generic type maps resource names (e.g., 'Property') to their expected shape. interface Resources { Property: { ListingId: string; ListPrice: number; // ... all other expected properties }; Agent: { Name: string }; } // 2. Create the feed instance, applying the Resources generic. const feed = createFeed<Resources>({ http: { baseURL: 'http://my-reso-api.com', }, // ... config }); // 3. Read a specific resource by ID. // Because the feed is typed with <Resources>, the 'Property' resource // and the 'myProperty' response are fully type-safe, preventing runtime errors. const myProperty = await feed.readById('Property', 123); // This access is now guaranteed to be type-safe! console.log(myProperty.data.ListingId); ``` ### Querying the API #### `readByQuery` Perform a RESO-compliant query. The function is an **async generator** that handles auto pagination for you. Use a `for await...of` loop to iterate through all pages of results. ```js for await (let properties of feed.readByQuery('Property', '$filter=ListPrice gt 100000')) { for (property of properties.data) { console.log(property); } } ``` #### `readById` Retrieve a specific record by ID: ```js const myProperty = await feed.readyById('Property', 123); console.log(myProperty.data); ``` ### Fetching `$metadata` Get the feed’s metadata XML: ```js const metadata = await feed.$metadata(); console.log(metadata); // The raw XML string ``` ### Rate Limiting Rate limiting is built-in for supported providers. You can also customize limits: | Parameter | Type | Description | | :------------- | :------------ | :------------------------------------------------ | | **`duration`** | `number` (ms) | The time window for the limit. | | **`points`** | `number` | The maximum requests allowed within the duration. | ```js const feed = createFeed({ http: { baseURL: 'http://my-reso-api.com', }, limiter: { duration: 30000, // 30 seconds points: 5, // max 5 requests per duration }, }); ``` > `duration` defines the time window in milliseconds, and `points` sets the maximum number of requests allowed within that window. ### Hooks Customize the client's behavior by passing an object of supported hooks (based on [ofetch interceptors](https://github.com/unjs/ofetch?tab=readme-ov-file#%EF%B8%8F-interceptors)). The clients behaviour can be customized with the following supported hooks: | Hook | Purpose | | :---------------- | :---------------------------------------------------- | | `onRequest` | Modify the request before it's sent. | | `onRequestError` | Handle errors during the request setup. | | `onResponse` | Inspect/Modify the response data after it's received. | | `onResponseError` | Handle HTTP errors (e.g., 4xx, 5xx) before throwing. | **Example** – Appending `/replication` to `Property` requests: ```js hooks: { onRequest: [ ({ request }) => { if (request.toLowerCase().includes('property')) { request += '/replication'; } }, ], }, ``` ### Error Handling The client throws a custom `FeedError` with detailed RESO error information: ```js try { const myProperty = await feed.readById('Property', 123); console.log(myProperty.data); } catch (error) { if (error instanceof FeedError) { console.log(error.details); } } ``` ## Contributing Bug reports, pull requests and feature discussions are welcome at [GitHub](https://github.com/ComfortablyCoding/reso.js) ## License [MIT License](https://github.com/ComfortablyCoding/reso.js/blob/main/LICENSE)