External frontends
Gadget applications can serve as a backend for a frontend hosted somewhere else. Your Gadget application's GraphQL API is accessible from anywhere, so you can build out an interface elsewhere and connect back to Gadget easily.
When to go external
Gadget has built-in support for developing and hosting frontends using Vite, a modern frontend build tool. Gadget's frontend hosting is reliable, performant, and grouped in with the deploy process for the rest of your application, so we recommend using Gadget's frontend hosting if possible for the best experience. Gadget's frontend hosting has a number of limitations currently however that may merit using a different, purpose-built frontend host.
- Currently, JS frontends built with Gadget's Vite integration are not rendered server-side and are only rendered client-side. This is generally ok for applications that live behind user-specific authentication, like Shopify apps or other SaaS tools, but not ok for marketing websites, blogs, or other web-page-like content. If you need server-side rendering, you may want to pick a hosting platform with good server-side rendering or static-site generation support.
- Gadget has great support for serverless functions and business logic via Models, Actions, and HTTP Routes, but Gadget doesn't have support for edge functions. If you need server-side code to run extremely close to your users for maximum performance, other hosting platforms have edge function support. Note: Edge functions on other platforms can still access data from a Gadget backend, and if they need to, the performance benefits of edge functions are largely negated. Gadget recommends edge functions only for stateless workloads that don't need to adjust their responses based on server-side state, or workloads which can use stale data. For applications that need an up-to-date view of backend data, Gadget recommends Gadget's hosting.
Building an external frontend
Setting up an external frontend for a Gadget application works like any other JavaScript application project -- you create a codebase, install the Gadget API client, and use it to communicate with your Gadget backend. Gadget's full-featured React hooks library @gadgetinc/react
is the easiest way to communicate from a frontend back to Gadget, so Gadget recommends using one of the React stacks to build an external frontend.
Using create-react-app
create-react-app
is a simple environment for building React applications maintained by the React team. create-react-app
works great for structuring a React frontend app that talks to a Gadget backend app over GraphQL, and is easy to deploy on platforms like Vercel or Netlify.
create-react-app
apps integrate with Gadget by installing the API Client for your app and the @gadgetinc/react
hooks library.
Setting up a create-react-app
app
If you don't already have a create-react-app
application, you can create one:
npx create-react-app example-app-frontend# oryarn create react-app example-app-frontend# thencd example-app-frontend
Next, you need to install your Gadget backend app's client package.
To install the Example App node package, register the Gadget NPM registry for the @gadget-client
package scope:
npm config set @gadget-client:registry https://registry.gadget.dev/npm
Then, install the Example App client package and the @gadgetinc/react
package using your package manager:
npm install @gadget-client/example-app @gadgetinc/react
yarn add @gadget-client/example-app @gadgetinc/react
These instructions are examples for an example app. When you create your own Gadget app, they'll update to reflect the right name and commands for your specific application.
Create your own app at gadget.new
As you make changes to Example App, Gadget will publish new versions of your API client to its NPM registry. See Client Regeneration for more details on when updates are necessary.
With your client package installed, you can instantiate it in any file, but we usually name the file src/api.js
:
import { Client } from "@gadget-client/example-app";export const api = new Client({authenticationMode: { browserSession: true },});
Once you have the required packages, you must complete the @gadgetinc/react
setup and provide the API client to the hooks library. For simplicity, we'll add the Provider
from @gadgetinc/react
to src/index.js
like so:
1// add these imports2import { Provider } from "@gadgetinc/react";3import { api } from "./api";45// existing imports6import React from "react";7import ReactDOM from "react-dom/client";8import "./index.css";9import App from "./App";1011const root = ReactDOM.createRoot(document.getElementById("root"));12root.render(13 <React.StrictMode>14 <Provider value={api.connection.currentClient}>15 <App />16 </Provider>17 </React.StrictMode>18);
With the provider in place, you can now use the @gadgetinc/react
hooks library in your app. For example, we can fetch a list of Task records from your app's GraphQL API with the useFindMany
hook in the App
component:
1import { api } from "./api";2import "./App.css";3import { useFindMany } from "@gadgetinc/react";45function App() {6 const [{ data, error, fetching }] = useFindMany(api.task);7 a;89 return (10 <div className="App">11 <header className="App-header">12 {fetching && <p>Loading...</p>}13 {error && <p>Error: {String(error)}</p>}14 {data && JSON.stringify(data)}15 </header>16 </div>17 );18}19export default App;
Your create-react-app
frontend is set up and ready for use with your Gadget backend!
Using next.js
Next.js is a popular framework for building React applications that require minimal setup to get going, and it works great for building frontends for Gadget applications. It has built-in support for server-side rendering and is straightforward to deploy on platforms like Vercel or Netlify.
next.js apps integrate with Gadget by installing the API Client for your app, and the @gadgetinc/react
hooks library.
Setting up a next.js app
If you don't already have a next.js application, you can create one with the create-next-app
utility:
plain// oryarn create next-app// orpnpm create next-app
Next, you need to install your Gadget backend app's client package.
To install the Example App node package, register the Gadget NPM registry for the @gadget-client
package scope:
npm config set @gadget-client:registry https://registry.gadget.dev/npm
Then, install the Example App client package and the @gadgetinc/react
package using your package manager:
npm install @gadget-client/example-app @gadgetinc/react
yarn add @gadget-client/example-app @gadgetinc/react
These instructions are examples for an example app. When you create your own Gadget app, they'll update to reflect the right name and commands for your specific application.
Create your own app at gadget.new
As you make changes to Example App, Gadget will publish new versions of your API client to its NPM registry. See Client Regeneration for more details on when updates are necessary.
With your client package installed, you can instantiate it in any file, but usually, we name the file api.js
:
1import { Client } from "@gadget-client/example-app";23export const api = new Client({4 // for client side data access, we don't pass anything and the client will default to using the browser session authentication mode5 // for server side data access, pass an API key by uncommenting the line below6 // authenticationMode: { apiKey: "gsk-some-api-key-here" }7});
Once you have the required packages, you must complete the @gadgetinc/react
set-up to provide the API client to the hooks library. React providers in next.js are usually added in the _app.js
file like so:
1import { Provider } from "@gadgetinc/react";2import { api } from "../api";34function MyNextApp({ Component, pageProps }: AppProps) {5 return (6 <Provider value={api.connection.currentClient}>7 <Component {...pageProps} />8 </Provider>9 );10}1112export default MyNextApp;
With the provider in place, you can now use the @gadgetinc/react
hooks library in your next.js pages. For example, we can fetch a list of Task records from your app's GraphQL API with the useFindMany
hook:
1import { useFindMany } from "@gadgetinc/react";2import { TaskCard } from "../components/TaskCard";34const Home: NextPage = () => {5 const [{ data, fetching, error }] = useFindMany(api.task, {6 sort: { createdAt: "Descending" },7 first: 30,8 });910 return (11 <div>12 <h1>Posts</h1>13 {fetching && <div class="spinner" />}14 {error && <div status="error">{error.message}</div>}15 {data && data.map((task) => <TaskCard key={task.id} task={task} />)}16 </div>17 );18};1920export default Home;
You can find more examples of next.js frontends built using Gadget in the Gadget examples repo.
Server-side vs client-side API access
Next.js supports two main ways of accessing data:
- requests made by the node.js process running server-side in a
getStaticProps
orgetServerSideProps
function - requests made by the browser running client-side in a React hook, like
use-fetch
,react-query
,urql
or@gadgetinc/react
.
Your app's GraphQL API supports both of these methods of data access.
Gadget generally recommends client-side data access for your next.js applications. Client-side data access is fastest for the user, as their browser isn't making extra requests to a server-side process that then makes requests to a Gadget API. Client-side access also allows the API to customize responses for the user to limit data access permissions and to power things like logged in/logged out state. However, server-side data access allows further data processing and is sometimes necessary to power things like next.js' [getStaticPaths
] for static site generation.
Client-side data access
For accessing data on the client, Gadget recommends using the @gadgetinc/react
hooks library:
import { Client } from "@gadget-client/example-app";// instantiate a client with the auth powered by each user's browser (the default)export const api = new Client();
Then, after wrapping our app in the <Provider/>
from @gadgetinc/react
, we can use React hooks to access data:
1import { api } from "../api";2import { useFindMany } from "@gadgetinc/react";3import { TaskCard } from "../components/TaskCard";45const Home: NextPage = () => {6 // use a React hook for data access within the component7 const [{ data, fetching, error }] = useFindMany(api.task);8 // ...9 return (10 <div>11 {data.map((task) => (12 <TaskCard key={task.id} task={task} />13 ))}14 </div>15 );16};
Server-side data access
For accessing data on the server, Gadget recommends using the standard, imperative style API client object without hooks from @gadgetinc/react
. It's easiest to instantiate your Gadget API client with an API key for authentication and then use it when generating server-side props.
1import { Client } from "@gadget-client/@gadget-client/example-app";23// instantiate a client with an API key that will only be used server-side4export const api = new Client({5 authenticationMode: { apiKey: "gsk-some-api-key-here" },6});
Then in our next.js pages, we can make API calls using the api.model.findMany
style finders built into the api
client object, and send the results as props to the client-side component:
1import { api } from "../api";2import { TaskCard } from "../components/TaskCard";34const Home: NextPage = (props) => {5 // use props passed from the getServerSideProps function6 return (7 <div>8 {props.tasks.map((task) => (9 <TaskCard key={task.id} task={task} />10 ))}11 </div>12 );13};1415export const getServerSideProps = async () => {16 return {17 props: {18 tasks: await api.task.findMany({}),19 },20 };21};
Using a Gadget API key to initialize your Gadget client on the server gives you full read or write access to the database. If working on a Shopify app, shop tenancy is not enforced.
This key should never be exposed in a browser, and users should never be able to see it. This should only be used when connecting to another secure server.
Hosting frontends
Frontends built using React or other client-side frameworks need to be deployed to a hosting provider. Gadget recommends using Vercel or Netlify for hosting your frontends, as both make it easy to deploy and work great with existing Gadget tooling. You can find more information on how to deploy a frontend application by viewing our external frontend deployment guide.
You must register the Gadget npm
registry in all environments where you want to use this package, which would include production
systems, continuous integration environments, or build steps like Vercel or Netlify's static site builders. This can be done using the
above npm config
command, or by writing out an .npmrc
file in those environments that points to Gadget for the @gadget-client
scope.
To indicate to hosting platforms where to find the @gadget-client/example-app
package, create an .npmrc
file in the web/frontend
directory with the following content:
registry=https://registry.npmjs.org/@gadget-client:registry=https://registry.gadget.dev/npm
Shopify embedded apps in external frontends
If you're building a Shopify application, Shopify recommends you build it in React using their Polaris component library and the App Bridge for communicating with the Shopify platform. Gadget has an easy-to-use library for building React frontends for Shopify with Polaris. Read more in the Shopify App Frontend guide.
Gadget also has an example Shopify Embedded App built with next.js you can use as a start in the examples repo.
Installation
To build a Shopify App frontend with your Gadget backend, you must first set up a React application. Gadget recommends using Gadget's built-in hosting for Shopify frontends if possible, but if not, create-react-app
, next.js
, or the Shopify CLI's Vite template frontend app work well.
After creating a React app, you must install your Gadget application's JS client package.
To install the Example App node package, register the Gadget NPM registry for the @gadget-client
package scope:
npm config set @gadget-client:registry https://registry.gadget.dev/npm
Then, install the Example App client package and the @gadgetinc/react
package using your package manager:
npm install @gadget-client/example-app @gadgetinc/react
yarn add @gadget-client/example-app @gadgetinc/react
These instructions are examples for an example app. When you create your own Gadget app, they'll update to reflect the right name and commands for your specific application.
Create your own app at gadget.new
As you make changes to Example App, Gadget will publish new versions of your API client to its NPM registry. See Client Regeneration for more details on when updates are necessary.
Next, you must install Shopify's App Bridge package (@shopify/app-bridge
), and Gadget's Shopify bindings package (@gadgetinc/react-shopify-app-bridge
).
npm install --save @gadgetinc/react-shopify-app-bridge @gadgetinc/react @shopify/app-bridge-react
yarn add @gadgetinc/react-shopify-app-bridge @gadgetinc/react @shopify/app-bridge-react
If you are using the Shopify CLI 3.0 to create your app, you need to install your Gadget dependencies in the /web/frontend
directory!
These two packages manage working with the Shopify App Bridge, and provide React hooks for accessing data from your Gadget models.
Next, you need to set up your Gadget api
client object and the wrapping Provider
React context from @gadgetinc/react-shopify-app-bridge
. You can instantiate the client without any options. The Provider
will set up authentication with Shopify using Shopify Session Tokens when you pass the api
instance to it later.
src/api.jsJavaScriptimport { Client } from "@gadget-client/my-gadget-app";export const api = new Client();
Now we need to import and set up the Provider
in the App component:
src/app.jsxJavaScript1import {2 AppType,3 Provider as GadgetProvider,4} from "@gadgetinc/react-shopify-app-bridge";5import { api } from "./api";6import { ProductManager } from "./productManager";7import React from "react";89export function MyApp() {10 return (11 <GadgetProvider12 type={AppType.Embedded}13 shopifyApiKey="---your Shopify app's API Key (Client ID) from the Partners dashboard---"14 api={api}15 >16 <ProductManager />17 </GadgetProvider>18 );19}
Once the Provider
is wrapping your application, you can start to build out components in your embedded application.
Shopify App Setup
When building embedded apps for Shopify, the App URL field in the Shopify App setup (found in your Partners Dashboard) should be set to the URL where your frontend application is hosted. For example, if you are developing locally this would likely be set to https://localhost
. Additionally, the App URL in your Gadget app's Shopify Connection should be set to the same URL.
Note: Shopify expects a Content-Security-Policy
header to be set from your application for it to be embedded in the
Shopify Admin. The header value should be set to frame-ancestors https://example-shop.myshopify.com/ https://admin.shopify.com;
Other frameworks
Gadget currently doesn't have specific adapter libraries built for client-side frameworks other than React, like Vue, Svelte, or Solid. However, your Gadget application has a rich GraphQL API ready to read and write data, which means you can use any existing GraphQL client for these frameworks to interface with your Gadget app right out of the box.
urql
for Vue and Svelte
Gadget recommends urql
as a high-quality GraphQL client with built-in support for Vue and Svelte. Your generated JS API Client package uses urql
under the hood, so it's easy to re-use the existing API authentication and connectivity code from the Gadget API client within a Vue or Svelte app.
For example, we can wire up a generated api
Client instance for use within a Vue app by accessing the api.connection.currentClient
urql.Client
instance for use in Vue:
1// import the urql vue bindings2import { provideClient } from "@urql/vue";3// import and instantiate your Gadget API client, which uses urql under the hood4import { Client } from "@gadget-client/example-app";56// create an instance of the Gadget API Client7// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info8export const api = new Client();910// provide the pre-configured urql client instance from the Gadget API Client to the vue bindings11provideClient(api.connection.currentClient);
Read more about @urql/vue
in urql
's docs.
For Svelte support, you can also use the generated Gadget API Client to set up the @urql/svelte
bindings:
1// import the urql svelte bindings2import { setContextClient } from "@urql/svelte";3// import and instantiate your Gadget API client, which uses urql under the hood4import { Client } from "@gadget-client/example-app";56// create an instance of the Gadget API Client7// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info8export const api = new Client();910// provide the pre-configured urql client instance from the Gadget API Client to the svelte context bindings11setContextClient(api.connection.currentClient);
Read more about @urql/svelte
in urql
's docs.
Other libraries
Because your Gadget app's GraphQL API is spec compliant, any GraphQL client can be used to make requests to it. You can use libraries like graphql-request
in JS, apollo-client
for JS, Swift, and Kotlin, or even any HTTP request library that can send and receive JSON.
For example, we can make an API call to Gadget using the cURL
command line utility:
curl -X POST \-H "Content-Type: application/json" \-d '{ "query": "query { gadgetMeta { name } }" }' \https://example-app.gadget.app/api/graphql
Related topics
- External frontend deployment guide
- Built-in Frontend guide