npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@granito/ngx-hal-client

v1.0.0-dev.6

Published

A HAL client to be used in Angular projects

Downloads

1

Readme

Latest Version Build

This is a Hypertext Application Language (HAL) client to be used in Angular projects.

Installation

Using npm:

$ npm install @granito/ngx-hal-client --save

Building from source

$ git clone https://github.com/granito-source/ngx-hal-client.git
$ cd ngx-hal-client
$ npm install
$ npm run build

Running the tests

$ npm tests

Basic usage

For illustrative purposes, let's assume that your Angular application needs to work with the following HAL API.

The root entry point is /api/v1. When one executes GET on this URI, the returned object looks like

{
    "apiVersion": "1.7.21",
    "_links": {
        "self": {
            "href": "/api/v1"
        },
        "messages": {
            "href": "/api/v1/messages"
        }
    }
}

In other words, it exposes the implementation version and declares one link to work with a collection of messages. When one executes GET on /api/v1/messages the API returns a collection page like this

{
    "start": 0,
    "_links": {
        "self": {
            "href": "/api/v1/messages?start=0"
        },
        "next": {
            "href": "/api/v1/messages?start=2"
        }
    },
    "_embedded": {
        "selected" : {
            "id": 0,
            "text": "Then what do they call it?",
            "_links": {
                "self": {
                    "href": "/api/v1/messages/0"
                }
            }
        },
        "items": [
            {
                "id": 0,
                "text": "Then what do they call it?",
                "_links": {
                    "self": {
                        "href": "/api/v1/messages/0"
                    },
                    "next": {
                        "href": "/api/v1/messages/1"
                    },
                }
            },
            {
                "id": 1,
                "text": "They call it Royale with Cheese.",
                "_links": {
                    "self": {
                        "href": "/api/v1/messages/1"
                    },
                    "prev": {
                        "href": "/api/v1/messages/0"
                    }
                }
            }
        ]
    }
}

Setup Angular HTTP Client

HAL Client uses Angular HTTP Client. You have to configure HTTP Client using dependency injection before HAL Client can be used. The modern way of doing it using providers in app.config.ts is shown below.

import { provideHttpClient, withFetch } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
    providers: [
        provideHttpClient(withFetch())
    ]
};

For other ways please refer to Setting up HttpClient in the Angular documentation.

Define resources

Then you need to have some resources defined, e.g. the root resource for your API hierarchy. If your root resource does not have any properties, you can just use Resource. Otherwise, like in our sample API, extend Resource class as needed.

import { Resource } from '@granito/ngx-hal-client';

export class ApiRoot extends Resource {
    readonly apiVersion!: string;
}

and

import { Resource } from '@granito/ngx-hal-client';

export class Message extends Resource {
    readonly id!: number;

    readonly text!: string;

    withText(text: string): Message {
        return this.clone({ text });
    }
}

Collections are represented by Collection resource.

Read the root resource

HAL Client defines Accessor object to give access to HAL resources. In order to access the root entry point of the API, you need to get its accessor first using HalClientService and then read the API root resource. Normally you would keep the root entry point of the API as your application's state, e.g. in a ReplaySubject.

import { Injectable } from '@angular/core';
import { HalClientService } from '@granito/ngx-hal-client';
import { Observable, ReplaySubject } from 'rxjs';
import { ApiRoot } from './api-root';

@Injectable({ providedIn: 'root' })
export class ApiRootService {
    private readonly apiRoot$ = new ReplaySubject<ApiRoot>(1);

    get apiRoot(): Observable<ApiRoot> {
        return this.apiRoot$.asObservable();
    }

    constructor(client: HalClientService) {
        client.root('/api/v1').read(ApiRoot).subscribe(
            api => this.apiRoot$.next(api));
    }
}

Following links

Once you have at least one resource instance, e.g. the API root from the example above, you can follow links available in the resource to get accessors for the linked resources.

@Injectable({ providedIn: 'root' })
export class MessageService {
    private messages$: Observable<Accessor | undefined>;

    constructor(apiRootService: ApiRootService) {
        this.messages$ = apiRootService.apiRoot.pipe(
            follow('messages')
        );
    }
}

In the example above, messages$ observable will emit either the accessor to the collection of messages or undefined if the API root does not have messages link.

CRUD operations

Accessor and Resource objects allow you to execute CRUD operations using HAL API. Some operations are defined on both objects and work identically, some are defined only on Resource, and some are available in both but have a bit different syntax and semantic.

Read collection

Now that you have an accessor for the collection of messages, you can read the collection and access its elements.

    readFirst(): Observable<Message | undefined> {
        return this.messages$.pipe(
            take(1),
            readCollection(Message),
            map(collection => collection?.values[0])
        );
    }

The example above shows how to access the first message in the collection if it exists.

Read resource

Messages are resources, and they may have their own links. You can read resources referenced by these links as shown below.

    private current$: Observable<Message>;
    ...
    readNext(): Observable<Message> {
        return this.current$.pipe(
            take(1),
            follow('next'),
            read(Message)
        );
    }

The example shows how to access the next message in the thread if it exists.

Refresh

The library provides refresh() RxJS operator to execute a read operation on the resource using its self link. For example, here is how you can refresh the API root in ApiRootService.

    refresh(): void {
        this.apiRoot$.pipe(
            take(1),
            refresh()
        ).subscribe(api => this.apiRoot$.next(api));
    }

Create resource

To create a new message, you can use create() operator.

    post(message: { text: string; }): Observable<Accessor | undefined> {
        return this.messages$.pipe(
            take(1),
            create(message)
        );
    }

Two things are worth mentioning here. First, the object passed to create() operator does not have to be a Resource. Second, if the POST operation returns the URI for the newly created resource in the Location header, then the observable will emit an accessor for this resource. You can use it to read the resource immediately after it is created, e.g. by using read() operator.

Update resource

Resource instances can be updated in the API by using update() operator.

    private current$: Observable<Message>;
    ...
    edit(text: string): Observable<Message> {
        return this.current$.pipe(
            take(1),
            update(message => message.withText(text)),
            read(Message)
        );
    }

On successful completion the observable will emit an accessor for the resource, which can be used to obtain a fresh copy of it from the API.

Delete resource

And finally, resources and collections can be deleted using del() operator.

    private current$: Observable<Message>;
    ...
    deleteCurrent(): Observable<void> {
        return this.current$.pipe(
            take(1),
            del()
        );
    }

Access embedded resources

Resource offers two methods to access embedded HAL resources. In order to access embedded arrays, you can use getArray() method.

    array(messages: Collection<Message>): Message[] | undefined {
        return messages.getArray(Message, 'items');
    }

To get a single embedded object, you can use get() method.

    one(messages: Collection<Message>): Message | undefined {
        return messages.get(Message, 'selected');
    }

Note, Collection is a subclass of Resource, so both methods can be used just fine. Also, it is worth mentioning, that using get() on an embedded array will return the first element, and using getArray() on a single embedded resource will return an array containing the resource.