Skip to content
On this page

Data flow in Remote Operation

Farfetched is designed to deal with data on the remote source (e.g. backend server), so there is an abstraction to represent an operation on this data — Remote Operation. For now there are two types of Remote Operations: Query and Mutation.

Because Farfetched considers data on the remote source as untrusted, it is required to pass any response through a couple stages of validation and transformation before it is used in the application.

Flow of for any Remote Operation
null

Basic and specific factories

There are two types of factories for Remote Operations: basic and specific. Basic factories are used to create Remote Operations with a more control of data-flow in user-land, while specific factories are used to create Remote Operations with a more control of data-flow in the library. Specific factories are built on top of basic ones and are providing better DX for more specific use-cases.

E.g. quite often API is basically HTTP-endpoint, which responses to you with some JSON — Farfetched provides you createJsonQuery for this case. This factory hides complexity under the declarative API and handles a lot of edge-cases for you

Basic factories

Specific factories

TIP

Data-flow control is a boring and complex task, so it is recommended to use specific factories in many cases to delegate this task to the library.

Data-flow in specific factories

Since only specific factories are allows Farfetched to have a full control of data-flow, in the following articles we will take a closer look to them. Basic factories work in the same way, but they require more attention from the user.

Request-response cycle

The first step is to send a request to the remote source and wait for a response. Because of Farfetched handles this stage internally, user-land code have to describe only the desired result of this stage and the library will perform the request-response cycle internally in the most optimal way.

Failed response stops the data-flow and returns control to the user-land code through .finished.failed Event. Successful response continues the data-flow and passes control to the next step — response parsing.

Response parsing

Specific factories of Farfetched performs this stage internally, based on a use-case they were created for. In case of parsing error, the data-flow stops and returns control to the user-land code through .finished.failed Event. Otherwise, the data-flow continues and passes control to the next step — contract application.

JSON example

createJsonQuery and createJsonMutation use JSON.parse to parse the response and throw an error if the response is not a valid JSON. Because these factories handle this stage internally, they can optimize the parsing process in the most optimal way.

For example, if some when in the future JSON.parse will be considered as a bottleneck, the library can replace it with a more optimal implementation without breaking the API. Your application would not be affected by this change, because it does not know anything about the implementation details of the library.

Contract application

Specific factories require explicit Contract because they consider the response as unknown by default. So, the user-land code have to describe the contract of the response or explicitly use unkownContract to preserve the unknown type.

If parsed data does not satisfy the Contract, the data-flow stops and returns control to the user-land code through .finished.failed Event with an error-message that is returned from the Contract. Otherwise, the data-flow continues and passes control to the next step — validation.

Validation

This is optional stage. It is performed by Validator and is used to check if the response is valid. If the response is not valid, the data-flow stops and returns control to the user-land code through .finished.failed Event with an error-message that is returned from the Validator. Otherwise, the data-flow continues and passes control to the next step — data mapping.

Since Validator is a Sourced, it's possible to add some extra data from the application to the validation process. For example, it could be a user's session token:

ts
const $session = createStore<{ userId: string } | null>(null);

const userQuery = createJsonQuery({
  //...
  response: {
    validate: {
      source: $session,
      fn: (result, _params, sessionToken) => result.userId !== session.userId,
    },
  },
});

Data mapping

This is optional stage. So, you can define a mapper to transform the response to the desired format.

WARNING

Data mappers have to be pure function, so they are not allowed to throw an error. If the mapper throws an error, the data-flow stops immediately without any error handling.

Since mapper is a Sourced, it's possible to add some extra data from the application to the mapping process. For example, it could be a current language:

ts
const $language = createStore<string>('EN');

const userQuery = createJsonQuery({
  //...
  response: {
    mapData: {
      source: $language,
      fn: ({ result }, language) => ({
        ...result,
        name: result.name.translations[language],
      }),
    },
  },
});

Data-flow in basic factories

Basic factories are used to create Remote Operations with a more control of data-flow in user-land. In this case, the user-land code have to describe request-response cycle and response parsing stages. Other stages could be handled by the library, but it is not required for basic factories.

Summary

In this article, we have learned how data-flow works under the hood in Remote operations. We have also learned about two types of factories and how they differ from each other. Key points:

  • Basic factories are used to create Remote Operations with a more control of data-flow in user-land, which allows you to have a very granular control over Remote Operation behavior.
  • Specific factories are used to create Remote Operations with a more control of data-flow being in the library, which creates a better DX for you.
  • Prefer specific factories in many cases to delegate data-flow control to the library.

Released under the MIT License.