Route invalidations
TIP
Requires an understanding of the code generated api and api clients. See Code generator HTTP api for more information.
The Compas structure also allows for defining route invalidations. Route invalidations are a typed and validated way for specifying which route data are altered on a successful call of a POST, PUT, PATCH or DELETE route. For most generators, like the router
, validator
and apiClient
this is a noop. But for caching api clients, like react-query (via the reactQuery
generator), this achieves a way to invalidate the cache for all routes, which data has changed as a result of a successful mutation, with the toggle of an option.
Structure
The structure is defined via a .invalidations
function on routes. Let's take a look at some examples;
const T = new TypeCreator("app");
const R = T.router("/app");
app.add(
// Example get routes
R.get("/list", "list").response({}),
R.get("/:id", "get").params({ id: T.uuid() }).response({}),
// For operations that mutate all responses in this group, just invalidate the whole group.
R.post("/shuffle", "shuffle")
.response({})
.invalidations(R.invalidates("app")),
// For operations invalidating a specific route.
R.post("/", "create")
.body({})
.response({})
.invalidations(R.invalidates("app", "list")),
// Invalidate multiple routes,
// Both this update route and `AppGet` define a `id` param, so we can use `useSharedParams` to only invalidate the get route of this specific entity.
R.put("/:id", "update")
.params({ id: T.uuid() })
.response({})
.invalidations(
R.invalidates("app", "list"),
R.invalidates("app", "get", { useSharedParams: true }),
),
// Provide a specification to map properties.
R.post("/toggle", "toggle")
.body({
id: T.uuid(),
})
.response({})
.invalidations(
R.invalidates("app", "get", {
specification: {
params: {
id: ["body", "id"],
},
},
}),
),
);
All above examples can be mixed and matched, and the generator will guide you in the right direction if some invalidation is invalid.
useSharedParams
anduseQueryParams
are shorthand properties for populating thespecification
. They extract the shared properties of the source and target route and build up the specification. Existingspecification
properties take precedence over properties that would be defined because ofuseSharedParams
oruseSharedQuery
.- The
specification
object is a way of specifying how the targetparams
andquery
object look like. The arrays of strings define an 'object path' for which values of the current route to use.
Usage
When this definition is used with the reactQuery
generator, Compas generates something like the below snippet with based on the above defined R.put("/:id", "update")
:
export function useAppUpdate(
options: UseMutationOptions = {},
hookOptions: { invalidateQueries?: boolean } = {},
): UseMutationResult {
// ... setup
if (hookOptions?.invalidateQueries) {
const originalOnSuccess = options.onSuccess;
options.onSuccess = async (data, variables, context) => {
queryClient.invalidateQueries(["app", "list"]);
queryClient.invalidateQueries([
"app",
"get",
{ id: variables.params.id },
]);
if (typeof originalOnSuccess === "function") {
return await originalOnSuccess(data, variables, context);
}
};
}
// ... call useMutation
}
As you can see, Compas does not call the invalidations by default, hookOptions.invalidateQueries
has to be truthy for that to happen. It will also handle and call the onSuccess
option if defined. Read the react-query docs about Query invalidation for more information.