Command
Introduction
Command
is a class that creates a template for requests and all the necessary information needed for their
creation. Its strength is its strict and predictable data structure. This lets us dump data, save it to storage as a
JSON, and recreate it later. This approach allows you to develop a full persistence flow; you can easily persist command
dumps between sessions.
Command
contains information about its behavior in the queues or cache, data on the server, and information necessary
to execute a valid request. In combination with TypeScript, this results in a very friendly flow that’s resistant to
mistakes.
You can trigger a request with the send
method, which adds a request to the queue and returns the response.
Purpose
- Configures request templates
- Standardizes the system’s data schema
- Sends requests via dispatchers
Initialization
Command should be initialized from the Builder instance. This passes a shared reference to the place that manages communication in the application.
Hyper Fetch currently uses currying in the createCommand
method to achieve auto-generated types for the endpoint
string. This solution will be changed once this TypeScript issue
is resolved.
import { builder } from "./builder";
export const getUsers = builder.createCommand<UserModel[]>()({
method: "GET",
endpoint: "/users",
});
export const getUser = builder.createCommand<UserModel>()({
method: "GET",
endpoint: "/users/:userId",
});
export const postUser = builder.createCommand<UserModel, UserPostDataType>()({
method: "POST",
endpoint: "/users",
});
export const patchUser = builder.createCommand<UserModel, Partial<UserPostDataType>>()({
method: "PATCH",
endpoint: "/users/:userId",
});
export const deleteUser = builder.createCommand<null>()({
method: "DELETE",
endpoint: "/users/:userId",
});
Request building
Initialize
The process begins with command initialization. At this point, you can configure how the request will behave, but most of the configurations are optional. You can also prepare a global configuration in the Builder and avoid copying the setup between commands.
const postUser = builder.createCommand<null>()({
endpoint: "/some-endpoint",
headers: {},
auth: true,
method: "POST"
cancelable: false,
retry: 2,
retryTime: 1000,
cache: false,
cacheTime: 50000
queued: false,
deduplicate: false,
offline: false,
options: {}, // Client options
disableRequestInterceptors: false,
disableResponseInterceptors: false,
});
Request Data
Use the setData
method to instruct any data to be sent to the server.
// Regular data
postUser.setData({ name: "John", age: 18 })
// Form data
const data = new FormData();
...
postFile.setData(data)
Parameters
Parameters must be defined in command endpoint using :
const getNote = builder.createCommand()({
endpoint: "/note/:noteId";
})
const getCategory = builder.createCommand()({
endpoint: "/category/:categoryId";
})
const getCategoryNote = builder.createCommand()({
endpoint: "/category/:noteId";
})
When you have properly prepared commands that expect parameters, you can add parameters using the setParams
method. In
generic TypeScript, these parameters will match the endpoint parameters by using literal types and will require literal
types.
getNote.setParams({ noteId: 1 });
getCategory.setParams({ categoryId: 2 });
getCategoryNote.setParams({ categoryId: 2, noteId: 1 });
Query parameters
You can set query params with the setQueryParams
method. With TypeScript, you can set it up to be accepted as strings,
objects, or a strict interface. The encoding type for arrays and other options can be set up in the Builder. You can
also provide your own encoding logic.
getUsers.setQueryParams({ search: "John", sort: "age" });
Trigger request
You can perform a request with the send
method.
// Simple Send
getNotes.send();
// Chained Send
getUsers.setQueryParams({ search: "John", sort: "age" }).send();
// Multiple chained Send
getCategory.setParams({ categoryId: 2 }).setQueryParams({ sortNotes: "age" }).send();
For usage with React
checkout our hooks docs.
Features
You can read more in the API reference and guides.
Cancellation
Queueing
Offline
Deduplication
Authentication
[Data Mapping](/guides/01-Basic/Data Mapping.mdx)
Methods
Using methods on a command is different from other classes in Hyper Fetch. This is to ensure isolation between different uses, which allows you to avoid overwriting previously-prepared commands and to dynamically generate keys for queues or the cache.
Using any method on command returns its clone! We don't return a reference!
// ❌ WRONG
const command = getUser;
command.setParams({ userId: 1 }); // Returns CLONED command with assigned params
command.send(); // Server error - no params
// ✅ Good
const command = getUser;
const commandWithParams = command.setParams({ userId: 1 }); // Returns CLONED command with assigned params
commandWithParams.send(); // Success
Keys
Each command gets its identifiers – queueKey
, cacheKey
, abortKey
, and effectKey
. They are needed to determine
under which key items will be cached, queued, canceled or handled by Effects. By
default, keys are auto-generated based on the current parameters and endpoint and method values. However, you can
overwrite these values with other methods as needed.
Typescript
Builder has four generic types built in: Response
, Payload
, Local Error Response
, and QueryParams
.
type Response = { name: string }; // What's returned from request
type Payload = { email: string }; // What's send with request
type LocalError = { nameMessage: string }; // Additional "local" errors like errors for particular form
type QueryParams = { sort: string; search: string }; // Query params interface
const someCommand = builder.createCommand<Response, Payload, LocalError, QueryParams>()({
endpoint: "category/:categoryId",
});
someCommand.setData(); // Require the 'Payload' type
someCommand.setParams(); // Require { categoryId: Param } type
someCommand.setQueryParams(); // Require the 'QueryParams' type
const [data, error] = someCommand.send();
data; // Has 'Response' type
error; // Has 'GlobalError' or 'LocalError' type
Parameters
Configuration options
{
abortKey: string;
auth: boolean;
cache: boolean;
cacheKey: string;
cacheTime: number;
cancelable: boolean;
deduplicate: boolean;
deduplicateTime: number;
disableRequestInterceptors: boolean;
disableResponseInterceptors: boolean;
effectKey: string;
endpoint: GenericEndpoint;
garbageCollection: number;
headers: HeadersInit;
method: GET | POST | PUT | PATCH | DELETE;
offline: boolean;
options: ClientOptions;
queueKey: string;
queued: boolean;
retry: number;
retryTime: number;
}