Migrating 4.x to 5.x
This guide will likely work for apps on 3.28 that have resolved EmberData deprecations from the 3.x series.
This guide is primarily intended for apps that got "stuck" on either 4.6 (due to ModelFragments) or 4.12 (typically due to the ArrayLike deprecation)
Note - it is not actually a requirement of 5.x to replace Models with Schemas (nor to replace adapters/serializers with requests). These things are deprecated in 5.x, but they still work.
The reason to take the approach outlined in this guide is because we have used capabilities provided by the @warp-drive/legacy package and by LegacyMode schemas together with Extensions to mimic much of the removed API surface to allow apps to bridge the gap to 5.x more easily.
Pre-Migration (update to Native Types)
If you use Typescript, before migrating, you should update your types to use the native types provided by both ember-source
and WarpDrive.
You can do this even if you are on an older version (pre-5.x) that didn't ship it's own types by using the "types" packages we specially publish for this purpose.
Step 1 - delete all the ember/ember-data DT type packages
{
"dependencies": {
"@types/ember": "4.0.11",
"@types/ember-data": "4.4.16",
"@types/ember-data__adapter": "4.0.6",
"@types/ember-data__model": "4.0.5",
"@types/ember-data__serializer": "4.0.6",
"@types/ember-data__store": "4.0.7",
"@types/ember__application": "4.0.11",
"@types/ember__array": "4.0.10",
"@types/ember__component": "4.0.22",
"@types/ember__controller": "4.0.12",
"@types/ember__debug": "4.0.8",
"@types/ember__destroyable": "4.0.5",
"@types/ember__engine": "4.0.11",
"@types/ember__error": "4.0.6",
"@types/ember__helper": "4.0.7",
"@types/ember__modifier": "4.0.9",
"@types/ember__object": "4.0.12",
"@types/ember__owner": "4.0.9",
"@types/ember__routing": "4.0.22",
"@types/ember__runloop": "4.0.10",
"@types/ember__service": "4.0.9",
"@types/ember__string": "3.16.3",
"@types/ember__template": "4.0.7",
"@types/ember__test": "4.0.6",
"@types/ember__utils": "4.0.7",
}
}
Step 2 - install the official packages using the latest versions.
Each package that we publish has a corresponding types-only package that you can use to gain access to official types while still using an older version of the library that doesn't have its own types yet.
Package | Types Package |
---|---|
ember-data | ember-data-types |
@ember-data/* | @ember-data-types/* |
@warp-drive/* | @warp-drive-types/* |
💡 Why are there non-types packages below?
Starting in 5.7, due to package unification these types also require the installation of the new "package unification" packages since the actual source code (and types) originates from there.
pnpm add -E ember-data-types@canary \
@ember-data-types/adapter@canary \
@ember-data-types/graph@canary \
@ember-data-types/json-api@canary \
@ember-data-types/legacy-compat@canary \
@ember-data-types/model@canary \
@ember-data-types/request@canary \
@ember-data-types/request-utils@canary \
@ember-data-types/serializer@canary \
@ember-data-types/store@canary \
@warp-drive-types/core-types@canary \
@warp-drive/core@canary \
@warp-drive/json-api@canary \
@warp-drive/legacy@canary \
@warp-drive/utilities@canary
npm add -E ember-data-types@canary \
@ember-data-types/adapter@canary \
@ember-data-types/graph@canary \
@ember-data-types/json-api@canary \
@ember-data-types/legacy-compat@canary \
@ember-data-types/model@canary \
@ember-data-types/request@canary \
@ember-data-types/request-utils@canary \
@ember-data-types/serializer@canary \
@ember-data-types/store@canary \
@warp-drive-types/core-types@canary
@warp-drive/core@canary \
@warp-drive/json-api@canary \
@warp-drive/legacy@canary \
@warp-drive/utilities@canary
yarn add -E ember-data-types@canary \
@ember-data-types/adapter@canary \
@ember-data-types/graph@canary \
@ember-data-types/json-api@canary \
@ember-data-types/legacy-compat@canary \
@ember-data-types/model@canary \
@ember-data-types/request@canary \
@ember-data-types/request-utils@canary \
@ember-data-types/serializer@canary \
@ember-data-types/store@canary \
@warp-drive-types/core-types@canary
@warp-drive/core@canary \
@warp-drive/json-api@canary \
@warp-drive/legacy@canary \
@warp-drive/utilities@canary
bun add --exact ember-data-types@canary \
@ember-data-types/adapter@canary \
@ember-data-types/graph@canary \
@ember-data-types/json-api@canary \
@ember-data-types/legacy-compat@canary \
@ember-data-types/model@canary \
@ember-data-types/request@canary \
@ember-data-types/request-utils@canary \
@ember-data-types/serializer@canary \
@ember-data-types/store@canary \
@warp-drive-types/core-types@canary
@warp-drive/core@canary \
@warp-drive/json-api@canary \
@warp-drive/legacy@canary \
@warp-drive/utilities@canary
This will install the following at the latest canary
{
"dependencies": {
"ember-data-types": "alpha",
"@ember-data-types/adapter": "alpha",
"@ember-data-types/graph": "alpha",
"@ember-data-types/json-api": "alpha",
"@ember-data-types/legacy-compat": "alpha",
"@ember-data-types/model": "alpha",
"@ember-data-types/request-utils": "alpha",
"@ember-data-types/request": "alpha",
"@ember-data-types/serializer": "alpha",
"@ember-data-types/store": "alpha",
"@warp-drive-types/core-types": "alpha",
"@warp-drive/core": "alpha",
"@warp-drive/json-api": "alpha"
"@warp-drive/legacy": "alpha",
"@warp-drive/utilities": "alpha",
}
}
Step 3 - configure tsconfig.json
{
"compilerOptions": {
"types": [
"ember-source/types",
"ember-data-types/unstable-preview-types",
"@ember-data-types/store/unstable-preview-types",
"@ember-data-types/adapter/unstable-preview-types",
"@ember-data-types/graph/unstable-preview-types",
"@ember-data-types/json-api/unstable-preview-types",
"@ember-data-types/legacy-compat/unstable-preview-types",
"@ember-data-types/request/unstable-preview-types",
"@ember-data-types/request-utils/unstable-preview-types",
"@ember-data-types/model/unstable-preview-types",
"@ember-data-types/serializer/unstable-preview-types",
"@warp-drive-types/core-types/unstable-preview-types"
]
}
}
Step 4 - brand your models
import Model from '@ember-data/model';
import type { Type } from '@warp-drive/core-types/symbols';
export default class User extends Model {
declare [Type]: 'user';
}
Step 5 - replace registry usage with branded model usages
// find
store.findRecord('user', '1');
store.findRecord<User>('user', '1');
store.findAll('user');
store.findAll<User>('user');
store.query('user', {});
store.query<User>('user');
store.queryRecord('user', {});
store.queryRecord<User>('user');
// peek
store.peekRecord('user', '1');
store.peekRecord<User>('user', '1');
// push
const user = store.push({
const user = store.push<User>({
data: {
type: 'user',
id: '1',
attributes: { name: 'Chris' }
}
}) as User;
});
Additional Resources
Step 6 - fix other type issues that arise
ArrayLike API usage is likely to give you the most issues here, if anything does.
Migration
Step 1 - Install the Mirror Packages
pnpm add -E @warp-drive-mirror/core@canary @warp-drive-mirror/json-api@canary @warp-drive-mirror/ember@canary @warp-drive-mirror/legacy@canary @warp-drive-mirror/utilities@canary
npm add -E @warp-drive-mirror/core@canary @warp-drive-mirror/json-api@canary @warp-drive-mirror/ember@canary @warp-drive-mirror/legacy@canary @warp-drive-mirror/utilities@canary
yarn add -E @warp-drive-mirror/core@canary @warp-drive-mirror/json-api@canary @warp-drive-mirror/ember@canary @warp-drive-mirror/legacy@canary @warp-drive-mirror/utilities@canary
bun add --exact @warp-drive-mirror/core@canary @warp-drive-mirror/json-api@canary @warp-drive-mirror/ember@canary @warp-drive-mirror/legacy@canary @warp-drive-mirror/utilities@canary
Step 2 - Configure The Build
import { setConfig } from '@warp-drive-mirror/core/build-config';
import { buildMacros } from '@embroider/macros/babel';
const Macros = buildMacros({
configure: (config) => {
setConfig(config, {
// for universal apps this MUST be at least 5.6
compatWith: '5.6'
});
},
});
export default {
plugins: [
...Macros.babelMacros,
],
};
Step 3 - Configure Reactivity
import '@warp-drive-mirror/ember/install';
Step 4 - Configure the Store
We use useLegacyStore
to create a store service preconfigured with maximal support for legacy APIs.
import { useLegacyStore } from '@warp-drive/legacy';
import { JSONAPICache } from '@warp-drive/json-api';
export default useLegacyStore({
linksMode: false,
legacyRequests: true,
cache: JSONAPICache,
schemas: [
// -- your schemas here
],
});
Additional Reading (for when you have questions later)
Step 5 - Convert + Profit
Key concepts:
- LegacyResourceSchema
- LegacyModeFieldSchema
- registerTrait
- LegacyTrait
- CAUTION_MEGA_DANGER_ZONE_registerExtension()
- CAUTION_MEGA_DANGER_ZONE_Extension
A Basic Model
We migrate models with ResourceSchemas and extensions.
import Model, { attr, belongsTo, hasMany, type AsyncHasMany } from '@ember-data/model';
import type { Type } from '@warp-drive/core-types/symbols';
import { cached } from '@glimmer/tracking';
import { computed } from '@ember/object';
export default class User extends Model {
declare [Type]: 'user';
@attr firstName;
@attr lastName;
@belongsTo('user', { async: false, inverse: null })
declare bestFriend: User | null;
@hasMany('user', { async: true, inverse: null })
declare friends: AsyncHasMany<User>;
@cached
get fullName() {
return this.firstName + ' ' + this.lastName;
}
@computed('firstName')
get greeting() {
return 'Hello ' + this.firstName + '!';
}
sayHi() {
alert(this.greeting);
}
}
A Model with Mixins
We can migrate mixins with traits and extensions.
import Model, { attr } from '@ember-data/model';
import type { Type } from '@warp-drive/core-types/symbols';
import Timestamped from '../mixins/timestamped';
export default class User extends Model.extend(Timestamped) {
declare [Type]: 'user';
@attr firstName;
@attr lastName;
}
import Mixin from '@ember/object/mixin';
import { attr } from '@ember-data/model';
export default Mixin.create({
createdAt: attr(),
deletedAt: attr(),
updatedAt: attr(),
async softDelete() {
const result = await fetch(`/api/${this.constructor.modelName}/${this.id}`, { method: 'DELETE' });
const newTimestamps = await result.json();
this.store.push({
data: {
type: this.constructor.modelName,
id: this.id,
attributes: newTimestamps
}
});
}
});
A Model with Fragments
TBD
Post Migration
- drop the old packages
- drop config for the old packages
- delete the store service
- rename v2-store => store
- rename
@warp-drive-mirror
=>@warp-drive