Meet this apt ingredient!
The Xata engineering crew values predictability in any respect levels of our codebase. This blog submit is ready how we tackle API communications in a predictable map.
Motivation
To us at Xata engineering, we sustain in mind spending time reverse engineering APIs lawful to understand what roughly records is on hand to be neither stress-free nor treasured. As an alternative, via OpenAPI we procure some insights into the potentialities uncovered by some APIs. Then as soon as more, why quit there? Studying an OpenAPI spec and manually writing code is… shall we embrace, also no longer our thing.
Obviously, some libraries exist to generate kinds from OpenAPI, however then we would restful must manually join them to react-ask or identical fetchers, all whereas the generated kinds don’t appear to be very thorough in most conditions. Even worse, in case of spherical dependencies in a given OpenAPI spec, this turns into much less of an risk for us.
Given these constraints, we saw likely for innovation. We needed to produce one thing! And because of this we began to craft openapi-codegen.
The dreams were sure:
- Generate human-readable kinds, so we are in a position to overview TypesScript files in desire to a mountainous OpenAPI YAML file.
- Embed all that that that you may take into accounts documentation, so we are in a position to private entry to the documentation straight via IntelliSense.
- Generate ready-to-recount React Hooks, with
url
andkinds
baked into the factor to carry far off from any human errors. - Be as flexible as that that that you may take into accounts: so far we are in a position to generate fetchers & react-ask hooks, however let’s no longer restrict the potentialities.
Let’s Play
To cherish about a of the superpowers this generator affords you, let’s develop a React application that consumes the Github API.
Initialize the Mission
For simplicity, let’s recount vitejs
to bootstrap our mission!
$ npm develop vite@most modern
We are going to settle the framework react
with the variant react-ts
, this ought to restful give us a brief working ambiance for our application.
Now, let’s delivery the magic!
$ npm i -D @openapi-codegen/{cli,typescript}
$ npx openapi-codegen init
- Opt
Url
- Paste the giphy OpenAPI url: https://api.apis.guru/v2/specs/github.com/1.1.4/openapi.yaml
- Enter
github
as namespace - Opt
react-ask substances
- Enter
src/github
as folder
Develop npx openapi-codegen gen github
and voilà!
Search for What We Bear
Let’s settle a shrimp little bit of time to ogle what we private got in ./src/github
. The extra involving file is GithubComponents.ts
, this is the set aside all our react-ask substances and fetchers are generated and likewise our entry point. We private got some generated kinds in Github{Parameters,Schemas,Responses}
. These are a 1-1 mapping with what we private got within the OpenAPI spec.
To supply, we private got two particular files: GithubFetcher.ts
and GithubContext.ts
. These files are generated easiest as soon as and could simply also be modified/prolonged to pass smartly with your needs.
In GithubFetcher.ts
that that you may:
- inject the
baseUrl
(a default is equipped) - take care of authentication
- tweak how ask strings are handled, especially in case that that you may need arrays
and in GithubContext.ts
that that you may:
- tweak how react-ask cache keys are handled (
queryKeyFn
) - inject runtime values (hooks) to the fetcher (
fetcherOptions
) - disable ask fetching (
queryOptions
)
Setup react-ask
Obviously, so far, we don’t even private react-ask installed, let’s produce this and setup our application.
$ npm i react-ask
Add the queryClient
// App.tsx
import { QueryClient, QueryClientProvider } from "react-ask";
const queryClient=new QueryClient();
feature App() {
return (
);
}
feature Customers() {
return todo;
}
export default App;
We are in a position to bustle yarn dev
and gaze a white page with “todo”.
Open up Fetching!
Let’s try to procure some users!
import { QueryClient, QueryClientProvider } from "react-ask";
import { useSearchUsers } from "./github/githubComponents";
const queryClient=new QueryClient();
feature App() {
return (
);
}
feature Customers() {
const { records, error, isLoading }=useSearchUsers({
queryParams: { q: "fabien" },
});
if (error) {
return (
{error.message}
Documentation
);
}
if (isLoading) {
return Loading…;
}
return (
{records?.objects.map((merchandise)=> (
- {merchandise.login}
))}
);
}
export default App;
And voilà! With out colorful the API, lawful taking half in with the autocompletion, I’m in a position to procure a listing a users! 🥳 The kinds even give us a hint that error.documentation_url
turn out to be a thing, rather cool, however let’s gaze if this is de facto working!
Error Management
Let’s refresh the page typically, unless we reach the API price restrict 😅 In the raze, we don’t gaze the error message, nor the documentation link.
Here is the set aside GithubFetcher.ts
has to be tweaked! Errors are certainly a shrimp bit trickier, as an error can even be factual object again from the API or the relaxation (text response in desire to JSON, or one thing much less predictable if the network is down), so we private got to develop determined we bought a identified structure in our application.
// GithubFetcher.ts
@@ -1,4 +1,5 @@
+import { BasicError } from "./githubSchemas";
@@ -43,8 +43,18 @@ export async feature githubFetch
Here we need to make sure to validate the error format, since everything is optional in BasicError
GitHub API and BasicError
have a message: string
property, I can safely throw an Error
. And just like this, we have nice and type-safe error handling.
Adding Some States
Of course, React is all about state isn’t it? So… let’s try!
function Users() {
const [query, setQuery]=useState("");
const { data, error, isLoading }=useSearchUsers(
{
queryParams: { q: query },
},
{
enabled: Boolean(query),
}
);
if (error) {
return (
{error.message}
Documentation
);
}
return (
setQuery(e.target.value)} />
{isLoading ? (
Loading…
) : (
{records?.objects.map((merchandise)=> (
- {merchandise.login}
))}
)}
);
}
Now I’m in a position to peep for users, and likewise reach the API price restrict map faster! 😅 I verbalize this would possibly be a lawful time to introduce authentication handling in our shrimp application.
Authentication
To withhold it easy for our demo functions, let’s settle a GitHub developer token and store it in localStorage. Here is unsafe and likewise you most certainly mustn’t produce this in production. If the token is no longer space, ask for it, if it is there, present the records and provide a disconnect button. We can must entry this token
at runtime later, with this in mind, let’s develop a Auth.tsx
file, that isolates our authentication common sense.
// Auth.tsx
import React, { createContext, useContext, useState } from "react";
import { useQueryClient } from "react-ask";
const authContext=createContext({
token: null,
});
export feature AuthProvider({ younger of us }: { younger of us: React.ReactNode }) {
const key="githubToken";
const [token, setToken]=useState(localStorage.getItem(key));
const [draftToken, setDraftToken]=useState("");
const queryClient=useQueryClient();
return (
{token ? (
{younger of us}
>
) : (
)}
);
}
export const useToken=()=> {
const { token }=useContext(authContext);
return token;
};
Now, we are in a position to wrap our App with the AuthProvider, so the token
is on hand on your complete application:
// App.tsx
feature App() {
return (
);
}
and inject the token
in GithubContext.ts
--- a/src/github/githubContext.ts
+++ b/src/github/githubContext.ts
@@ -1,4 +1,6 @@
import kind { QueryKey, UseQueryOptions } from "react-ask";
+import { useToken } from "../useAuth";
import { QueryOperation } from "./githubComponents";
export kind GithubContext={
@@ -6,7 +8,9 @@ export kind GithubContext={
/Headers to inject within the fetcher
*/
- headers?: {};
+ headers?: {
+ authorization?: string;
+ };
/Ask params to inject within the fetcher
*/
@@ -36,14 +40,22 @@ export feature useGithubContext(
- _queryOptions?: Leave out,
"queryKey" | "queryFn"
>
): GithubContext {
+ const token=useToken();
+
return {
- fetcherOptions: {},
- queryOptions: {},
+ fetcherOptions: {
+ headers: {
+ authorization: token ? `Bearer ${token}` : undefined,
+ },
+ },
+ queryOptions: {
+ enabled: token !==null && (queryOptions?.enabled ?? factual),
+ },
queryKeyFn: (operation)=> {
const queryKey: unknown[]=hasPathParams(operation)
? operation.course
And that’s it! Now we private got all the pieces in spot to develop an unheard of application spherical github API.
How is that this working? Let’s private a glance of the generated useSearchUsers
to realise.
export const useSearchUsers=(
variables: SearchUsersVariables,
alternatives?: Leave out,
"queryKey" | "queryFn"
>
)=> {
// 1. we retrieve our personalized auth common sense
const { fetcherOptions, queryOptions, queryKeyFn }=
useGithubContext(alternatives);
return reactQuery.useQuery(
queryKeyFn({
course: "/search/users",
operationId: "searchUsers",
variables,
}),
// 2. the personalised `headers.authorization`, half of `fetcherOptions` is passed to the fetcher
()=> fetchSearchUsers({ ...fetcherOptions, ...variables }),
{
...alternatives,
...queryOptions,
}
);
};
What About Ask Caching?
One in all the important thing facets of react-ask, is to private a ask cache. By default, openapi-codegen will give you a key cache that suits the URL scheme.
To illustrate:
useSearchUsers( { queryParams: { q: "fabien" } }
will invent the next cache key: ["search", "users", { q: "fabien" }]
.
So if I desire to invalidate the users list, I’m in a position to call:
import { useQueryClient } from "react-ask";
const MyComp=()=> {
const queryClient=useQueryClient();
const onAddUser=()=> {
queryClient.invalidateQueries(["search", "users"]);
};
/... */
};
Indicate: Here is lawful a default habits, in case that that you may need extra explicit needs, that that you may consistently tweak GithubContext#queryKeyFn
.
Reduction & Feedback
This mission is restful somewhat early stage (however it strictly follows semver, so if we shatter the API, that that you may realize it!). We’re consistently delighted to private feedback and let you in case that that you may need any questions. Whenever you happen to would maintain to browse the code or settle it for a wander, develop particular to look at out the repo on GitHub.
Overjoyed coding of us!
>
>
+
TQueryFnData,>
>>>{>
code>
Read More
Fragment this on knowasiak.com to talk to of us on this topicRegister on Knowasiak.com now in case that that you may properly be no longer registered but.