Upgrading from 4 to 5
Deprecation Removals
These previously deprecated members have been removed:
Resource.getKey() -> Resource.key
Simply rename this to get key()
Resource.getEntitySchema() -> Resource
This has been simplified to simply use the Resource itself:
- before
- after
class MyResource extends Resource {
static customEndpoint<T extends typeof MyResource>(this: T) {
return {
...super.listShape(),
// notice the next line
schema: { results: [this.getEntitySchema()], nextPage: '' },
};
}
}
class MyResource extends Resource {
static customEndpoint<T extends typeof MyResource>(this: T) {
return {
...super.listShape(),
// notice the next line
schema: { results: [this], nextPage: '' },
};
}
}
Other breaking changes
yarn add @rest-hooks/test@2 @rest-hooks/legacy@2
Be sure to also upgrade these libraries if you use them:
@rest-hooks/test@2
@rest-hooks/legacy@2
These libraries don't have any breaking changes within themselves, but
they do require rest-hooks@5
and (reflexively) rest-hooks@5
requires
at least v2.
Network Definitions (Resource/FetchShape, etc)
FetchShape: {type: 'delete'} -> { type: 'mutate', schema: new schemas.Delete(this) }
Resource.deleteShape()
will continue to work as expected. However, if
you defined some custom shapes with type: 'delete'
- before
- after
class MyResource extends Resource {
static someOtherDeleteShape<T extends typeof Resource>(
this: T,
): DeleteShape<any, Readonly<object>> {
const options = this.getFetchOptions();
return {
// changed
type: 'delete',
// changed
schema: this.asSchema(),
options,
getFetchKey: (params: object) => {
return 'DELETE ' + this.url(params);
},
fetch: (params: Readonly<object>) => {
return this.fetch('delete', this.url(params));
},
};
}
}
import { schemas } from 'rest-hooks';
class MyResource extends Resource {
static someOtherDeleteShape<T extends typeof Resource>(
this: T,
): DeleteShape<any, Readonly<object>> {
const options = this.getFetchInit();
return {
// changed
type: 'mutate',
// changed
schema: new schemas.Delete(this),
options,
getFetchKey: (params: object) => {
return 'DELETE ' + this.url(params);
},
fetch: (params: Readonly<object>) => {
return this.fetch('delete', this.url(params));
},
};
}
}
Validation Errors: `This is likely due to a malformed response`
To aid with common schema definition or networking errors, Rest Hooks will sometimes throw an error. This only occurs during development, to help users correctly define their schemas and endpoints.
While the heuristics have been heavily tuned, if you don't believe the errors reported are valid please report a bug.
When reporting, be sure to include
- The exact network response from the network inspector
- The full schema definition.
Alternatively, this can be disabled by adding static automaticValidation = 'silent' | 'warn'
class MyResource extends Resource {
static automaticValidation = 'silent' as const;
// ...
}
Warn will no longer throw an error, but still add a message to the browser console. Silent removes the check completely.
Imports
import { reducer, NetworkManager } from '@rest-hooks/core'
Many 'advanced' features of rest-hooks are not longer exported by 'rest-hooks' package itself, but require importing from @rest-hooks/core
- reducer
- NetworkManager
- action creators:
- createFetch
- createReceive
- createReceiveError
Managers
These only apply if you have a custom Manager
action.meta.url -> action.meta.key
It's recommend to now use the action creators
exported from @rest-hooks/core
- createFetch
- createReceive
- createReceiveError
getState()
This is very unlikely to make a difference, but the internal cache state
(accessible with getState()) might be slightly different. Mutations now
result in entries in meta
and results
. This brings them more in line with
reads, making the distinction simply about which hooks they are allowed
in. (To prevent unwanted side effects.)
Cache Lifetime Policy
useInvalidator() triggers suspense
You can likely remove invalidIfStale if used in conjunction with useInvalidator()
invalidIfStale is still useful to disable the stale-while-revalidate
policy.
`delete` suspends instead of throwing 404
Delete marks an entity as deleted. Any response requiring that entity will suspend. Previously it throw a 404 error.
Missing entities suspend
Required entities missing from network response will now throw error in useResource() just like other unexpected deserializations.
Use SimpleRecord for optional entities.
- before
- after
const schema = {
data: MyEntity,
};
class OptionalSchema extends SimpleRecord {
readonly data: MyEntity | null = null;
static schema = {
data: MyEntity,
};
}
const schema = OptionalSchema;
invalidIfStale
When invalidIfStale is true, useCache() and useStatefulResource() will no longer return entities, even if they are in the cache.
This matches the expected behavior that any loading
data should not be usable.
Upgrading from beta versions to final
The last breaking changes introduced to rest-hook
were in delta.0
where TTL
and deletes were reworked. If you are on a more recent beta (i
, j
, k
, rc
),
upgrades should be as simple as updating the version.
If this is not the case, please report a bug.
Deprecations
After a successful upgrade, it is recommended to adopt the modern practices.
Resource.fetchOptionsPlugin() -> Resource.getFetchInit()
- before
- after
class AuthdResource extends Resource {
static fetchOptionsPlugin = (options: RequestInit) => ({
...options,
credentials: 'same-origin',
});
}
class AuthdResource extends Resource {
static getFetchInit = (init: RequestInit) => ({
...init,
credentials: 'same-origin',
});
}
(Resource.getFetchInit())../api/resource#static-getfetchinitinit-requestinit-requestinit)
Resource.getFetchOptions() -> Resource.getEndpointExtra()
- before
- after
class PollingResource extends Resource {
static getFetchOptions(): FetchOptions {
return {
pollFrequency: 5000, // every 5 seconds
};
}
}
class PollingResource extends Resource {
static getEndpointExtra(): FetchOptions {
return {
pollFrequency: 5000, // every 5 seconds
};
}
}
(Resource.getEndpointExtra())../api/resource#static-getendpointextra--endpointextraoptions--undefined)
Resource.asSchema() -> Resource
This has been simplified to simply use the Resource itself:
- before
- after
class MyResource extends Resource {
static customEndpoint<T extends typeof MyResource>(this: T) {
return {
...super.listShape(),
// notice the next line
schema: { results: [this.asSchema()], nextPage: '' },
};
}
}
class MyResource extends Resource {
static customEndpoint<T extends typeof MyResource>(this: T) {
return {
...super.listShape(),
// notice the next line
schema: { results: [this], nextPage: '' },
};
}
}
@rest-hooks/legacy
For v5 of Rest Hooks, the existing Resource
and SimpleResource
classes will
be exported.
In v6, this will no longer be the case. However, they will continue to live in @rest-hooks/legacy
, allowing
easy safe upgrade to v6 by simply changing the import path. However, it is still recommended to
try to migrate to @rest-hooks/rest
as this is the future. v1 of @rest-hooks/rest will be the easiest to
start with.
yarn add @rest-hooks/legacy
- before
- after
import { Resource } from 'rest-hooks';
class MyResource extends Resource {}
import { Resource } from '@rest-hooks/legacy';
class MyResource extends Resource {}
@rest-hooks/rest
Rest Hooks is protocol agnostic, so the REST/CRUD specific class Resource
will eventually be fully deprecated and removed. @rest-hooks/rest
is intended as its
replacement. Other supplementary libraries like @rest-hooks/graphql
could be
added in the future, for intance. This is also beneficial as these libraries
change more frequently than the core of rest hooks.
yarn add @rest-hooks/rest
- before
- after
import { Resource } from 'rest-hooks';
class MyResource extends Resource {}
import { Resource } from '@rest-hooks/rest';
class MyResource extends Resource {}
Breaking change:
Nested entities
static schema
will return fromuseResource()
static schema
Nesting entities inside a schema will now denormalize those nested items.
- before
- after
import { Resource } from 'rest-hooks';
class ArticleResource extends Resource {
// other stuff omitted
readonly user: string = '';
static schema = {
user: UserResource,
};
}
const article = useResource(ArticleResource.detail(), { id });
const user = useCache(UserResource.detail(), { id: article.user });
FetchShape -> Endpoint
Endpoints use the builder pattern to make customization easy. Use extend() to customize.
@rest-hooks/endpoint is also its own package. This empowers you to publish interfaces for public APIs by marking @rest-hooks/endpoint as a peerDependency in the package.
- before
- after
import { Resource } from 'rest-hooks';
export default class UserResource extends Resource {
/** Retrieves current logged in user */
static currentShape<T extends typeof Resource>(this: T) {
return {
...this.detailShape(),
getFetchKey: () => {
return '/current_user/';
},
fetch: (params: {}, body?: Readonly<object | string>) => {
return this.fetch('post', `/current_user/`, body);
},
};
}
}
import { Resource } from '@rest-hooks/rest';
export default class UserResource extends Resource {
/** Retrieves current logged in user */
static current<T extends typeof Resource>(this: T) {
const endpoint = this.detail();
return endpoint.extend({
fetch() { return endpoint(this); }
url() { return '/current_user/' },
})
}
}
Currently all Endpoints also implement the FetchShape
interface, so feel free to incrementally migrate. This means using Endpoint and extended via object spreads will still work:
import { Resource } from 'rest-hooks';
export default class UserResource extends Resource {
static currentShape<T extends typeof Resource>(this: T) {
return {
// this is an Endpoint, but can be spread the same way
...this.detail(),
getFetchKey: () => {
return '/current_user/';
},
fetch: (params: {}, body?: Readonly<object | string>) => {
return this.fetch('post', `/current_user/`, body);
},
};
}
}
Eventually support for FetchShape will be deprecated, and then removed.
Summary of interface differences
- schema is optional
- type removed in favor of sideEffect
- type = 'read' -> sideEffect = undefined
- type = 'mutate' -> sideEffect = true
- options members elevated to top
- top level object should be the actual fetch