You can use variables in both database and HTTP queries.
For example, here's a database query that contains an id
variable in its statement:
And here's an HTTP query that contains an id
variable in its path:
You specify variables by wrapping the variable in {{}}
.
Using variables in useDecode
To call this endpoint from the front-end:
function ShowUser({ id }) {
let { data: user } = useDecode(["getUser", { id }]);
// ...
}
You pass in an array as the first argument. The first element is the slug, the second element is params for the request.
If you've used useSWR in the past, you might think you need to memoize { id }
. Not with useDecode
- the library memoizes for you under the hood.
Dependent variables
Sometimes, one request might depend on the results of another, like this:
function ShowUser({ id }) {
let { data: user } = useDecode(["getUser", { id }]);
// Error: will fail initially as `user` is not available
let { data: companyOrders } = useDecode([
"getCompanyOrders",
{ id: user.companyId },
]);
// ...
}
Just pass a function to useDecode
that returns the array instead. useDecode
will invoke the function to derive the query parameters. If the function throws an error or returns null
, Decode will assume it's because the query depends on another fetch. It will try again after "getUser" succeeds:
function ShowUser({ id }) {
let { data: user } = useDecode(["getUser", { id }]);
let { data: companyOrders } = useDecode(() => [
"getCompanyOrders",
{ id: user.companyId },
]);
// ...
}
Let's say you have an HTTP request that looks like this:
Path and query param variables are passed in to useDecode
as top-level properties in the params object, like this:
function ShowOrgUsers({ orgId }) {
let { data: users } = useDecode(["listUsers", { id: orgId, sortBy: "asc" }]);
// ...
}
Often for write requests (i.e. POST/PUT/PATCH), you want to send along a body. Say you have an HTTP request that look like this:
Here's how you'd invoke the request:
function EditUser({ id }) {
let fetcher = useFetcher();
let handleSubmit = ({ name, email }) => {
fetcher("patchUser", { id, body: { name, email } });
};
}
Decode will match id
up with the required variable in the path. Everything under body
will be forwarded along to the upstream API, like this:
PATCH https://api.acme.org/1234
{
name: "Gabe Goldman",
email: "gabe@example.com"
}
useDecode
Underneath the hood, useDecode
wraps the SWR library. You can find the docs for SWR here:
📔https://github.com/vercel/swr
So, if you know how to do something with SWR, you can probably do it with useDecode
!
The key difference: You pass in an endpoint and a fetcher
function to SWR, like so:
let { data, error } = useSWR("/api/users", fetcher);
Whereas all you need to pass to useDecode
is a slug, like so:
let { data, error } = useDecode("getUser");
In addition, you can optionally pass in a "transform" function, like this:
import camelcaseKeys from "camelcase-keys";
// ...
let { data, error } = useDecode("getUser", camelcaseKeys);
The transform function runs after the results are received from Decode but before they are cached by SWR. So, the data is in its "post transformation" state throughout your React app.
Put another way, this is the pipeline:
fetched from decode
|> transformed by transform function
|> cached and returned
useDecode
If you do use any of the strategies below, we'd definitely love to hear why.
If you'd prefer to make requests without using useDecode
, you have a few options:
useFetcher
+ SWRThis can be helpful if for whatever reason you want to use SWR more directly:
import { useFetcher } from "@decode/client";
import useSWR from "swr";
// ...
let fetcher = useFetcher();
let { data } = useSWR("listUsers", fetcher);
useFetcher
on its ownRemember, you can use the fetcher to just make requests at any time:
import { useFetcher } from "@decode/client";
// ...
let fetcher = useFetcher();
useEffect(() => {
let doEffect = async () => {
let data = await fetcher("listUsers");
};
doEffect();
});
useToken
to use plain old fetchLast, you can use useToken
to grab an API token you can use to make a plain old fetch request:
import { useToken } from "@decode/client";
// ...
let token = useToken();
useEffect(() => {
let doEffect = async () => {
let res = await fetch("https://api.usedecode.com/e/listJobs", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
});
if (!res.ok) {
setError("Uh oh, something went wrong when fetching that request.");
}
let json = await res.json();
let data = res.result;
};
doEffect();
});
A Decode error response looks like this:
{
"ok": false,
"who": "upstream",
"error": {
"summary": "Query to database hit timeout limit (10s)"
}
}
ok
- whether or not the request succeededwho
- who is "responsible" for the error. Possible values are you
, decode
, and upstream
(the API or database you are calling).error.summary
- Decode tries its best to unpack and describe the errorHere are some of the error status codes you might receive:
**422**
This means the gateway was "unreachable." This is most commonly experienced when Decode can't connect to a database.
502
This means the upstream resource returned an error.
If you're calling an API:
summary
will include the HTTP status code returned by the APIerror.upstream_error_body
, which contains the contents of the error body received from the API400
You made a bad request. Most commonly, you are not including required variables in your request.
404
The slug you are attempting to invoke is not registered on Decode.