Debugging and profiling Gadget backends 

Gadget's CLI, ggt, includes a ggt debugger command that allows you to debug or profile your Gadget backend.

Setup 

Before you can start debugging or profiling, you need to have ggt installed and your project's files pulled down locally.

  1. Install ggt locally if you have not already:
terminal
npm install -g ggt
  1. Use ggt dev to pull down your project's files locally:
terminal
ggt dev --app <app-name> --env <env-name>

For more information on ggt, see the CLI guide.

Debugging 

ggt debugger configures your editor to enable debugging of your Gadget app's backend actions, HTTP routes, and loader functions. Once configured, you can set breakpoints in your code and debug your application directly from your editor.

There are built-in launch configurations for VS Code and its forks, but you can use any editor that supports the Chrome DevTools Protocol. Other editors may require additional configuration.

Initial setup 

To configure debugging, run the following command in your project directory:

terminal
ggt debugger --configure vscode

This will add .vscode/launch.json and .vscode/tasks.json config files to your project. The --configure flag only needs to be run once per project.

ggt debugger --configure will fail if you already have .vscode/tasks.json or .vscode/launch.json files in your project. You will need to remove or rename these files before running the configuration command, then manually merge existing configuration.

Starting the debugger 

After initial configuration, you can start debugging by running the Gadget debugger launch configuration from your editor. Any breakpoints you set in your editor will be hit when the debugger is attached.

Profiling 

Your Gadget backend runs on Node.js, which means a Node.js profiler can be used to get detailed information about the performance of your actions and routes.

A profiler can help you identify bottlenecks and computationally expensive functions or third party libraries, optimize your actions, and improve the performance of your backend.

Starting the profiler 

You need to use ggt to set up a connection to your backend, then a Node.js profiler like Chrome DevTools to capture and analyze the profile.

  1. Run ggt debugger in your local terminal to start a websocket connection to your backend:
terminal
ggt debugger

The localhost address for the websocket connection will be listed in your terminal. Use this address to connect your Node.js profiler to your backend. This guide will use Chrome DevTools to profile your backend.

  1. Open Chrome and navigate to chrome://inspect. You should see your localhost address listed as a target.
  2. Click inspect under your localhost address to open the Chrome DevTools inspector for your backend.

You are now set up and ready to start profiling your backend.

Profiling CPU time 

The Node.js profiler is a sampling profiler. It works by capturing a stack trace of the JavaScript code running in your backend at regular intervals.

This means it is important to run the code you want to profile multiple times to get a representative sample of the code's execution. A single execution of an action may not be enough for the profiler to capture an accurate profile.

To capture a profile for an action, you can:

  1. Start the profiler in the Performance tab of Chrome DevTools.
  2. Use the API playground in the Gadget editor to call your backend action or route multiple times:
Run your action multiple times to generate a profile
JavaScript
for (let i of Array(100)) { await api.generateReport(); }
  1. Stop the profiler in Chrome DevTools to finish capturing the profile.
  2. (Optional) Save the profile locally.

Now you can analyze your profile.

Getting a representative sample

You need enough runs of your action or route to get a representative sample. If you see functions appearing and disappearing between runs, or if the relative time spent in functions keeps changing, you need more runs. The profile should show consistent patterns before you can trust the results.

Tips for generating a good profile 

There are things you can do to generate a more useful profile.

  • It is worth repeating: run the code you want to profile multiple times to get a representative sample of the code's execution. A single execution of an action may not be enough for the profiler to capture an accurate profile.
  • Break down your code into smaller functions so the profile captures more granular information. Profilers display the time spent in each function, so the more granular the functions, the more detailed the profile.

Analyzing your profile 

Once you have captured a profile of your actions, you can start to analyze it and identify bottlenecks in your code.

The Chrome DevTools profiler shows two key metrics for each function:

  • Total time: The CPU time spent in a function and all functions it calls. If functionA() calls functionB(), the total time for functionA includes all the time spent executing functionB.
  • Self time: The CPU time spent only in that function's own code, excluding any time spent in called functions.

What to look for 

Focus on functions that combine high self time with high call frequency, these are your biggest optimization opportunities. For example, a function with 30% self time called 1000 times is a better target than one with 50% self time called once.

Functions with high total time but low self time are calling expensive subfunctions, drill down into what they call. Look for patterns like nested loops, repeated calculations, or inefficient data transformations.

If you see expensive functions in third-party libraries, look at the call stack to see what your code is calling that leads to those expensive library functions. You may be able to optimize how you are using the library, or find an alternative approach.

How to take action 

Once you have identified a bottleneck, here are common optimization strategies:

  • Cache expensive computations: If you are recalculating the same value repeatedly, cache it, or store the result in the database.
  • Reduce iterations: Look for opportunities to reduce loop iterations or use more efficient algorithms. Computed views can be helpful for reading and aggregating large sets of data from the database.
  • Batch operations: Instead of processing items one-by-one, batch them together. Your app API includes bulk action endpoints that can and should be used, including bulk endpoints for background actions.
  • Optimize data structures: Use the right data structure for your use case, for example, Set for speedy lookups, Map for key-value pairs.
  • Move work outside hot paths: If possible, precompute values or move expensive operations to less frequently called code.

After making changes, re-profile your code to verify the improvement. Compare the self time and total time of the optimized functions before and after your changes.

More information 

See the Google Chrome DevTools docs for more information on using the Node.js profiler.

How profiler times relate to Gadget billing 

The times shown in the profiler represent CPU time, not wall-clock time. This is the same metric Gadget uses for billing, Gadget only bills for actual CPU time consumed by your code.

Importantly, idle time is not billed. When your code is waiting for async operations like network requests, database queries, or file I/O, the CPU is idle and no time accumulates. This means:

  • A function with high total time but low self time is calling expensive subfunctions. Look at what it calls.
  • A function with high self time is doing CPU-intensive work itself. Optimize its logic directly.
  • Time spent awaiting external services will not affect your CPU billing.

Troubleshooting your app 

Beyond the debugger and profiler, Gadget provides several other tools to help you diagnose issues in your application.

Using logs 

Your application logs are one of the first places to look when something is not working as expected. Gadget logs all events in your application, including incoming requests, webhooks, connection syncs, and any custom log statements you add to your code.

To view your logs, open the Logs panel in the Gadget editor sidebar.

Logs can also be streamed through your terminal with the command:

terminal
ggt logs

Filtering by log level 

By default, the log viewer shows all log levels. To focus on problems, use the log level filter to show only errors or warnings.

Inspecting log details 

Click on any log entry to expand it and view additional details. Depending on the type of log, you may see structured data such as error messages, stack traces, record data, or request metadata. Some log entries contain only minimal information like a proxy ID, while others include the full error context.

Using the AI assistant to explain errors 

You can use the built-in AI assistant to help understand log entries. Click the Explain button on a log entry, or copy and paste the log content into the assistant. The assistant has context about your app, Gadget documentation, and Shopify documentation, making it a useful tool for understanding errors you encounter in the log viewer.

For more details on logging, log levels, structured data, querying with LogQL, and trace IDs, see the Logger guide.

Using browser developer tools 

For frontend issues, your browser developer tools provide valuable debugging information that may not appear in your backend logs. This is especially useful when:

  • Your page is stuck in an infinite reload or shows a blank screen
  • A request is failing before it reaches your backend
  • You need to inspect GraphQL queries or API responses

Inspecting network requests 

You can see every request your frontend makes in the Network tab, including GraphQL queries to your Gadget backend.

Inspecting the console 

The Console tab in browser developer tools shows JavaScript errors, warnings, and any console.log output from your frontend code.

Using the operations dashboard 

The operations dashboard provides a high-level overview of your application's performance, errors, and resource usage. Open it from the bottom of the Gadget editor sidebar.

From the operations dashboard, you will be able to monitor specific usage metrics such as response codes, rate limits, worker usage, and database usage. These views can be used to detect anomalous activity and optimize your application.

Each section of the dashboard also includes a ? tooltip explaining what the chart shows, and a View in docs link for more information.

Learn more about the operations dashboard here Operations dashboard.

Changelog 

Gadget publishes a changelog on the editor home page under What's New, or on the Changelog page in the documentation. Check the changelog regularly, as the issue you are encountering may already be resolved in a newer framework version. Upgrading your Gadget framework version can fix known issues and unlock new features.

Gadget status page 

If you suspect the issue is not specific to your app, check the Gadget status page for any ongoing platform incidents. You can subscribe to status updates via email, Slack, Microsoft Teams, Google Chat, webhook, or RSS.

Community support 

The Gadget Discord server is a valuable resource for troubleshooting. You can search existing conversations in the bugs and help channels to find others who may have encountered the same issue. You can also ask questions and get support from Gadget staff and the community.

Shopify developer forums 

If your issue involves Shopify API behavior or unexpected responses from Shopify, the Shopify developer forums can help you find others who have encountered similar integration challenges.

Was this page helpful?