Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@api-platform/ld

Package Overview
Dependencies
Maintainers
0
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@api-platform/ld

Fetch Edge Side APIs

  • 1.0.0
  • latest
  • npm
  • Socket score

Version published
Maintainers
0
Created
Source

@api-platform/ld

Rich JSON formats such as JSON-LD use IRIs to reference embeded data. This library fetches the wanted Linked Data automatically.

I have an API referencing books and their authors, GET /books/1 returns:

{
      "@id": "/books/1",
      "@type": ["https://schema.org/Book"],
      "title": "Hyperion",
      "author": "https://localhost/authors/1"
}

Thanks to @api-platform/ld you can load authors automatically when you need them:

import ld from '@api-platform/ld'

const pattern = new URLPattern("/authors/:id", "https://localhost");
const books = await ld('/books', {
    urlPattern: pattern,
    onUpdate: (newBooks) => {
        log()
    }
})

function log() {
    console.log(books.author?.name)
}

log()

Installation

npm install @api-platform/ld

Usage

Use ld like fetch and specify the URLPattern to match IRIs that are going to be fetched automatically:

import ld from '@api-platform/ld'

await ld('/books', {urlPattern: new URLPattern("/authors/:id", "https://localhost")})

Available options:

  • fetchFn fetch function, defaults to fetch().then((res) => res.json())
  • urlPattern the url pattern filter
  • relativeURIs supports relative URIs (defaults to true)
  • onUpdate: (root, options: { iri: string, data: any }) callback on data update
  • onError error callback on fetch errors

URLPattern is available as a polyfil at https://www.npmjs.com/package/urlpattern-polyfill

Examples

Tanstack Query

import ld from "@api-platform/ld";
import {useEffect} from "react"
import {createRoot} from "react-dom/client"
import {
  QueryClient,
  useQuery,
  QueryClientProvider,
} from '@tanstack/react-query'

const queryClient = new QueryClient();
const pattern = new URLPattern("/(books|authors)/:id", window.origin);

function Books() {
  const {isPending, error, data: books} = useQuery({
    queryKey: ['/books'],
    notifyOnChangeProps: 'all',
    queryFn: ({queryKey}) => ld(queryKey, {
      urlPattern: pattern,
      onUpdate: (root, {iri, data}) => {
        queryClient.setQueryData(queryKey, root)
      }
    })
  })

  if (isPending) return 'Loading...'
  if (error) return 'An error has occurred: ' + error.message
  return (
    <ul>
      {books.member.map(b => (<li data-testid="book">{b?.title} - {b?.author?.name}</li>))}
    </ul>
  );
}

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Books />
    </QueryClientProvider>
  )
}
createRoot(root).render(<App />)

React

import { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
import ld from "@api-platform/ld";

function App() {
const pattern = new URLPattern("/(books|authors)/:id", window.origin);
const [books, setBooks] = useState({});

useEffect(() => {
  let ignore = false;
  setBooks({});
  ld('/books', {onUpdate: (books) => setBooks(books), urlPattern: pattern})
  .then(books => {
    if (!ignore) {
      setBooks(books);
    }
  });
  return () => {
    ignore = true;
  };
}, []);

return (
  <ul>
    {books.member?.map(b => (<li data-testid="book">{b.title} - {b.author?.name}</li>))}
  </ul>
);
}

const root = createRoot(document.getElementById("root"));
root.render(<App />);

Axios

import ld from "@api-platform/ld";
const pattern = new URLPattern("/(books|authors)/:id", window.origin);
const list = document.getElementById('list')

function onUpdate(books) {
  const l = []
  books.member.forEach((book) => {
    const li = document.createElement('li')
    li.dataset.testid = 'book'
    li.innerText = `${book.title} - ${book.author?.name}`
    l.push(li)
  });
  list.replaceChildren(...l)
}

ld('/books', {urlPattern: pattern, onUpdate, fetchfn: (url, options) => axios.get(url)})
  .then((books) => {
    books.member.forEach((book) => {
      const li = document.createElement('li')
      li.dataset.testid = 'book'
      li.innerText = `${book.title} - ${book.author?.name}`
      list.appendChild(li)
    });
  })

VanillaJS

import ld from "@api-platform/ld";
const pattern = new URLPattern("/(books|authors)/:id", window.origin);
const list = document.getElementById('list')

function onUpdate(books) {
  const l = []
  books.member.forEach((book) => {
    const li = document.createElement('li')
    li.dataset.testid = 'book'
    li.innerText = `${book.title} - ${book.author?.name}`
    l.push(li)
  });
  list.replaceChildren(...l)
}

ld('/books', {urlPattern: pattern, onUpdate})
  .then((books) => {
    books.member.forEach((book) => {
      const li = document.createElement('li')
      li.dataset.testid = 'book'
      li.innerText = `${book.title} - ${book.author?.name}`
      list.appendChild(li)
    });
  })

SWR

Example of a SWR hook:

import ld, { LdOptions } from '@api-platform/ld'
import useSWR from 'swr'
import {useState} from 'react'
import type { SWRConfiguration, KeyedMutator } from 'swr'

export type fetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
export type Fetcher = (...args: any[]) => Promise<any>
export type onUpdateCallback<T> = (root: T, options: { iri: string, data: object }) => void;

export default function useSWRLd<T extends object>(url: string, fetcher: Fetcher, config: Partial<LdOptions<T>> & SWRConfiguration = {}) {
  let cb: undefined | KeyedMutator<T> = undefined

  // You may need to force re-rendering as the comparison algorithm of SWR does not work well when object keys are added
  // another solution is to improve the compare function
  const [, setRender] = useState(false)
  const res = useSWR(
    url,
    (url: RequestInfo | URL, opts: RequestInit) =>
      ld(url, {
        ...opts,
        fetchFn: fetcher,
        urlPattern: config.urlPattern,
        onUpdate: (root) => {
          if (cb) {
            cb(root, { optimisticData: root, revalidate: false })
            setRender((s: boolean) => !s)
          }
        },
        relativeURIs: config.relativeURIs,
        onError: config.onError,
      }),
    config
  );

  cb = res.mutate
  return res
}

FAQs

Package last updated on 20 Aug 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc