|
| 1 | +# Integrations / Frontend Consumption |
| 2 | + |
| 3 | +## jsonapi-react-tools: Consuming APIs in React/TypeScript |
| 4 | + |
| 5 | +While JsonApiToolkit focuses on building compliant backend APIs, consuming these APIs effectively on the frontend is crucial. The `@intility/jsonapi-react-tools` package provides TypeScript types and utilities specifically designed to make working with JsonApiToolkit responses seamless in React applications. |
| 6 | + |
| 7 | +It ensures type safety by mirroring the JSON:API structure produced by JsonApiToolkit, allowing you to focus only on defining your resource attributes on the frontend. |
| 8 | + |
| 9 | +### Features |
| 10 | + |
| 11 | +* **Strong JSON:API Type Definitions:** Provides generic types like `JsonApiDocument<T>`, `JsonApiCollectionDocument<T>`, `ResourceObject<T>`, etc., where `T` is your specific attribute type. |
| 12 | +* **Seamless Integration:** Types are tailored for JsonApiToolkit's output, including support for `data`, `links`, `meta` (with `pagination`), `included`, and `errors`. |
| 13 | +* **Advanced Query Builder:** A type-safe utility to generate complex JSON:API query strings for filtering (including operators and logical groups), sorting, pagination, and inclusion, matching JsonApiToolkit's parsing capabilities. |
| 14 | +* **Framework Agnostic:** Primarily types and helpers, with no hard dependency on specific data-fetching libraries (though examples often use `react-query`). |
| 15 | + |
| 16 | +### Installation |
| 17 | + |
| 18 | +Install the package into your **frontend** React project: |
| 19 | + |
| 20 | +```bash |
| 21 | +npm install @intility/jsonapi-react-tools |
| 22 | +``` |
| 23 | + |
| 24 | +> [!NOTE] |
| 25 | +> This package needs to be fetched via the `npm.intility.com` proxy. |
| 26 | +
|
| 27 | +### Getting Started: Typing Responses |
| 28 | + |
| 29 | +1. **Define Your Attribute Types:** In your frontend project, define a type for the `attributes` of your resource. |
| 30 | + |
| 31 | + ```ts |
| 32 | + export type CompanyAttributes = { |
| 33 | + companyName: string; |
| 34 | + companyCode: string; |
| 35 | + companyTenantId: string; |
| 36 | + protected: boolean; |
| 37 | + lastSyncedAt: string; |
| 38 | + }; |
| 39 | + ``` |
| 40 | + |
| 41 | +2. **Use Types with Data Fetching:** Use the types from `@intility/jsonapi-react-tools` when fetching data. The package provides types for the full JSON:API envelope. In this example we'll use `react-query`, but you can adapt it to any data-fetching library. |
| 42 | + |
| 43 | + ```tsx |
| 44 | + import { useQuery } from "@tanstack/react-query"; |
| 45 | + // Import the document type and your attribute type |
| 46 | + import { JsonApiCollectionDocument } from "@intility/jsonapi-react-tools"; |
| 47 | + import { CompanyAttributes } from "~/types/Company.ts"; // Your local type |
| 48 | +
|
| 49 | + // Assuming you have a default query function configured for 'api' |
| 50 | + // that fetches data and returns the parsed JSON response. |
| 51 | +
|
| 52 | + export const CompanyList = () => { |
| 53 | + // Provide the specific document type wrapping your attributes type |
| 54 | + const { data: companyResponse, error, isLoading } = useQuery< |
| 55 | + JsonApiCollectionDocument<CompanyAttributes> |
| 56 | + >({ queryKey: ["api", "companies"] }); |
| 57 | +
|
| 58 | + if (isLoading) |
| 59 | + return <div>Loading companies...</div>; |
| 60 | + |
| 61 | + if (error || !companyResponse) |
| 62 | + return <div>Error loading companies</div>; |
| 63 | +
|
| 64 | + return ( |
| 65 | + <ul> |
| 66 | + {companyResponse.data.map((company) => ( |
| 67 | + <li key={company.id}> |
| 68 | + {company.attributes.companyName} ({company.attributes.companyCode}) |
| 69 | + </li> |
| 70 | + ))} |
| 71 | + </ul> |
| 72 | + ); |
| 73 | + }; |
| 74 | + ``` |
| 75 | + |
| 76 | + This approach keeps the original JSON:API structure intact while providing full type safety based on JsonApiToolkit's output. |
| 77 | + |
| 78 | +### Using the Query Builder |
| 79 | + |
| 80 | +JsonApiToolkit supports rich querying via URL parameters. `jsonapi-react-tools` includes a type-safe query builder to generate these strings easily. |
| 81 | + |
| 82 | +1. **Import the Builder:** |
| 83 | + |
| 84 | + ```ts |
| 85 | + import { buildJsonApiQueryString, JsonApiQueryOptions } from "@intility/jsonapi-react-tools"; |
| 86 | + import { CompanyAttributes } from "../types/Company.ts"; // Your attribute type |
| 87 | + ``` |
| 88 | + |
| 89 | +2. **Define Query Options:** Create an options object. Field names for `filter` and `sort` are type-checked against your `CompanyAttributes`. Filter operators (`eq`, `ne`, `like`, etc.) have autocompletion. |
| 90 | + |
| 91 | + ```ts |
| 92 | + const queryOptions: JsonApiQueryOptions<CompanyAttributes> = { |
| 93 | + filter: { |
| 94 | + // Field 'companyName' must exist in CompanyAttributes |
| 95 | + companyName: { like: "Intility" }, |
| 96 | + or: [ |
| 97 | + // Field 'protected' must exist |
| 98 | + { protected: { eq: true } }, |
| 99 | + // Field 'companyCode' must exist |
| 100 | + { companyCode: { in: ["AA", "ZZ"] } } |
| 101 | + ] |
| 102 | + }, |
| 103 | + sort: [ |
| 104 | + // Field 'companyName' must exist, '-' prefix is allowed |
| 105 | + "-companyName", |
| 106 | + // Field 'lastSyncedAt' must exist |
| 107 | + "lastSyncedAt" |
| 108 | + ], |
| 109 | + page: { |
| 110 | + number: 1, |
| 111 | + size: 20 |
| 112 | + }, |
| 113 | + include: ["locations", "employees"] // Relationship names (string array) |
| 114 | + }; |
| 115 | + ``` |
| 116 | + |
| 117 | +3. **Generate the Query String:** |
| 118 | + |
| 119 | + ```typescript |
| 120 | + const queryString = buildJsonApiQueryString<CompanyAttributes>(queryOptions); |
| 121 | + ``` |
| 122 | + This will produce a query string like: |
| 123 | + |
| 124 | + `?filter[companyName][like]=Intility&filter[or][0][protected][eq]=true&filter[or][1][companyCode][in]=AA,ZZ&sort=-companyName,lastSyncedAt&page[number]=1&page[size]=20&include=locations,employees` |
| 125 | + |
| 126 | + >*This query would fetch companies with names like "Intility", either protected or with specific company codes, sorted by name and last synced date, paginated to the first 20 results, and including related locations and employees.* |
| 127 | + |
| 128 | +4. **Use with Data Fetching:** Append the `queryString` to your API endpoint URL. |
| 129 | + |
| 130 | + ```tsx |
| 131 | + const { data: companyResponse, error, isLoading } = useQuery< |
| 132 | + JsonApiCollectionDocument<CompanyAttributes> |
| 133 | + >({ queryKey: ["api", "companies", queryString] }); |
| 134 | + ``` |
| 135 | + |
| 136 | + The query builder supports all standard JSON:API operators (`eq`, `ne`, `gt`, `ge`, `lt`, `le`, `like`, `in`, `nin`, `isnull`, `isnotnull`) and logical groupings (`and`, `or`, `not`). |
| 137 | + |
| 138 | +### Further Information |
| 139 | + |
| 140 | +For more details on the package itself, visit the repository: |
| 141 | + |
| 142 | +* **GitHub:** [jsonapi-react-tools](https://github.com/intility/jsonapi-react-tools) |
| 143 | + |
0 commit comments