Skip to main content
Version: 2.2

Resource

import { Resource } from 'rest-hooks';

export default class ArticleResource extends Resource {
readonly id: number | null = null;
readonly title: string = '';
readonly content: string = '';
readonly author: number | null = null;
readonly tags: string[] = [];

pk() {
return this.id;
}

static urlRoot = 'http://test.com/article/';
}
import { Resource } from 'rest-hooks';

export default class ArticleResource extends Resource {
id = null;
title = '';
content = '';
author = null;
tags = [];

pk() {
return this.id;
}

static urlRoot = 'http://test.com/article/';
}
import { Resource } from 'rest-hooks';

export default class ArticleResource extends Resource {
+id: ?number = null;
+title: string = '';
+content: string = '';
+author: ?number = null;
+tags: string[] = [];

pk() {
return this.id;
}

static urlRoot = 'http://test.com/article/';
}

Resource is an abstract class that will help you define the data you are working with. There are two sides to Resource - the static and instance side.

Static

Is used to define how you retrieve and mutate data across the network. There are several static methods that do this, but their ultimate purpose is to build FetchShapes, which tell the hooks how to process requests. Shapes are provided for the common REST request types. However, it is encouraged to build your own or override the provided ones to fit the needs of your API.

Instance

Instances are mostly for you to define how you want to interact with your data. This means you should start off by defining the fields you expect to see, and provide defaults in case they are not sent for some reason. Resource also requires that you define a method to get an entity's (entity is an instance of a Resource) unique identifier. (This is used for book-keeping the normalized cache.) Make sure to mark all members as readonly as you should be treating all your data as immutable (this library assumes that)!

You are encouraged to add your own member methods. Often times it is useful to provide methods for computed values that are commonly used in your React components.

A final note: Resource provides a factory method called fromJS() that will be used to construct instances. This is the only supported way of created Resources so please don't use constructors.

Factory method

static fromJS\<T extends typeof Resource>(this: T, props: Partial\<AbstractInstanceType\<T>>): AbstractInstanceType\<T>

This is used to create instances of the Resource you defined. Will copy over props provided to the instance in construction.

Be sure to always provide:

pk: () => string | number | null

PK stands for primary key and is intended to provide a standard means of retrieving a key identifier for any Resource. In many cases there will simply be an 'id' field member to return. In case of multicolumn you can simply join them together.

Multi-column primary key:

pk() {
return [this.firstCol, this.secondCol, this.thirdCol].join(',');
}

Null value

A null can be used as a default to indicate the resource has not been created yet. This is useful when initializing a creation form using Resource.fromJS() directly. If pk() resolves to null it is considered not persisted to the server, and thus will not be kept in the cache.

Other uses

While the pk() definition is key (pun intended) for making the normalized cache work; it also becomes quite convenient for sending to a react element when iterating on list results:

//....
return (
<div>
{results.map(result => <TheThing key={result.pk()} thing={result} />)}
</div>
)

Singleton Resources

What if there is only ever once instance of a Resource for your entire application? You don't really need to distinguish between each instance, so likely there was no id or similar field defined in the API. In these cases you can just return a literal like 'the_only_one'.

pk() {
return 'the_only_one';
}

static urlRoot: string

Used to build url patterns in url() and listUrl(). Used as the default in getKey() so typically you'll want this to be globally unique per Resource.

static getKey()

This defines the key for the Resource itself, rather than an instance. As seen below, by default it simply returns the urlRoot since this is typically globally unique. However if you want to share urlRoot across different Resources, be sure to override this.

/** Returns the globally unique identifier for this Resource */
static getKey<T extends typeof Resource>(this: T) {
return this.urlRoot;
}

Data methods

static merge\<T extends typeof Resource>(first: InstanceType\<T>, second: InstanceType\<T>) => InstanceType\<T>

Takes only the defined (non-default) values of first and second and creates a new instance copying them over. Second will override values of first. Merge is shallow, so you'll need to override this to do any deep merges.

static hasDefined\<T extends typeof Resource>(instance: InstanceType\<T>, key: keyof InstanceType\<T>) => boolean

Returns whether provided key is defined (non-default) in instance.

static toObjectDefined\<T extends typeof Resource>(instance: AbstractInstanceType\<T>) => Partial\<InstanceType\<T>>

Returns an Object with only the defined (non-default) members of instance.

static keysDefined\<T extends typeof Resource>(instance: InstanceType\<T>) => (keyof InstanceType\<T>)[]

Returns an Array of all defined (non-default) keys of instance.

Static network methods and properties

These are the basic building blocks used to compile the Fetch shapes below.

static url\<T extends typeof Resource>(urlParams?: Partial\<AbstractInstanceType\<T>>) => string

Computes the url based on the parameters. Default implementation follows /urlRoot/[pk] pattern.

Used in detailShape(), updateShape() partialUpdateShape(), and deleteShape()

static listUrl\<T extends typeof Resource>(searchParams?: Readonly\<Record\<string, string>>) => string

Computes url for retrieving list items. Defaults to urlRoot with searchParams being sent as GET parameters.

Used in listShape() and createShape()

static fetch\<T extends typeof Resource>(method: "get" | "post" | "put" | "patch" | "delete" | "options", url: string, body?: Readonly\<object | string>>) => Promise\<any>

Performs the actual network fetch returning a promise that resolves to the network response or rejects on network error. This can be useful to override to really customize your transport.

static getEntitySchema() => schema.Entity

Returns the shape of the data when requesting one resource at a time. Defaults to a plain object containing the keys. This can be useful to override if your response is in a different form.

static getRequestOptions() => RequestOptions | undefined

Returns the default request options for this resource. By default this returns undefined

Fetch shapes

These provide the standard CRUD shapes common in REST APIs. Feel free to customize or add new shapes based to match your API.

detailShape(): ReadShape

A GET request using standard url() that receives a detail body. Mostly useful with useResource

listShape(): ReadShape

A GET request using listUrl() that receives a list of entities. Mostly useful with useResource

createShape(): MutateShape

A POST request sending a payload to listUrl() with empty params, and expecting a detail body response. Mostly useful with useFetcher

updateShape(): MutateShape

A PUT request sending a payload to a url() expecting a detail body response. Mostly useful with useFetcher

partialUpdateShape(): MutateShape

A PATCH request sending a partial payload to url() expecting a detail body response. Mostly useful with useFetcher

deleteShape(): DeleteShape

A DELETE request sent to url() Mostly useful with useFetcher