Using Gadget with React
Gadget has React bindings that use the API client generated for simple-blog-example. These bindings are built on top of the React bindings provided by urql
, but with the same type safety guarantees as the generated client.
Installation
To use your Gadget client with React, you must install both React and the React bindings.
npm install @gadgetinc/react react
yarn add @gadgetinc/react react
Client setup
Authentication
The urql
client that the simple-blog-example JS client provides is pre-configured to connect to the right Gadget platform GraphQL endpoint and uses the same authentication mechanism that the outer Gadget client uses. If you configure the Gadget client to use a browser session, the urql
client will use the same browser session. If you configure you Gadget client to use API key authentication, the urql
client will use that same API key.
React is a client side framework and your code is likely to be visible to all the users of your application. For this reason, Gadget recommends using Session Cookie based authentication for accessing the Gadget API. You shouldn't use an API key that has write permissions for authenticating as that API key will be available to any client and ripe for abuse. You can instead use session-based authentication where users must log in to get privileges, or you can leave the client in the unauthenticated mode and grant permissions to read some backend data to the Unauthenticated
role.
See the Authentication documentation for more details on safely giving users access to your application.
Creating the client for React apps
Once your simple-blog-example package has been installed, create an instance of the Client
class in a shared file somewhere:
This client will be shared by all your React components, so it's best to put this code outside any particular component, or within one of your root components where it can be shared. Be sure to select the appropriate authentication mode for your use case.
import { Client } from "@gadget-client/simple-blog-example";export const api = new Client({authenticationMode: { browserSession: true },});
If you are using Next.js, or other server-side rendering environments, using browserSession authentication mode will not work, as server side environments don't support localStorage
.
Instead, instantiate the client without specifying an authentication mode.
export const api = new Client();
This will default to using the anonymous
authentication mode on the server,
and the browserSession
authentication mode on the client.
Providing the Client
Once your client is installed and set up, you must provide an urql
client to your React components using the Gadget React provider. Your Client
instance exposes the urql
client object at .connection.currentClient
.
1import { Provider } from "@gadgetinc/react";2import { api } from "shared/api";3const App = () => (4 <Provider api={api}>5 <YourRoutes />6 </Provider>7);
Querying data
You can now use @gadgetinc/react
's hooks for fetching models. Gadget provides various hooks for different kinds of queries: useFindOne
, useMaybeFindOne
, useFindMany
, useFindFirst
, useMaybeFindFirst
, useFindBy
, and useGet
. These hooks mirror the underlying query methods found on the generated client for simple-blog-example.
useFindOne
, useMaybeFindOne
, useFindMany
, useFindFirst
, useMaybeFindFirst
, and useGet
take a manager from the API client as the first argument, whereas useFindBy
takes a specific findByXYZ
method on a manager. All of these methods also take an optional options argument. This is a combination of the options for the relevant query, such as select
to choose specific fields, or filter
s and search
for useFindMany
, useFindFirst
, and useMaybeFindFirst
. This options object can also take any of the urql
[useQuery
] options (except query
and variables
).
The result of calling these hooks is a two element array, similar to the result of urql
's useQuery
hook. The first element is the result, composed of an error
, fetching
status, and the data
itself. The second element is a function that you can call to run the query again. By default, urql
will use its cache and reach out to the network only when it has been invalidated.
1import { useFindOne } from "@gadgetinc/react";2import { api } from "shared/client";34const BlogPosts = () => {5 const [result, refresh] = useFindMany(api.posts, {6 select: { id: true, title: true },7 });89 const { data, fetching, error } = result;10 if (error) return <p>Error: {error.message}</p>;11 if (fetching && !data) return <p>Fetching posts...</p>;12 if (!data) return <p>Could not find any posts</p>;1314 return (15 <ul>16 {data.posts.map((post) => (17 <li key={post.id}>{post.title}</li>18 ))}19 </ul>20 );21};
Running actions
The Gadget React bindings provides three options for calling actions: useAction
, useBulkAction
, and useGlobalAction
.
Similar to querying data, these hooks return a two element array. The first element contains all the same result information as queries, but the second element is a function to call the action. This function takes a variables object, along with an optional options argument.
1import { useAction } from "@gadgetinc/react";2import { useRef } from "react";3import { api } from "shared/client";45const CreateComment = () => {6 const commentInputRef = useRef(null);7 const [result, createComment] = useAction(api.comments.create, {8 select: { id: true },9 });1011 const { fetching, result, error } = result;12 return (13 <form>14 {error && <p>Error: {error.message}</p>}15 {fetching && <p>Creating comment...</p>}16 <div>17 <label for="commentBody">Comment body:</label>18 <input ref={commentInputRef} type="text" id="commentBody" />19 </div>20 <button21 onClick={async (event) => {22 event.preventDefault();23 await createComment({ comment: { body: commentInputRef.current.value } });24 commentInputRef.current.value = "";25 }}26 >27 Create comment28 </button>29 </form>30 );31};