gadget-server 

The gadget-server package is an autogenerated package of utilities for implementing your Gadget app's backend.

Exports from gadget-server are only available in the backend of your application, and not on the frontend. For reading and writing data from your frontend, see the @gadgetinc/react package.

logger 

An instance of the structured logger for your Gadget app.

JavaScript
1import { logger } from "gadget-server";
2
3// log at the info level
4logger.info("hello world");
5
6// log an error
7logger.error({ error: new Error("Something went wrong") });

Logs are viewable in the Log Viewer within Gadget.

api 

An instance of your Gadget app's API client.

JavaScript
import { api } from "gadget-server";
await api.widget.create({ title: "new widget " });

This api client has the system-admin role and can perform any action in your Gadget app.

See the API Reference for more information on your app's API and the api client object.

trigger 

An instance of the triggers associated with your Gadget app's action. When an action is executed, it receives a trigger object containing information about the event that initiated the action. This object varies depending on the trigger type and includes a type property that specifies the kind of trigger.

api/models/someModel/actions/someAction.js
JavaScript
export async function run({ trigger }) {
// This logs an object providing details about the trigger
console.log(trigger); // Output example: { "type": "api", "rootModel": "someModel", "rootAction": "someAction", ... }
}

For more information on triggers check out our guide here.

connections 

An instance of your Gadget app's connections, which facilitate interactions with external systems. This object contains pre-configured clients for supported Gadget connections, such as Shopify, OpenAI, and Sentry.

For example, if you wanted to access your OpenAI connection in a Fastify boot plugin, you could do so like this:

api/boot/setup.js
JavaScript
1import { connections } from "gadget-server";
2
3export default async function (server) {
4 const openAIKey = connections.openai.apiKey;
5 // Do things with the key
6}

For more information on the available connections for Gadget app check out the guide here.

Model Actions 

save(record) 

Saves the given record to the database.

models/widget/actions/update.js
JavaScript
import { save } from "gadget-server";
export async function run({ record }) {
await save(record);
}

If the record is a new record, this will persist the record and assign it an ID. If the record is an existing record, this will update the database with any changed values from the record.

save will validate the record before persisting it. save persists the record to the database using your app's Internal API.

Returns 

Async function, which returns a Promise. Resolves to void. Any encountered validation errors will be thrown.

deleteRecord(record) 

Deletes the given record from the database.

models/widget/actions/delete.js
JavaScript
import { deleteRecord } from "gadget-server";
export async function run({ record }) {
await deleteRecord(record);
}

delete removes the record from your database using your app's Internal API.

Returns 

Async function, which returns a Promise. Resolves to void.

applyParams(params, record) 

Sets incoming parameters onto a record object.

models/widget/actions/update.js
JavaScript
import { applyParams } from "gadget-server";
export async function run({ record }) {
applyParams(record);
await save(record);
}

applyParams will set any incoming parameters onto the record. This is useful for updating a record with the parameters from an action or similar call. applyParams does not persist the record -- it just mutates the in-memory record object.

Parameters 
  • params - The data passed to an action from API calls, webhook events, or direct user inputs, which include the fields of a record.
  • record - Record to apply parameters to.
Returns 

Returns void.

Background actions 

enqueue(action, input, options) 

Enqueues a model or global action to run in the background.

JavaScript
1export async function run({ record, api }) {
2 await api.enqueue(
3 api.someModelOrGlobalAction,
4 {},
5 { id: "background-action-1", priority: "HIGH" }
6 );
7}
Parameters 
  • action - Any model or global action.
  • input - Parameters or data to pass to an action.
  • options (optional) - Options for governing how a background action is enqueued:
    • id - A unique identifier to use for the background action. Must be unique among all other background actions within its environment. If not set, a unique ID will be autogenerated and returned.
    • priority - How high to place this background action in its queue, HIGH priority actions will be executed before default priority actions, and those before LOW priority actions. If not set, the default priority will be used.
    • queue - A queue to put this background action in that limits the maximum concurrency of all actions in the queue. If not set, the action will go into the global queue, and won't be concurrency limited. For more info on queue options read the reference here.
    • retries - Configure how many times to retry the action if it fails, and how fast. For more info on the available retry options, see the retries reference.
    • startAt - The time to start running a background action. The time defined must be formatted as an ISO string.
Returns 

Returns void.

queue 

queue allows you to place the background action in a dedicated queue and also enables you to control the maximum concurrency of all actions in the queue.

JavaScript
1export async function run({ record, api }) {
2 await api.enqueue(
3 api.someModelOrGlobalAction,
4 {},
5 {
6 queue: {
7 name: "my-queue",
8 maxConcurrency: 4,
9 },
10 }
11 );
12}
  • queue: The name of the queue.
  • maxConcurrency: The maximum concurrency of all actions in the queue. If not set, the default will be set to 1

retries 

retries allows you to configure how fast and how many times to retry the background action if it fails.

retries can be set to an integer to make your background action retry that many times with the default retry schedule, or set to an object to configure exactly how many times and at what rate retries occur.

JavaScript
1export async function run({ record, api }) {
2 await api.enqueue(
3 api.someModelOrGlobalAction,
4 {},
5 {
6 retries: {
7 retryCount: 5,
8 maxInterval: 60000,
9 backoffFactor: 2,
10 initialInterval: 1000,
11 randomizeInterval: true,
12 },
13 // OR simply retries: 5
14 }
15 );
16}
  • retryCount: The maximum number of times to retry the operation if it keeps failing. The default is 6.
  • maxInterval: The maximum amount of time to delay a retry while exponentially backing off. The default is not set, so the retry can backoff indefinitely.
  • backoffFactor: The exponential backoff factor to use for calculating the retry delay for successive retries. Set this higher to delay longer. The default is 2.
  • initialInterval: How long to initially delay the first retry, in milliseconds. The default is 1000 ms.
  • randomizeInterval: Randomizes the delays between attempts by multiplying with a factor between 1 to 2. The default is false.

Shopify 

preventCrossShopDataAccess(params, record, options) 

Enforce that the given record is only accessible by the current shop. For multi-tenant Shopify applications, this is key for enforcing data can only be accessed by the shop that owns it.

models/shopifyProduct/actions/update.js
JavaScript
1import { applyParams, preventCrossShopDataAccess, save } from "gadget-server";
2
3export async function run({ params, record, logger, api }) {
4 applyParams(params, record);
5 await preventCrossShopDataAccess(params, record);
6 await save(record);
7}
  • For existing records, this function verifies the record object has the same shopId as the shop in the current session, and throws if not.
  • For new records, this function sets the record's shopId to the current session's shopId.
Parameters 
  • params - Incoming parameters, validated against the current shopId
  • record - Record to validate or set the shopId on
  • options:
    • shopBelongsToField: Picks which belongs to relationship on a model is used for cross-shop validation. Must be the API identifier of a belongs to relationship to the shopifyShip model. If there are multiple relationships to the shopifyShop model on the passed-in record, this is a required parameter
    • customerBelongsToField: Picks which belongs to relationship on a model is used for cross-customer validation. Must be the API identifier of a belongs to relationship to the shopifyCustomer model. If there are multiple relationships to the shopifyCustomer model on the passed-in record, this is a required parameter
    • enforceCustomerTenancy: A boolean field that disables cross-customer validation. Defaults to true
Returns 

Returns void. Throws if the record is not accessible by the current shop.

finishBulkOperation(record) 

Updates the state of a bulkOperation record from Shopify when the operation completes.

models/shopifyBulkOperation/actions/complete.js
JavaScript
1import {
2 applyParams,
3 preventCrossShopDataAccess,
4 finishBulkOperation,
5 save,
6} from "gadget-server";
7
8export async function run({ params, record, logger, api }) {
9 applyParams(params, record);
10 await preventCrossShopDataAccess(params, record);
11 await finishBulkOperation(record);
12 await save(record);
13}
Parameters 
  • record - The bulkOperation record to update

globalShopifySync(params) 

Start running a global Shopify sync for all shops matching the params. This will create a shopifySync record and trigger a sync for each shop.

Parameters 
  • params: The params object for the sync -apiKeys: the list of Shopify APP API keys to trigger a sync for, default: all enabled apps
    • syncSince: the start date to start syncing data from, default: all time
    • models: the list of model api identifiers to trigger syncs for, default: all enabled models
    • force: should Gadget still run actions for records where the updated_at timestamps match
    • startReason: a string reason to store on the created shopifySync records
returns 

Async function. Returns void.

OpenAI 

openAIResponseStream 

Converts the result returned when making a request using the OpenAI connection with stream: true into a readable stream that Fastify can respond with.

openAIResponseStream must be imported from gadget-server/ai a sub module of gadget-server

Using the openAIResponseStream function to convert an AsyncIterable into a Readable stream
JavaScript
1import { openAIResponseStream } from "gadget-server/ai";
2
3export async function run({ params, logger, api, connections }) {
4 const stream = await connections.openai.chat.completions.create({
5 model: "gpt-3.5-turbo",
6 messages: [{ role: "user", content: "Hello!" }],
7 stream: true,
8 });
9 await reply.send(
10 openAIResponseStream(stream, {
11 onComplete: (content) => {
12 console.log(content);
13 },
14 })
15 );
16}
Parameters 
  • stream - An AsyncIterable containing OpenAI response parts.
returns 

A Readable stream with the transformed content from the input stream.

Action Contexts 

Each Action and Global Action is passed a context object as its first and only argument. This context object contains all the inputs necessary for running an action, as well as some utilities.

The context object is usually destructured in the run or onSuccess function arguments with curly braces:

JavaScript
export async function run({ api, record, params, ...rest }) {
// ...
}
Properties 
PropertyTypeDescription
apiinstance of your app's clientA connected, authorized instance of the generated API client for the current Gadget application. See the API Reference for more details on this object's interface
paramsRecord<string, any>The incoming data from the API call invoking this action
recordGadgetRecord<Model>The record this action is operating on. Only available in Model Actions, and not available in Global Actions.
sessionSessionA record representing the current user's session, if there is one
configRecord<string, string>An object of all the environment variables created in Gadget's Environment Variables editor
connectionsConnectionsAn object containing client objects for all connections. Read the connections guide to see what each connection provides
loggerLoggerInstanceA logger object suitable for emitting log entries viewable in Gadget's Log Viewer
emailsGadgetMailerA instance of the Gadget wrapper for NodeMailer, used to facilitate the sending of emails.
modelNotYetTypedAn object describing the metadata for the model currently being operated on, like the fields and validations this model applies
currentAppUrlstringThe current URL for your app and environment. e.g. https://my-app--development.gadget.app
triggerAn object containing what event invoked the model's Action to run and trigger the Run Effect.
requestRequestDataAn object describing the incoming HTTP request, if this action was triggered by an HTTP request

Exported Types 

The gadget-server package contains TypeScript types specific to your application, generated from your app's models and fields. This gives great type safety where your actions and routes can import a specific type that describes the required inputs and output.

Actions 

gadget-server exports a type for each Action's context argument, which is the data passed to the run or onSuccess function.

The context type is named <Action><Model>ActionContext, where <action> is the capitalized name of your action, and <model> is the capitalized name of your model. For example, if you have an action named create on a model named `user, you can import the type for that action like this:

TypeScript
import { CreateUserActionContext } from "gadget-server";

Global Actions 

gadget-server exports a type for each Global Action's context argument, which is the data passed to the run or onSuccess function.

The context type is named <Action>GlobalActionContext, where <action> is the capitalized name of your Global Action. For example, if you have a Global Action named flipWidgets, you can import the type for that action like this:

TypeScript
import { FlipWidgetsGlobalActionContext } from "gadget-server";

Trigger Descriptors 

api trigger 

api triggers describe calls to your Gadget app's GraphQL API, like those made by the JS client or in the GraphQL playground.

An example API trigger
json
1{
2 "type": "api",
3 "mutationName": "updateWidget",
4 "rootModel": "widget",
5 "rootAction": "update",
6 "rawParams": {
7 "id": "123",
8 "widget": {
9 "title": "New Widget Title",
10 "inventoryCount": 10
11 }
12 }
13}
Properties 
  • type: will always be set to "api"
  • mutationName: the string name of the mutation called in the API
  • rootModel: the API identifier of the Model the mutation was called on. Can be different than the root-level model when invoking Nested Actions. Is not set for Global Actions.
  • rootAction: the API identifier of the Action triggered by the mutation. Can be different than the root-level action when invoking Nested Actions.
  • rawParams: the params passed to this API call, including any data for nested actions if passed

scheduler trigger 

scheduler triggers describe actions invoked by the built-in Scheduler within Gadget. No other data is currently passed with this type of trigger.

json
{
"type": "scheduler"
}

shopify_sync trigger 

shopify_sync triggers describe actions run by Gadget's Shopify Sync, including daily syncs and manual syncs.

An example Shopify Sync trigger
json
1{
2 "type": "shopify_sync",
3 "shopId": "123456",
4 "apiVersion": "2023-01",
5 "shopifyScopes": ["read_products", "write_products"],
6 "syncId": "1",
7 "syncSince": null,
8 "models": ["shopifyShop", "shopifyProduct"],
9 "force": false,
10 "startReason": undefined // will be "scheduled" if Action ran via daily sync
11}
Properties 
  • type: will always be set to "shopify_sync"
  • shopId: the identifier of the Shopify shop being synced
  • apiVersion: the version of the Shopify API being used for the sync
  • shopifyScopes: the available OAuth scopes of the Shopify shop being synced
  • syncId: the identifier of the sync record tracking the state of this sync (optional, only available if set)
  • syncSince: the specified date range of this sync (optional, only set if specified when the sync was started)
  • models: the list of model API identifiers that this sync will work on
  • force: indicates if this sync is being run in 'force' mode, which will always run actions even if the 'updated_at' timestamps match between Gadget and Shopify
  • startReason: the string describing the reason why this sync was started (optional, only set if specified when the sync began)

shopify_webhook trigger 

shopify_webhook triggers describe actions that occur in response to Shopify webhooks, such as 'products/update' or 'orders/create'.

json
1// An example Shopify Webhook trigger
2{
3 "type": "shopify_webhook",
4 "topic": "products/update",
5 "payload": {
6 "id": 788032119674292900,
7 "title": "Example T-Shirt",
8 "body_html": "An example T-Shirt",
9 "vendor": "Acme",
10 "product_type": "Shirts",
11 "created_at": null,
12 "handle": "example-t-shirt"
13 // ... etc matching Shopify's format exactly
14 },
15 "shopId": "shop123",
16 "retries": 0
17}
Properties 
  • type: will always be set to "shopify_webhook"
  • topic: the string representing the topic of the incoming webhook from Shopify, like products/update or orders/create
  • payload: the raw incoming payload from Shopify, which includes all the data sent by the webhook unchanged
  • shopId: the identifier for the Shopify store that received the webhook
  • retries: the number of times this webhook has been retried

shopify_oauth trigger 

shopify_oauth triggers describe actions invoked during the installation of an app through the Shopify Partners connection process. No other data is currently passed with this type of trigger.

json
{
"type": "shopify_oauth"
}

shopify_admin trigger 

shopify_admin triggers describe actions invoked during the installation of a Shopify app provisioned in the Shopify Admin. No other data is currently passed with this type of trigger.

json
{
"type": "shopify_admin"
}

RequestData 

The RequestData type describes an incoming HTTP request being processed by an Action.

Note: The RequestData object is passed to Actions and Global Actions, and is a read-only view of the incoming request. This is different than the Request object passed to HTTP Route handlers which has more properties only available in HTTP routes.

Properties 
  • ip: the requesting client's IP (also known as the x-forwarded-for header in other systems)
  • url: the requested URL (usually /api/graphql)
  • method: the HTTP request method, like GET, POST, etc
  • userAgent: the passed user agent string for this request
  • headers: a map of strings to strings or string arrays describing each incoming request header
  • id: a unique identifier assigned to this request by Gadget, used for logging and available in the x-request-id response header
JavaScript
export async function run({ api, request, ...rest }) {
console.log(request.ip); // log the IP of the client making this request
}

Route handlers 

Route context 

All route handlers registered in HTTP Routes are passed a context object as their first and only argument:

Context keyDescription
requestthe Request object describing the incoming HTTP request
replythe Reply object for sending an HTTP response
apia connected, authorized instance of the generated API client for the current Gadget application. See the API Reference for more details on this object's interface.
applicationSessiona record representing the current user's session, if there is one.
applicationSessionIDthe ID of the record representing the current user's session, if there is one.
connectionsan object containing client objects for all connections. Read the connections guide to see what each connection provides.
loggera logger object suitable for emitting log entries viewable in Gadget's Log Viewer.
configan object of all the environment variables created in Gadget's Environment Variables editor.
currentAppUrlthe current url for the environment. e.g. https://my-app.gadget.app

request 

The request object passed in the route context describes the incoming HTTP request, with properties for accessing the HTTP request headers, the request body, the matched route, and more. request is powered by Fastify, a high-performance HTTP framework for nodejs.

Request objects have the following fields:

FastifyRequest fieldDescription
querythe parsed query string from the incoming request, its format is specified by the route's querystringParser
bodythe request payload, see Content-Type Parser for details on what request payloads Fastify natively parses and how to support other content types
paramsthe params matching the URL
headersthe headers getter and setter
methodthe HTTP method for the route, like GET, POST, or DELETE
rawthe incoming HTTP request from Node core
idthe request ID
loga logger instance for the incoming request
ipthe IP address of the incoming request
hostnamethe host of the incoming request (derived from X-Forwarded-Host header when the trustProxy option is enabled). For HTTP/2 compatibility it returns :authority if no host header exists.
protocolthe protocol of the incoming request (will always be https on Gadget)
methodthe method of the incoming request
urlthe URL of the incoming request
routerMethodthe method defined for the router that is handling the request
routerPaththe path pattern defined for the router that is handling the request
is404true if the request is being handled by a 404 error handler, false if it is not
socketthe underlying connection of the incoming request
routeSchemathe scheme definition set for the router that is handling the request
routeConfigthe route config object
routeOptionsthe route option object passed when defining the route
bodyLimiteither the server-wide limit or route-specific limit on the size of the request body
urlthe path of the URL to match this route
logLevellog level defined for this route
versiona semver-compatible string that defines the version of the endpoint
exposeHeadRoutecreates a sibling HEAD route for any GET routes
prefixTrailingSlashstring used to determine how to handle passing / as a route with a prefix.

For more details on the Request object, see the Fastify documentation.

reply 

The reply object passed to each route in the context has functions for setting up and sending an HTTP response from your server for your route. The object is a FastifyReply object from Fastify, a high-performance HTTP framework for nodejs.

Reply objects have these functions and properties:

Reply propertyDescription
code(statusCode)sets the status code
status(statusCode)an alias for .code(statusCode)
statusCoderead and set the HTTP status code
header(name, value)sets a response header
headers(object)sets all the keys of the object as response headers
getHeader(name)retrieve the value of an already set header
getHeaders()gets a shallow copy of all current response headers
removeHeader(key)remove the value of a previously set header
hasHeader(name)determine if a header has been set
trailer(key, function)sets a response trailer
hasTrailer(key)determine if a trailer has been set
removeTrailer(key)remove the value of a previously set trailer
type(value)sets the header Content-Type
redirect([code,] dest)redirect to the specified URL with an optional status code. If not provided, the status code defaults to 302
callNotFound()invokes the custom not found handler
serialize(payload)serializes the specified payload using the default JSON serializer or using the custom serializer (if one is set) and returns the serialized payload
serializer(function)sets a custom serializer for the payload
send(payload)sends the payload to the user, could be a plain text, a buffer, JSON, stream, or an Error object
senta boolean value that you can use if you need to know if send has already been called
rawthe http.ServerResponse from Node core
logthe logger instance of the incoming request
requestthe incoming request
contextaccess the request's context property

For more details on the Reply object, see the Fastify documentation.

LoggerInstance 

The LoggerInstance object is a Pino logger instance that can be used to log messages from your Gadget app.

JavaScript
1import { logger } from "gadget-server";
2// log a plain old string message at the info level
3logger.info("Hello world");
4
5// log a structured object and a message at the info level
6logger.info({ key: "value", foo: 42, record: someRecord }, "making progress");
7
8// log an error at the error level
9try {
10 "foo" in null;
11} catch (error) {
12 logger.error({ error });
13}

LoggerInstance is a high performance alternative to console.log which is optimized for use in production. console.log logs will still appear in your Log Viewer but are not recommended for use in production.

The default log levels (and associated log functions) are trace, debug, info, warn, error, and fatal.

View the Pino logger docs for full API documentation.

Passing structured data 

An object can optionally be supplied as the first parameter to log messages. Each key and value of the object is stringified and written to your application's logs as JSON.

JavaScript
logger.info({ value: { foo: "bar" } });

Log calls at any level can also pass Error objects to get an easy-to-digest log entry in the Log Viewer for the error, including its stack trace and any other associated details. To log Error objects, pass the object at the error key of the structured data:

JavaScript
const error = new Error("something went wrong");
logger.warn({ error: error }, "error encountered, continuing");