o.js

repository·master·Indexed 42 days ago

https://github.com/janhommes/o.js

A lightweight, isomorphic OData JavaScript library designed to simplify OData-compliant data requests. It provides a fluent API for CRUD operations, supports complex querying via odata-query integration, and includes functionality for batching requests. Version 2.0.1 requires Node 18 or higher for native fetch and URL support.

Tokens
5.7K
Snippets
23
Records
28
Agent score
52%

What's inside o.js

  1. Batching requests with .batch()

    master

    By default, o.js executes requests sequentially. To group multiple requests into a single OData batch request, use the .batch() method. These requests are sent to the $batch endpoint defined in your configuration.

    Note: Changsets are currently experimental and must be enabled in the configuration via useChangset: true.

  2. Use .fetch() for raw responses

    master
    By default, o.js attempts to parse the response data. If you need to access the raw response (e.g., to check status codes or access OData metadata), use the .fetch() method instead of .query(). This behaves similarly to the standard fetch API.
  3. Use o.js in Node.js

    master

    In Node.js environments, require the o function from the odata package. Note that version 2.0.0+ requires Node 18 or higher to support native fetch and URL.

    const o = require('odata').o;
    
    o('http://my.url')
      .get('resource')
      .then((data) => console.log(data));
  4. Use o.js in a browser (ESM/TypeScript)

    master

    For modern web applications using modules or TypeScript, import the o function from odata.

    import { o } from 'odata';
    
    (async () => {
      // Chaining directly from the constructor
      const data1 = await o('http://my.url')
        .get('resource')
        .query({ $top: 3 });
    
      // Using a reusable handler
      const oHandler = o('http://my.url');
      const data2 = await oHandler
        .get('resource')
        .query({ $top: 3 });
    })();
    import { o } from 'odata';
    
    (async () => {
      const data1 = await o('http://my.url')
        .get('resource')
        .query({ $top: 3 });
    })();
  5. Perform CRUD operations with o.js

    master

    o.js provides a fluent API for standard OData CRUD operations. The following examples use async/await syntax.

    Create (POST)

    const data = { FirstName: 'Bar', LastName: 'Foo', UserName: 'foobar' };
    const response = await o('http://my.url').post('User', data).query();

    Read (GET)

    const response = await o('http://my.url').get('User').query({$filter: `UserName eq 'foobar'`});
    // Returns the exact user if one match is found, otherwise an array of users.

    Update (PATCH)

    const data = { FirstName: 'John' };
    const response = await o('http://my.url').patch(`User('foobar')`, data).query();

    Delete

    const response = await o('http://my.url').delete(`User('foobar')`).query();
    // Create
    await o('http://my.url').post('User', { FirstName: 'Bar' }).query();
    
    // Read
    await o('http://my.url').get('User').query({$filter: `UserName eq 'foobar'`});
    
    // Update
    await o('http://my.url').patch(`User('foobar')`, { FirstName: 'John' }).query();
    
    // Delete
    await o('http://my.url').delete(`User('foobar')`).query();
  6. Use o.js in a browser (Script Tag)

    master

    For non-module environments, include the UMD build via a script tag. The library will be available on the window.odata object.

    <script src="node_modules/odata/dist/umd/o.js"></script>
    <script>
      window.odata
        .o('http://my.url')
        .get('resource')
        .query({ $top: 3 })
        .then(function (data) {});
    </script>
    <script src="node_modules/odata/dist/umd/o.js"></script>
  7. Configure o.js default options

    master

    The configuration is based on RequestInit and custom OData settings. Default values include:

    {
        "batch": {
          "boundaryPrefix": "batch_",
          "changsetBoundaryPrefix": "changset_",
          "endpoint": "$batch",
          "headers": {"Content-Type": "multipart/mixed"},
          "useChangset": false,
          "useRelativeURLs": false
        },
        "credentials": "omit",
        "fragment": "value",
        "headers": {"Content-Type": "application/json"},
        "mode": "cors",
        "redirect": "follow",
        "referrer": "client",
        "onStart": "() => null",
        "onFinish": "() => null",
        "onError": "() => null"
    }
  8. Polyfills for IE11 support

    master

    To support older browsers like IE11, include the dist/umd/o.polyfill.js file in your project.

    Note on Node.js versions:

    • Versions < 2.0.0: Polyfills are included automatically for Node.
    • Versions >= 2.0.0: Only supports Node 18 and higher (which includes native fetch and URL).
  9. Configure the o.js constructor and rootUrl

    master

    The o constructor accepts a rootUrl and an optional config object.

    function o(rootUrl: string | URL, config?: OdataConfig | any)

    Root URL Strategies

    1. Direct Query: Pass a full URL to query a specific resource immediately. o('http://my.url/some-resource').query()

    2. Reusable Handler: Create a handler with a rootUrl in the config to avoid repeating the base URL. const oHandler = o('', { rootUrl: 'http://my.url' }); oHandler.get('some-resource').query(); // requests http://my.url/some-resource

    3. Merged Path: If a rootUrl is provided as the first argument AND in the config, they are merged. const oHandler = o('v1', { rootUrl: 'http://my.url' }); oHandler.get('some-resource').query(); // requests http://my.url/v1/some-resource

    4. Browser Relative: In a browser, if only a path is provided, it attempts to use the current browser URL. const oHandler = o('v1'); // requests http://[current-url]/v1/some-resource

    const oHandler = o('', { rootUrl: 'http://my.url' });
    oHandler.get('some-resource').query().then();
  10. Query with OData parameters and odata-query

    master

    You can pass OData query parameters directly as an object to .query() or .fetch(). These are attached as URLSearchParams.

    Supported Parameters

    • $filter: string
    • $orderby: string
    • $expand: string
    • $select: string
    • $skip: number
    • $top: number
    • $count: boolean (adds inline count metadata)
    • $search: string
    • $format: string
    • $compute: string
    • $index: number
    • [key: string]: any: allows any additional OData parameters.

    Using odata-query

    Since version 2.0.0, you can use the odata-query library to build complex filters.

    import buildQuery from 'odata-query'
    
    const filter = { not: { and: [{ SomeProp: 1 }, { AnotherProp: 2 }] } };
    
    // Option 1: Pass buildQuery result to .query()
    oHandler.get('People').query(buildQuery({ filter }));
    
    // Option 2: Append buildQuery result to the resource path
    oHandler.get('People' + buildQuery({ filter, key: 1, top: 10 })).query();
    import buildQuery from 'odata-query'
    
    const filter = { not: { and:[ { SomeProp: 1 }, { AnotherProp: 2 } ] } };
    
    // Using only filter in query()
    oHandler.get('People')
      .query(buildQuery({ filter }))
      .then((filteredPeople) => {});
  11. Configure OData batch requests with OdataBatchConfig

    master

    The OdataBatchConfig interface allows you to configure experimental batch request behavior. This is passed via the batch property in the main OdataConfig object.

    Available options:

    • endpoint: The specific endpoint for batch operations.
    • headers: Custom Headers to include in the batch request.
    • boundaryPrefix: A prefix for the multipart boundary.
    • useChangset: Boolean indicating whether to use a changeset within the batch.
    • changsetBoundaryPrefix: A prefix for the changeset boundary.
    • useRelativeURLs: When truthy, relative URLs will be used within the batch elements.
    import { OdataConfig } from 'o.js';
    
    const config: OdataConfig = {
      rootUrl: 'https://api.example.com/odata',
      fragment: 'value',
      onStart: () => null,
      onFinish: () => null,
      onError: () => null,
      batch: {
        useChangset: true,
        useRelativeURLs: true,
        boundaryPrefix: 'o_js_batch',
        changsetBoundaryPrefix: 'o_js_changeset'
      }
    };