RPC methods
Overview
The SCAYLE Storefront is built upon a foundation of Remote Procedure Call (RPC) methods. These are JavaScript functions that adhere to a defined interface and are registered with the RPC system. This approach allows us to encapsulate business logic into reusable functions, exposed through an automatically generated API. As a result, developers can easily integrate this functionality without the overhead of building and managing REST APIs. TypeScript provides automatic type checking for parameters and return values, enhancing reliability.
Server-side Execution
In Nuxt applications, code can be executed both in client-side and server-side contexts. Nuxt offers several rendering modes to control this behavior, with Universal Rendering being the most common. In this mode Vue components are first rendered on the server then hydrated in the client.
No matter which rendering mode is used, the bodies of RPC methods are executed exclusively on the server. The RPC framework automatically detects the current context and adjusts its behavior.
- During Server-Side execution (Including SSR and API handlers): Calling an RPC method directly executes its corresponding function on the server.
- On the Client-Side: Calling an RPC method triggers an HTTP request to the server. The server then executes the method and returns the result.
This process is handled automatically, providing a consistent API for calling RPC methods regardless of the rendering context. In the Storefront Application, all calls to SCAYLE APIs are made within RPC methods. This server-side execution improves performance, protects sensitive API tokens from client exposure, and keeps certain implementation details private.
Understanding a real example
Consider the useProduct
composable, which can be used during both server-side rendering and on the client. It relies on the getProduct
RPC method. Because getProduct
executes on the server, it has secure access to the API token and can call the Storefront API. Furthermore, most RPC methods leverage caching to reduce latency and minimize the load on the Storefront API.

Storefront Core API Example
The following code example is a slightly modified and annotated version of the actual RPC method used to fetch a product. It displays many of the core functionalities of an RPC method including API calls, logging, caching and handling parameters.
// Import the necessary types from @scayle/storefront-nuxt
import type { RpcHandler, FetchProductParams, Product } from '@scayle/storefront-nuxt'
import { defineRpcHandler } from '@scayle/storefront-nuxt'
// Wrap the function with defineRpcHandler for improved typechecking
export const getProduct: RpcHandler<FetchProductParams, Product> =
defineRpcHandler(async function getProduct(
options,
context,
) {
// From the RPCContext, get the SAPI API client, logger, cache utility and default withParams
const { sapiClient, log, cached, withParams } = context
// When an RPC method depends on calling another RPC method,
// use `context.callRpc`, passing the RPC method name as the
// first argument followed by the arguments if necessary.
const campaignKey = await context.callRpc?.('getCampaignKey')
// If the getCampaignKey call failed and returned an error
// we propogate that error.
if (campaignKey instanceof ErrorResponse) {
return campaignKey
}
// Log a message each time the method is executed
log.info(`Fetching product ${options.id}`)
// To reduce latency and avoid unnecessary load on SAPI we
// cache the results. The cache is keyed by the prefix plus a hash of
// the arguments passed to the function. In this case we set a TTL
// of five minutes.
return await cached(
// This is the SAPI method we are calling
sapiClient.products.getById,
{
cacheKeyPrefix: `getById-product-${options.id}`,
ttl: 5 * MINUTE,
},
)(
// Call the cached function with the following options
// Most of which come from the RPC method's parameters
options.id, {
// First use the RPC method parameter and fallback to the default value
with: options.with ?? withParams?.product,
campaignKey,
pricePromotionKey: options.pricePromotionKey,
includeSellableForFree: options.includeSellableForFree,
})
})
Custom RPC Methods
Storefront applications can define and implement custom RPC methods, which are then callable within the application. These custom RPCs have access to the RPCContext, providing information about the session, shop, and secure tokens.
Creating a custom RPC method is recommended when integrating with external services, such as SCAYLE add-ons or third-party providers. This approach also helps prevent sensitive code and data from being exposed to the client.
A Basic Example
To create a custom RPC method, begin by adding a new file to the rpcMethods
directory of your project:
This example demonstrates creating an RPC method called hasUserSubscribed
, which checks if the current user is subscribed using a hypothetical newsletterApi
. defineRpcHandler
is a utility function exported from @scayle/storefront-nuxt
which ensures that the handler function conforms to the type expectations of an RPC handler function.
Passing Arguments to RPC Methods
RPC methods can also accept parameters. These parameters are the first argument of your RPC handler function. The RpcContext
remains the second argument. You'll also need to update the RpcHandler<Input, Output>
type declaration to reflect the input and output types.
This example defines the subscribeToNewsletter
RPC method, which accepts an email address and adds it as a subscriber to the newsletter. The RpcHandler
type declaration specifies the input type ({ email: string }
) and the output type ({ success: boolean }
).
Throwing Errors and Controlling Status Codes
If an uncaught error is thrown during the execution of an RPC method, the HTTP response will have a 500 Server Error status. To have more control over the status code, you can instead return an ErrorResponse
object with the status code and message set.
If you want to control the status code for a successful response, you can return a Response
object. Notice, this does not impact the type of the RpcHandler
. When calling the RPC method through useRpc
, useRpcCall
or context.callRpc
the Response
will be automatically unwrapped so the return value will be of the type { success: boolean }
.
Registering Custom RPC Methods
In order for a custom RPC method to be usable, it must be registered. Registration will create the necessary API endpoint for the RPC and extend the global types to include the RPC. There are two ways to register a custom RPC method: through the storefront:custom-rpc:extend
hook or by configuring the rpcDir
and rpcMethodNames
options. The hook approach is newer and safer - it guarantees that built-in functionality is not overridden and is also usable from Nuxt modules. However, there may some situations where you want to override this safety mechanism and in that case you can use rpcDir
.
Using the hook approach
To add a custom RPC method through the hook, listen for the storefront:custom-rpc:extend
hook and append your RPC method import definitions. An import definition includes the source file and the export names to register as RPC methods.
The registration of RPC methods via this hook is slightly more restrictive:
- Hook-registered RPC methods have a lower priority than those installed via
rpcDir
. If there is a naming conflict, the one registered viarpdDir
one will take precedence. - RPC methods registered via this hook are not allowed to override core RPC methods from the Storefront SDKs.
Using the rpcDir approach
This strategy is only usable within a Storefront Application. To register RPC methods in this manner, you must define rpcDir
and rpcMethodNames
in the Nuxt configuration ( nuxt.config.ts
). rpcDir
defines the directory from where the method functions will be imported, and rpcMethodNames
is a list of the identifiers to import and register as RPC methods.
export default defineNuxtConfig({
storefront: {
rpcDir: 'rpcMethods',
rpcMethodNames: ['hasUserSubscribed', 'subscribeToNewsletter']
}
})
Overriding Core RPC Methods
The Storefront SDKs also include their own set of RPCs. This introduces the possibility of name conflicts, which could result in accidentally overriding an existing RPC method.
In such cases, you will receive a warning in your console indicating which RPC method was overridden, as this can lead to unexpected side effects and cause the application to behave improperly.
If the override is intentional, you can suppress console warnings for intended overrides, by adding the overridden RPC method names to the storefront.rpcMethodOverrides
configuration in your nuxt.config.ts
.
Overriding core RPC methods should be done with extreme caution and only when absolutely necessary. If an RPC method is overridden, we cannot guarantee that the Storefront SDKs will work as expected. It becomes your responsibility to test all relevant cases thoroughly.
Source | Priority | Can Override Core RPCs? | Location |
---|---|---|---|
Core RPC Method | Medium | n/a | storefront-core |
storefront:custom-rpc:extend | Low | No | Application or Modules |
`rpcDir`/`rpcMethodNames` | Highest | Yes | Application |
Using Custom RPC Methods in Components and Composables
After defining and registering your custom RPC methods, you need a way to call them from your components or composables. Two options are available, depending on your needs:
- useRpc: This composable provides a declarative approach for calling RPCs within pages, components, or other composables. It's suitable for calls that should occur during initial setup or data fetching.
useRpc
is a wrapper arounduseAsyncData
so it shares the same behavior and options. - useRpcCall: Use this composable when triggering an RPC in response to an event or user interaction (e.g., clicking a button, submitting a form).
useRpcCall
is more akin to$fetch
.
Both useRpc
and useRpcCall
are composables and must follow composable usage rules. For further guidance, refer to Nuxt's data fetching guide.
useRpc
useRpc
offers a declarative approach to data fetching. It handles the fetch state, automatically executes the RPC method, and returns the result or any errors. Many Storefront Core composables utilize useRpc
as lightweight wrappers around provided RPCs.
Here's how to call the hasUserSubscribed
RPC from the previous example:
The first argument to useRpc
is the RPC method name (the name under which the RPC is exported). The second argument is a unique key, which Nuxt uses to avoid redundant fetches and repopulate server-side rendered data. For more details, consult the Nuxt documentation or check out the "Nuxt 3.8 - Client-side caching with getCachedData" YoutTube video by Alexander Lichter, a member of the Nuxt Core Team.
We recommend using location-specific unique keys and explicitly passing data rather than assuming key usage and fetch behavior elsewhere.
The third (optional) argument provides parameters to the RPC method. In this example, hasUserSubscribed
doesn't require parameters. If parameters are needed, you can pass a reactive function or a computed value. useRpc
will automatically re-fetch the data when any of the reactive parameters change.
useRpcCall
useRpcCall
is designed for calling RPCs in response to events or user interactions, such as clicking a button or submitting a form. Consider the subscribeToNewsletter
example, triggered by a form submission:
useRpcCall
accepts the RPC method name and returns a callback function. When invoked, this callback receives the RPC parameters, makes the request, and returns a promise that resolves with the RPC's response.
RPC Context
The RPC context object provides information about the current application, including application secrets and helper functions for RPCs.
Key properties and methods include:
Property Name | Description |
---|---|
sapiClient | A reference to the Storefront API client initialized to the shop of the request. |
cached | A utility function for caching functions. |
user | The current user. |
wishlistKey | The wishlist identifier for the current session. |
basketKey | The basket identifier for the current session. |
sessionId | The unique session id of the current user. |
shopId | The shop id of the current request. |
log | A reference to the logger. |
runtimeConfiguration | An object containing the private properties set at runtime through the environment. |
callRpc | A function used to call another RPC method. |
Extending the RPC Context
You can add custom information to the RPC context to make it available to all your RPC methods. This is achieved by hooking into the storefront:context:created
Nitro runtime hook.
Create a server plugin and register a hook using nitroApp.hooks.hook('storefront:context:created', handler)
. The handler
is a synchronous or asynchronous function that receives the base RPCContext
as its argument. You can modify the context passed to RPC methods by adding or changing properties on this object.
Adding a New Property
To add a new property to the RPC Context, adapt the following snippet:
Overwriting Existing Values
Caution: Overwriting existing RPCContext
properties is strongly discouraged, as it can break application functionality. Proceed with extreme care if you choose to do so.
If overwriting or changing existing values within the RPC context is absolutely necessary, use the following approach: