Repository navigation
Conversation
47 of 48 tasks
Merged
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
4.0, collected on
next: breaking changes deferred from 3.x, where they would break working code (in 3.x these are warnings or opt-in options), and larger features.Packaging
^3.3.0as peer dependency, the tested version. 3.x requires>=2.3.0 <4(Bound the knex peer dependency range #95).exportsmap, no default export,engines: ^20.19.0 || >=22.12.0(Convert objection to an ES module package #140).lib, the tests on vitest, TypeScript 7, sqlite3 6, CI on Node 20.19.0 and up.packageManagerfield,pnpm-lock.yaml,onlyBuiltDependenciesforsqlite3, CI withpnpm install --frozen-lockfile, scripts and contributing docs, and check thatpnpm packproduces the same package (was Switch from npm to pnpm #136, PR Switch from npm to pnpm #201). With the old Node versions dropped, no workaround for pnpm's Node requirement is needed.limitPerParentwithout window functions) and its CI job (Drop MySQL 5.7 support in 4.0 #292), PR Drop support for MySQL 5.7 (#292) #294).main: create a3.xbranch and deploy the docs from it, so the docs workflow onmaincan't deploy the 4.0 docs over the 3.x ones (was The docs workflow on main can still deploy the 4.0 docs #134).Errors instead of 3.x warnings
onConflict(),ignore()ormerge()is used withinsertGraph()/upsertGraph(). 3.x ignores the clause with a warning (InsertGraph does not work with onConflict Vincit/objection.js#2156, PR Throw on onConflict() with insertGraph() and upsertGraph() (#2156) #199).noInset. 3.x warns (Validate graph options and reject unknown keys #84, PR Throw on unknown graph options (#84) #259).*or^, like'a.^.b'or'*.a', inwithGraphFetched()and graph options alike. 3.x silently drops the rest (Smaller graph issues: uuid case, recursive option expressions, #ref left in results #221), PR Throw for anything after * or ^ in relation expressions (#221) #304).skipUndefined()(PR Remove the deprecated skipUndefined() #200).insertGraph()withrelatefor new models below related models, which are ignored today. 3.9.0 warns (insertGraph() with relate silently drops new children below related nodes #217), PR Throw in insertGraph() for new models below related models (#217) #295).delete()withorderBy()andlimit(): knex drops the order, so the limit deletes arbitrary rows. 3.9.0 warns (Smaller relation write issues: MySQL delete with order and limit, extras-only patch count, duplicate join rows #235, PR Throw for offset() in updates and deletes and for ordered, limited deletes on MySQL (#286, #235) #298).unrelate()applies the relation'smodify/filteralso without a where clause, like HasMany and ManyToMany (BelongsToOne unrelate() ignores the relation's modify without a where clause #234, PR Apply the relation's modify to BelongsToOne unrelate() without a where clause (#234) #249), PR Apply the relation's modify to BelongsToOne unrelate() without a where clause (#234) #301).fromJson(), silently get no related models. A hidden flag marks models built from database rows ($fetchGraph doesn't load relation if the foreign key column is not selected Vincit/objection.js#1832, Decide which 3.x warnings become errors in 4.0 #272, PR Throw for missing and array join properties when fetching relations (#1832, #2258, #114) #302).HasManyRelationVincit/objection.js#2667, Decide which 3.x warnings become errors in 4.0 #272, PR Throw for missing and array join properties when fetching relations (#1832, #2258, #114) #302).Behaviour changes
snakeCaseMappers()converts colon keys declared injsonSchema.propertiesas a whole, so columns likens:foo_barround-trip withpreserveJsonKeys(PR Preserve the JSON keys of field expressions in snakeCaseMappers() by default (#1089) #231), PR Convert declared keys with a colon as a whole in snakeCaseMappers() (#1089) #297).resultin static hooks the value the caller gets:after*hooks run last, afterfirst()and*AndFetch, with the real shape (model, array, number orundefined), and a returned value is used as is.before*hooks getundefined(result in static hook arguments is always an array (Vincit#1842) #66, StaticHookArguments#result is always an array Vincit/objection.js#1842, PR Pass static after hooks the result in the shape that the query returns (#66) #308).withGraphFetched()assigns related models after their staticafterFindhook, so a returned value is used.withGraphJoined()calls the hook too, for changes in place: a returned value is ignored there and documented, with a tracking issue for using it. Do it together with theafter*result change (withGraphFetched didn't return staticHook afterFind result Vincit/objection.js#1963, PRs Assign what the relation queries of withGraphFetched() return to the owners (#1963) #312, Call the afterFind hooks of the related model classes in withGraphJoined() (#1963) #313).$setRelated()/$appendRelated()keep foreign keys in sync: the key follows the relation, and becomesnullwhen the relation is cleared, when the related model has no key yet, and for models removed from a has-many relation. Conflicting keys set explicitly are overwritten.fromJson()/$setJson()don't sync. Inserts through$relatedQuery()may set the key in memory before their relate query runs (Keep foreign keys in sync in $setRelated() and $appendRelated() (#2001) #100, Feature request: setRelated modifies associated id column Vincit/objection.js#2001, PR Keep foreign keys in sync in $setRelated() and $appendRelated() (#2001) #100).count(),sum(), …) as column selections infindSelection()/hasSelection(), which changes the columns root andwithGraphFetched()queries add: aggregates match by their alias only, and the workarounds inRelationJoinergo (Aggregates in withGraphJoined() modifiers are treated as column selections (Vincit#2219) #111, withGraphJoined doesn't select root object's columns when using modifiers and more than 1 relation Vincit/objection.js#2219), PR Match aggregates only by their alias in selections (#111) #303).limit()/offset()/first()in graph modifiers per parent by default. 3.x has the opt-inlimitPerParentgraph option (limit()/first()in graph modifiers apply to all parents instead of per parent (Vincit#429) #127, modifyEager#builder.limit for eager limits entire N+1 Vincit/objection.js#429, PR Apply limit(), offset() and first() in graph modifiers per parent by default (#127) #229).limitPerParent, find out whether a relation query has a limit without building it twice, so itsmodifyandonBuildhooks run once (PR Apply limit(), offset() and first() in graph modifiers per parent by default (#127) #229), PR Drop support for MySQL 5.7 (#292) #294).Run the graph operations of→ not needed: the deadlock was fixed in 3.6.0 (patches by id), and the docs already say graph writes aren't atomic without a transaction.insertGraph()/upsertGraph()one after another on MySQL (upsertGraph() without a transaction can deadlock on MySQL 8 #135)'[movies.reviews, movies.actors]', instead of silently using only the last one: inwithGraphFetched()/withGraphJoined()and other relation expressions (3.x warns), and in graph options (Accept relation expressions for graph options like noInsert #131, PR Throw for relation expressions that name a relation twice (#131) #251).update()rejects field expression keys like'meta:color', so it always replaces whole values. 3.x writes them withjsonb_setand validates them like a patch (update() with field expressions: validation is wrong, a column default can wipe the JSON column #160, PR Reject field expression keys in update() (#160) #230).insert()rejects field expression keys with the sameValidationErrorasupdate()(PR Reject field expression keys in update() (#160) #230), PR Reject field expression keys in insert() (#160) #296).asArray()returns[]forundefined, and theresultof static hooks is[]where it was[null](asArray(): return [] for undefined #129, PR Return an empty array from asArray() for undefined (#129) #210).upsertGraph()always clears the owner's foreign key before deleting aBelongsToOneRelationrow, likeunrelate(), whatever the owner's schema says. 3.9.0 only does it when the schema allowsnull(PR Set nullable foreign keys to null before upsertGraph() deletes the rows of BelongsToOneRelations (#212) #250) and otherwise deletes first, leaving the reference to the database. Schemas or columns that don't allownullthen throw instead of cascading the owner away; the schema check of PR Set nullable foreign keys to null before upsertGraph() deletes the rows of BelongsToOneRelations (#212) #250 goes (upsertGraph() removing a BelongsToOne model deletes the row before clearing the owner's foreign key #212), PR Always clear the foreign key before upsertGraph() deletes the row of a BelongsToOneRelation (#212) #299).upsertGraph()ignores a#unrelate/#deletemarker on aHasOneRelation/HasOneThroughRelationmodel that isn't the current one and keeps the current model, likeBelongsToOneRelationsince 3.9.0. Arrays of to-many relations still replace the list (Markers on a HasOne model that isn't the current one delete the current model #261), PR Keep the current HasOneRelation model in upsertGraph() when the graph ignores a different model (#261) #300).upsertGraph()runs has-many relates before deletes, so rows moved in a tree withON DELETE CASCADE, or up a level while their old parent is deleted, aren't deleted with their old parent or fail. Rows moved below related models are kept since 3.9.0 (PR Don't delete rows that upsertGraph() moves below related models (#214) #278). Unique foreign keys replaced in the same graph (e.g.HasOneRelation) must keep working (upsertGraph() deletes rows that the same graph relates to another owner #214). Design this as one order of the graph operations, and fold the separate step that deletes replacedBelongsToOneRelationrows after the patches (PR Delete replaced BelongsToOneRelation rows in upsertGraph() after pointing their owners to the new rows #228) into it. (PRs Plan the actions of upsertGraph() before the first write (#214) #307, Relate moved rows in upsertGraph() before deleting the removed rows (#214) #309, Throw in upsertGraph() when a relate or update finds no row (#214) #310)findById()andfindByIds()put their id condition in its own where group, like$query()and the*ById()writes, sofindById(1).patch(…).orWhere(b)can't write other rows. "Id or slug" lookups becomewhere('id', x).orWhere('slug', x)(deleteById() and findById().patch()/delete() let a user's orWhere() write other rows #232, Decide whether findById() keeps its id condition in its own where group #271, PR Keep the id condition of findById() and findByIds() in its own where group (#271) #315).limit()on MySQL and SQL Server without the outerid in (…)query, which needs a second form today (PR Select the related ids of many-to-many patches with join table extras from their filter query (#161) #266, PR Pick the related ids of limited many-to-many patches from their filter query (#161) #322).pg,pgnative,cockroachdb) return them, so JSON-looking strings stay strings; other drivers keep the 3.x parsing.static jsonColumnFormat = 'auto' | 'text' | 'parsed'overrides it, e.g.'text'for MariaDB or JSON in Postgrestextcolumns (Postgres: JSON columns holding a JSON-looking string are parsed twice #205, PR Leave JSON attributes as the Postgres drivers return them, and add jsonColumnFormat (#205) #321).Typings
Strict mode, narrowing after
select()and the rebind slot change how builder types carry the model, result and column scope, so they should be designed together. Details in #16.TypeConfig { strict: false }as the opt-out (prototype Prototype opt-in strict typings with column scopes and FromSchema #94). Before 4.0 ships, fix the gaps that cause false errors:snakeCaseMappers(), generic builders followed bywithGraph*(), and column scopes that depend on call order. Column names instringvariables needref()or a cast.FromSchemaworks in both modes. Measure the check time on a real codebase before the release. Done in PR Check the columns and values of queries against the models in the typings (#16) #327: columns checked against the relation graph, strict at 1.1–1.4× loose check time. Generic code followed bywithGraph*()needsref()for column methods (accepted for 4.0).select()(PR Narrow the result rows of select() and the aggregates in the typings (#16) #329).*QueryBuilderTypemembers, which are removed (draft Add a rebind slot for custom query builder types (#319) #125). Designed together with strict mode, as one way builder types carry the model, result and column scope. The check-time measurement covers both; if the combined cost is too high, keep the five members instead (PR Declare custom query builder types through a single RebindType member (#16) #317).QueryBuilder<out M>is left to the strict mode design.$relatedQuery()/relatedQuery()of one-relations resolve toM | undefined(TypeScript return type from $relatedQuery is not optional for optional relation Vincit/objection.js#2355), andtoJSON()returns a recursive JSON type, so relations aren't typed as model instances (ModelObject with relations Vincit/objection.js#2380) (PRs Type $relatedQuery() of to-one relations as possibly undefined (#2355) #330, Type the result of toJSON() as the model's JSON, with related models as JSON (#2380) #331).→ kept as in 3.x:withGraphnarrowing: type to-one relations asT | nullnullonly where the model declares the relation nullable (parent?: Person | null), as otherwise narrowed results no longer fit their declared model type.relate().returning()asPojo[]instead ofany[]. Later, once typed relation mappings are common: precise row types per relation kind (related rows for HasMany, the owner row for BelongsToOne, join rows for ManyToMany) (PR Type the rows of relate().returning() as plain objects (#53) #332).insert()/patch()types, as both ignore it (PR Reject relation data in the types of insert(), patch() and update() (#53) #320).allowMarkersfromInsertGraphOptions.insertGraph()rejects markers since 3.8.0 (PR Remove the deprecated allowMarkers from InsertGraphOptions #208).Features
tagIds = [1, 4, 7], as its own relation class; read-only first is an option ( Solution for Fixing Binding Issues with Array in JSON Field inHasManyRelationVincit/objection.js#2667, from Upstream backlog: features for later and reports needing a repro #103). Done asBelongsToManyRelation(PRs Add BelongsToManyRelation for owners that hold an array of keys (#2667) #319, Support relate(), unrelate(), insert(), delete() and graph writes for BelongsToManyRelation (#2667) #323).ref()and thewhereJson*methods for MySQL and SQLite: objection renders the SQL per database, SQL Server throws (JSON support for mysql and sqlite3 Vincit/objection.js#276, from Upstream backlog: features for later and reports needing a repro #103, PRs Render the JSON field expressions of ref() for MySQL and SQLite (#276) #324–Render the whereJson*() methods for MySQL and SQLite (#276) #326).modifyJoin(relationPath, (join, { owner, related, joinTable }) => …)to adjust the ON clause injoinRelated()andwithGraphJoined(), likemodifyGraph()(Add a way to modify the "on clause" when using joinRelated. Vincit/objection.js#1975, from Upstream backlog: features for later and reports needing a repro #103, PR Add modifyJoin() to modify the ON clause of relation joins (#1975) #314).