@vantreeseba/drizzle-graphql 15.1.4 → 15.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -14
- package/dist/index.cjs +405 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +405 -125
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -301,7 +301,7 @@ narrow the scope, never widen it. It reaches every path that touches the table:
|
|
|
301
301
|
| `orderBy` through a to-one — `comments(orderBy: { post: { title: … } })` | an out-of-scope post sorts as `NULL` |
|
|
302
302
|
| `updatePosts`, `updatePostsMany`, `deletePosts` | an out-of-scope row is simply not matched |
|
|
303
303
|
| `upsertPosts` (conflict branch) | a conflicting row outside the scope is left alone, not taken over |
|
|
304
|
-
| nested `connect` / `disconnect` / `set` | only in-scope rows can be attached or detached |
|
|
304
|
+
| nested `connect` / `disconnect` / `set` | only in-scope rows can be attached or detached; a many-to-many `set` keeps the links to out-of-scope rows |
|
|
305
305
|
|
|
306
306
|
Two things it deliberately does not do:
|
|
307
307
|
|
|
@@ -347,10 +347,14 @@ const { schema } = buildSchema(db, {
|
|
|
347
347
|
- The column is **removed from `CreatePostsInput` and `UpdatePostsInput`**, so a client cannot
|
|
348
348
|
send it at all — the SDL itself says the column is server-owned. It stays readable on the
|
|
349
349
|
object type.
|
|
350
|
-
- Every insert stamps it, including rows created by a nested `create
|
|
350
|
+
- Every insert stamps it, including rows created by a nested `create` and junction rows a
|
|
351
|
+
many-to-many `connect` / `set` inserts.
|
|
351
352
|
- Updates never write it, so an update cannot hand a row to another owner. Neither does the
|
|
352
353
|
conflict branch of an upsert: it is left out of the default overwrite and of the columns
|
|
353
354
|
`onConflict.update` can name.
|
|
355
|
+
- A relation whose foreign key is such a column gets no
|
|
356
|
+
[nested-write](#nested-writes) field from either side: every nested operation on it would
|
|
357
|
+
write that column. The build notes which relations it left out.
|
|
354
358
|
|
|
355
359
|
Together the two close the loop: `contextValues` decides which tenant a new row belongs to, and
|
|
356
360
|
`scope` decides which rows a request can see and change afterwards.
|
|
@@ -493,8 +497,8 @@ What a declared table gets:
|
|
|
493
497
|
| relation filters — `users(where: { posts: { some: {} } })`, `none`, `every`, `.through()` junctions | a marked post neither matches nor blocks `none`, with the same default as the relation field (see below) |
|
|
494
498
|
| `orderBy` through a to-one | a marked target the relation field hides sorts as `NULL` |
|
|
495
499
|
| `updatePosts`, `updatePostsMany`, a second `deletePosts` | a marked row is simply not matched |
|
|
496
|
-
| nested `connect` / `disconnect` / `set` | a marked row cannot be attached or detached |
|
|
497
|
-
| `CreatePostsInput`, `UpdatePostsInput` | the marker column is **removed** — only the mutations write it |
|
|
500
|
+
| nested `connect` / `disconnect` / `set` | a marked row cannot be attached or detached; a many-to-many `set` keeps the links to marked rows |
|
|
501
|
+
| `CreatePostsInput`, `UpdatePostsInput`, nested `create` payloads | the marker column is **removed** — only the mutations write it |
|
|
498
502
|
|
|
499
503
|
Every read path also gains a `deleted` argument for the cases the default hides:
|
|
500
504
|
|
|
@@ -1371,7 +1375,10 @@ can count or summarise related rows without fetching them:
|
|
|
1371
1375
|
All parents in a selection are aggregated with a single
|
|
1372
1376
|
`SELECT <fk>, … WHERE <fk> IN (…) GROUP BY <fk>` per request, so `postsAggregate` on a list of
|
|
1373
1377
|
users is one extra query, not one per user. Differently-aliased selections with different
|
|
1374
|
-
`where` arguments are batched separately.
|
|
1378
|
+
`where` arguments are batched separately. A batch whose parent keys would bind more than
|
|
1379
|
+
10,000 parameters (10,000 parents on a single-column key, 5,000 on a two-column one) is split
|
|
1380
|
+
into several such queries, which keeps it within SQLite's 32,766- and Postgres/MySQL's
|
|
1381
|
+
65,535-parameter limits; the batched relation fields split the same way.
|
|
1375
1382
|
|
|
1376
1383
|
## Distinct
|
|
1377
1384
|
|
|
@@ -1600,8 +1607,9 @@ mutation {
|
|
|
1600
1607
|
out the rest of the row. Listing an unsupplied column in `update` is an error rather than
|
|
1601
1608
|
a silent no-op
|
|
1602
1609
|
- That holds per row: a batch whose rows supply different columns is split into one
|
|
1603
|
-
statement per run of consecutive rows that write the same columns, run in one transaction
|
|
1604
|
-
|
|
1610
|
+
statement per run of consecutive rows that write the same columns, run in one transaction
|
|
1611
|
+
(on a synchronous SQLite driver such as better-sqlite3 too), so a row that left a column out
|
|
1612
|
+
keeps it. The rows come back in the order they were sent
|
|
1605
1613
|
- `target` must match one of the table's unique constraints exactly; anything else is
|
|
1606
1614
|
rejected with the list of valid targets, instead of the database's opaque "no unique or
|
|
1607
1615
|
exclusion constraint matching" error
|
|
@@ -1692,9 +1700,10 @@ mutation {
|
|
|
1692
1700
|
|
|
1693
1701
|
- `connect`, `disconnect` and `set` take the target's own `Filters` input — a list of them
|
|
1694
1702
|
on a to-many relation, a single one that must match exactly one row on a to-one — so they
|
|
1695
|
-
select rows the same way a query does. A to-one `
|
|
1696
|
-
|
|
1697
|
-
|
|
1703
|
+
select rows the same way a query does. A to-one `connect` that matches no row, or several,
|
|
1704
|
+
fails with `DRIZZLE_NESTED_WRITE_INVALID` before anything is detached. A to-one
|
|
1705
|
+
`disconnect` is just `disconnect: true`, since there is only one row to detach. A filter
|
|
1706
|
+
that matches everything is rejected rather than attaching the whole table
|
|
1698
1707
|
- `set` replaces the relation wholesale — everything attached is detached first, then the
|
|
1699
1708
|
filters attach. It runs before `create`, so a row created in the same write survives it
|
|
1700
1709
|
- A to-one relation takes one operation at a time (`connect` **or** `disconnect` **or**
|
|
@@ -1716,14 +1725,42 @@ mutation {
|
|
|
1716
1725
|
— so a failure anywhere leaves nothing behind
|
|
1717
1726
|
- A `set` that carries only relation fields is a valid update: it changes what the row is
|
|
1718
1727
|
attached to without rewriting the row
|
|
1728
|
+
- When the parent write produces no row — an update whose `where` matched nothing, an
|
|
1729
|
+
insert or upsert a conflict turned into a no-op — whatever a parent-side `create` inserted
|
|
1730
|
+
for it is rolled back too, so no orphan is left. In a batch only that entry is undone
|
|
1731
|
+
- The returned rows reflect the nested write: on a self-referencing relation
|
|
1732
|
+
(`reports: { connect: … }` on the row itself), where a child-side statement rewrites the
|
|
1733
|
+
parent row, the parent rows are read back after it
|
|
1734
|
+
- A relation joining on more than one column per side gets no nested-write field: setting
|
|
1735
|
+
one key would leave the rest of the join (a tenant column, say) unmatched and unwritten
|
|
1736
|
+
|
|
1737
|
+
### A relation's own `where`
|
|
1738
|
+
|
|
1739
|
+
A relation declared with a `where` (`publishedPosts: r.many.posts({ …, where: { published: true } })`)
|
|
1740
|
+
is written through the same way it is read: only the rows it shows.
|
|
1741
|
+
|
|
1742
|
+
- `disconnect`, `set`, and the detaching a to-one attach does, reach only rows that satisfy
|
|
1743
|
+
the `where` — `publishedPosts: { set: [] }` leaves the drafts attached
|
|
1744
|
+
- `connect` and `set` attach only rows that satisfy it, so the relation shows what was
|
|
1745
|
+
attached. A to-one `connect` must match exactly one such row
|
|
1746
|
+
- There is no `create`: the inserted row is attached whether or not it satisfies the
|
|
1747
|
+
`where`, and the relation could come back without it. Create it with its own mutation, or
|
|
1748
|
+
through a relation with no `where`
|
|
1749
|
+
- A many-to-many `set` unlinks only the links to rows the `where` shows
|
|
1750
|
+
- A relation that inherits its `where` from the other side (declared without `from` / `to`)
|
|
1751
|
+
filters the parent rather than the target. Writing through it on a parent row it is
|
|
1752
|
+
hidden for fails with `DRIZZLE_NESTED_WRITE_INVALID`
|
|
1719
1753
|
|
|
1720
1754
|
### The target table's own rules
|
|
1721
1755
|
|
|
1722
1756
|
A nested write is a write to another table, so it follows that table's rules, not just the
|
|
1723
1757
|
root field's:
|
|
1724
1758
|
|
|
1725
|
-
- **`scope`**
|
|
1726
|
-
|
|
1759
|
+
- **`scope`** and **`softDelete`** narrow the rows `connect` / `disconnect` / `set` can
|
|
1760
|
+
reach — a many-to-many `set` keeps the links to rows it cannot reach — and
|
|
1761
|
+
**`contextValues`** stamp the rows a nested `create` inserts. A relation keyed on a
|
|
1762
|
+
`contextValues` column is not offered at all, and the payload of a nested `create` leaves
|
|
1763
|
+
out the context columns and the soft-delete marker, as the target's own create input does
|
|
1727
1764
|
- **`features`**: each nested input is generated only when the table it writes has that
|
|
1728
1765
|
write turned on. Whatever is left out is noted on the console at build time, and a
|
|
1729
1766
|
relation with nothing left loses its field altogether
|
|
@@ -1767,7 +1804,8 @@ mutation {
|
|
|
1767
1804
|
re-connecting is a no-op rather than a unique-constraint error
|
|
1768
1805
|
- `disconnect` deletes only the links, never the rows they pointed at
|
|
1769
1806
|
- `set: []` unlinks everything and links nothing back, which is how a many-to-many relation
|
|
1770
|
-
is cleared
|
|
1807
|
+
is cleared. "Everything" is everything the request could have unlinked: links to rows
|
|
1808
|
+
outside the target's `scope`, soft-deleted, or hidden by the relation's own `where` stay
|
|
1771
1809
|
- There is no `create`: inserting a row *and* linking it is a write to two tables, and the
|
|
1772
1810
|
link is what the relation field is for. Create the row with its own mutation, then
|
|
1773
1811
|
`connect` it
|
|
@@ -2452,7 +2490,15 @@ Generated schemas resolve nested relations without N+1 query explosions:
|
|
|
2452
2490
|
`ROW_NUMBER() OVER (PARTITION BY …)` window function — still one query, not one per
|
|
2453
2491
|
parent. A relation joined on several columns (`from: [a, b], to: [x, y]`) is batched,
|
|
2454
2492
|
grouped and partitioned by all of them, matching the parents with
|
|
2455
|
-
`(a = … AND b = …) OR …` instead of `IN (…)
|
|
2493
|
+
`(a = … AND b = …) OR …` instead of `IN (…)`, nested as a balanced tree so a batch of
|
|
2494
|
+
thousands of parents stays within SQLite's expression-depth limit. A batch over 10,000 key
|
|
2495
|
+
parameters runs as several queries, each parent read whole by one of them. The parent's join values are bound as the
|
|
2496
|
+
column holds them, not in their GraphQL output form, so a relation joined on a timestamp,
|
|
2497
|
+
`date`, `bigint` or decimal column works on this path and in `<relation>Aggregate` too. A
|
|
2498
|
+
parent row this library remapped keeps its original values for that; a parent your own
|
|
2499
|
+
resolver built in GraphQL form (an ISO string for a timestamp, say) has them mapped back the
|
|
2500
|
+
way an input value would be, which cuts a `'string'`-mode microsecond timestamp to
|
|
2501
|
+
milliseconds — return the row as drizzle read it to keep the full precision.
|
|
2456
2502
|
|
|
2457
2503
|
```Typescript
|
|
2458
2504
|
// entities.fieldResolvers is keyed by table name, then relation name
|