|
| 1 | +# @tanstack/lit-query |
| 2 | + |
| 3 | +Lit adapter for `@tanstack/query-core` using Lit reactive controllers. |
| 4 | + |
| 5 | +## Install |
| 6 | + |
| 7 | +```bash |
| 8 | +npm install @tanstack/lit-query @tanstack/query-core lit @lit/context |
| 9 | +``` |
| 10 | + |
| 11 | +For local development in this repository: |
| 12 | + |
| 13 | +```bash |
| 14 | +npm install |
| 15 | +npm run build |
| 16 | +``` |
| 17 | + |
| 18 | +## Quick Start |
| 19 | + |
| 20 | +```ts |
| 21 | +import { LitElement, html } from 'lit' |
| 22 | +import { QueryClient } from '@tanstack/query-core' |
| 23 | +import { QueryClientProvider, createQueryController } from '@tanstack/lit-query' |
| 24 | + |
| 25 | +const client = new QueryClient({ |
| 26 | + defaultOptions: { queries: { retry: false } }, |
| 27 | +}) |
| 28 | + |
| 29 | +class AppProvider extends QueryClientProvider { |
| 30 | + constructor() { |
| 31 | + super() |
| 32 | + this.client = client |
| 33 | + } |
| 34 | + |
| 35 | + protected override createRenderRoot(): HTMLElement | DocumentFragment { |
| 36 | + return this |
| 37 | + } |
| 38 | +} |
| 39 | +customElements.define('app-provider', AppProvider) |
| 40 | + |
| 41 | +class UsersView extends LitElement { |
| 42 | + private readonly users = createQueryController(this, { |
| 43 | + queryKey: ['users'], |
| 44 | + queryFn: async () => { |
| 45 | + const response = await fetch('/api/users') |
| 46 | + return response.json() as Promise<Array<{ id: string; name: string }>> |
| 47 | + }, |
| 48 | + }) |
| 49 | + |
| 50 | + protected override createRenderRoot(): HTMLElement | DocumentFragment { |
| 51 | + return this |
| 52 | + } |
| 53 | + |
| 54 | + render() { |
| 55 | + const query = this.users() |
| 56 | + if (query.isPending) return html`Loading...` |
| 57 | + if (query.isError) return html`Error` |
| 58 | + return html`<ul> |
| 59 | + ${query.data?.map((u) => html`<li>${u.name}</li>`)} |
| 60 | + </ul>` |
| 61 | + } |
| 62 | +} |
| 63 | +customElements.define('users-view', UsersView) |
| 64 | +``` |
| 65 | + |
| 66 | +## API Surface (v1) |
| 67 | + |
| 68 | +- `QueryClientProvider`, `useQueryClient`, `resolveQueryClient` |
| 69 | +- `createQueryController` |
| 70 | +- `createMutationController` |
| 71 | +- `createInfiniteQueryController` |
| 72 | +- `createQueriesController` |
| 73 | +- `useIsFetching`, `useIsMutating`, `useMutationState` |
| 74 | +- `queryOptions`, `infiniteQueryOptions`, `mutationOptions` |
| 75 | + |
| 76 | +## Runnable Examples |
| 77 | + |
| 78 | +This repo includes a Vite Lit example app at `examples/lit-query-e2e-app`. |
| 79 | +The demo uses a local in-memory mock API (`src/todoApi.ts`) for deterministic behavior. |
| 80 | + |
| 81 | +Run: |
| 82 | + |
| 83 | +```bash |
| 84 | +npm run example:install |
| 85 | +npm run example:dev |
| 86 | +``` |
| 87 | + |
| 88 | +Open: |
| 89 | + |
| 90 | +- `http://127.0.0.1:4173/` (full integration demo) |
| 91 | +- `http://127.0.0.1:4173/basic-query.html` (query-only runnable example) |
| 92 | +- `http://127.0.0.1:4173/mutation.html` (mutation runnable example) |
| 93 | + |
| 94 | +Smoke test: |
| 95 | + |
| 96 | +```bash |
| 97 | +npm run example:e2e |
| 98 | +``` |
| 99 | + |
| 100 | +Focused scenarios: |
| 101 | + |
| 102 | +```bash |
| 103 | +npm run example:e2e:query-error |
| 104 | +npm run example:e2e:mutation-error |
| 105 | +npm run example:e2e:refetch-button |
| 106 | +npm run example:e2e:lifecycle-reconnect |
| 107 | +npm run example:e2e:lifecycle-contract |
| 108 | +``` |
| 109 | + |
| 110 | +Run all scenarios: |
| 111 | + |
| 112 | +```bash |
| 113 | +npm run example:e2e:all |
| 114 | +``` |
| 115 | + |
| 116 | +This repo also includes a pagination example app at `examples/lit-query-pagination-app`. |
| 117 | +It demonstrates paginated queries, optimistic updates, prefetching, and error recovery against a local API. |
| 118 | + |
| 119 | +Run: |
| 120 | + |
| 121 | +```bash |
| 122 | +npm run example:pagination:install |
| 123 | +npm run example:pagination:dev |
| 124 | +``` |
| 125 | + |
| 126 | +Smoke test: |
| 127 | + |
| 128 | +```bash |
| 129 | +npm run example:pagination:e2e |
| 130 | +``` |
| 131 | + |
| 132 | +Focused scenarios: |
| 133 | + |
| 134 | +```bash |
| 135 | +npm run example:pagination:e2e:prefetch |
| 136 | +npm run example:pagination:e2e:error |
| 137 | +npm run example:pagination:e2e:mutations |
| 138 | +npm run example:pagination:e2e:boundary |
| 139 | +``` |
| 140 | + |
| 141 | +Run all scenarios: |
| 142 | + |
| 143 | +```bash |
| 144 | +npm run example:pagination:e2e:all |
| 145 | +``` |
| 146 | + |
| 147 | +Use a different port (optional): |
| 148 | + |
| 149 | +```bash |
| 150 | +DEMO_PORT=4180 npm run example:dev |
| 151 | +DEMO_PORT=4180 npm run example:e2e:all |
| 152 | +``` |
| 153 | + |
| 154 | +E2E harness options (optional): |
| 155 | + |
| 156 | +```bash |
| 157 | +PW_HTTP_PROBE_TIMEOUT_MS=1000 |
| 158 | +PW_SERVER_READY_TIMEOUT_MS=30000 |
| 159 | +PW_WAIT_FOR_TEXT_TIMEOUT_MS=10000 |
| 160 | +PW_CAPTURE_FAILURE_ARTIFACTS=false |
| 161 | +PW_ARTIFACT_DIR=output/playwright |
| 162 | +``` |
| 163 | + |
| 164 | +This repo also includes an SSR example app at `examples/lit-query-ssr-app`. |
| 165 | +It demonstrates explicit `QueryClient` prefetch, SSR render, dehydrate, and hydrate flow for Lit. |
| 166 | + |
| 167 | +Run: |
| 168 | + |
| 169 | +```bash |
| 170 | +npm run example:ssr:install |
| 171 | +npm run example:ssr:dev |
| 172 | +``` |
| 173 | + |
| 174 | +Open: |
| 175 | + |
| 176 | +- `http://127.0.0.1:4174/` (SSR example app) |
| 177 | + |
| 178 | +Smoke test: |
| 179 | + |
| 180 | +```bash |
| 181 | +npm run example:ssr:e2e |
| 182 | +``` |
| 183 | + |
| 184 | +That root smoke test runs both the default same-origin SSR flow and the documented `SSR_PUBLIC_ORIGIN=http://localhost:4174` variant. |
| 185 | + |
| 186 | +Focused scenarios: |
| 187 | + |
| 188 | +```bash |
| 189 | +npm run example:ssr:e2e:error |
| 190 | +npm run example:ssr:e2e:refreshing |
| 191 | +``` |
| 192 | + |
| 193 | +Run the full default-origin SSR suite plus the documented public-origin smoke: |
| 194 | + |
| 195 | +```bash |
| 196 | +npm run example:ssr:e2e:all |
| 197 | +``` |
| 198 | + |
| 199 | +Run either path directly (optional): |
| 200 | + |
| 201 | +```bash |
| 202 | +npm run example:ssr:e2e:default |
| 203 | +npm run example:ssr:e2e:public-origin |
| 204 | +npm run example:ssr:e2e:all:default |
| 205 | +``` |
| 206 | + |
| 207 | +Use a different host or port (optional): |
| 208 | + |
| 209 | +```bash |
| 210 | +SSR_PORT=4180 npm run example:ssr:dev |
| 211 | +SSR_PORT=4180 npm run example:ssr:e2e |
| 212 | +SSR_HOST=0.0.0.0 npm run example:ssr:dev |
| 213 | +npm run example:ssr:dev:public-origin |
| 214 | +``` |
| 215 | + |
| 216 | +## Quality Gates |
| 217 | + |
| 218 | +- Core matrix: `docs/TEST_MATRIX.md` |
| 219 | +- Integration matrix: `docs/TEST_MATRIX_INTEGRATION.md` |
| 220 | +- Perf matrix: `docs/TEST_MATRIX_PERF.md` |
| 221 | +- RFC and phase log: `docs/RFC-v4.1.md` |
| 222 | + |
| 223 | +Current local quality gate: |
| 224 | + |
| 225 | +```bash |
| 226 | +npm run typecheck && npm test && npm run build |
| 227 | +``` |
| 228 | + |
| 229 | +Full local demo gate: |
| 230 | + |
| 231 | +```bash |
| 232 | +npm run demo:gate |
| 233 | +``` |
| 234 | + |
| 235 | +## License |
| 236 | + |
| 237 | +MIT |
0 commit comments