Nuxt module for oRPC
I am trying to create a nuxt module which would register the catch all event handler, but I am having hard time to register the routers in it. Anyone has idea how to achieve this?
And the handler:
On the project side, I am registering it in a server pluginI can see that:
๐ Registering router for module: todos
๐ Registered 6 schemas for module: todos
๐ง Built combined router with modules: todos
But on actual requests I see:
๐ RPC Handler - Handling oRPC request: POST /rpc/todos/list
๐ง Built combined router with modules:
๐ง Available router modules: []
So somewhere I lost the registered items, I have tried with useRuntimConfig, same happens. What clears it between the server plugin and the request?
๐ง Available router modules: []
So somewhere I lost the registered items, I have tried with useRuntimConfig, same happens. What clears it between the server plugin and the request?
19 Replies
Hi @BenG. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
The issue you're encountering is due to the separation between build-time (when your Nuxt module and server plugins are registered) and runtime (when your API handlers are executed). In Nuxt 3/Nuxt 4, the server context is stateless between requests, and any in-memory state (like your
The runtime context (where your API handlers run) is isolated from the build context (where modules and plugins are registered). They do not share state, code, or context except through runtime configuration or persistent storage. This means that any in-memory state set up in a server plugin or module will not be available in your event handler at request timeย Runtime Context vs. Build Context. - Server Plugins and Nitro Lifecycle:
Nitro plugins (in
To persist data between build-time/module registration and runtime/request handling, you must use a persistent mechanism, such as: - The runtime config (
You cannot rely on in-memory state set up in server plugins or modules to be available in API handlers at request time. Use persistent storage or static code exports for sharing data between build-time and runtime in Nuxt/Nitroย Runtime Context vs. Build Context. If you need to register dynamic routers, consider using Nitro's storage layer or another persistent mechanism to store and retrieve your router definitionsย Server Storage. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
routerRegistry
and schemaRegistry
maps) initialized at build or module registration time will not persist across requests in production or serverless environments. This is especially true when using Nitro, which can spin up new server instances per request (e.g., in serverless deployments) or reloads modules between requests for HMR in development.
Key points from the documentation:
- Runtime Context vs. Build Context:The runtime context (where your API handlers run) is isolated from the build context (where modules and plugins are registered). They do not share state, code, or context except through runtime configuration or persistent storage. This means that any in-memory state set up in a server plugin or module will not be available in your event handler at request timeย Runtime Context vs. Build Context. - Server Plugins and Nitro Lifecycle:
Nitro plugins (in
server/plugins/
) are executed once when the server starts, but in serverless or development environments, the server (and thus the plugin) may be re-initialized per request, losing any in-memory stateย Nuxt Lifecycle: Nitro Plugins.
- How to Share State:To persist data between build-time/module registration and runtime/request handling, you must use a persistent mechanism, such as: - The runtime config (
useRuntimeConfig
), but this is meant for configuration, not dynamic state.
- A cross-platform storage layer (e.g., Redis, file storage) via Nitro's storage APIย Server Storage.
- Or, if you need to share code, export functions or data from a shared module and import them in both places, but not in-memory state.
What is happening in your case:
- You register routers in a server plugin (executed at server start/build time).
- When a request comes in, your handler runs in a new context, and the in-memory registry is empty.
How to fix:
- Store your router and schema registry in a persistent storage (e.g., Nitro storage, file, or database), or
- Register your routers at runtime (inside the handler or a Nitro plugin that runs per request), or
- Use a static registry (i.e., export a constant or function from a shared file and import it in both the plugin and handler), but do not rely on in-memory mutation across build/runtime boundaries.
Summary:You cannot rely on in-memory state set up in server plugins or modules to be available in API handlers at request time. Use persistent storage or static code exports for sharing data between build-time and runtime in Nuxt/Nitroย Runtime Context vs. Build Context. If you need to register dynamic routers, consider using Nitro's storage layer or another persistent mechanism to store and retrieve your router definitionsย Server Storage. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
<@371347799255547916> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@kapa.ai Can you give me exampes for: Store your router and schema registry in a persistent storage (e.g., Nitro storage, file, or database), or
Register your routers at runtime (inside the handler or a Nitro plugin that runs per request),
Let me check the knowledge base for you again. One moment! :books:
----------------------
Certainly! Here are examples for both approaches, based strictly on the knowledge sources:
---
1. Store your router and schema registry in persistent storage (Nitro storage)
You can use Nitro's storage layer to persist data between requests. For example, you can configure a storage driver (like filesystem or Redis) in your
- In-memory mutation (e.g., using a
nuxt.config.ts
:
See: Server Storage Example
Then, in your server plugin or handler, you can use useStorage
to set and get your routers and schemas:
See: Nitro Storage API
---
2. Register your routers at runtime (inside the handler or a Nitro plugin that runs per request)
If your router definitions are static or can be imported, you can register them directly in your handler or a Nitro plugin:
This approach avoids the need for persistent storage by building the router object at runtime from static imports.
---
Note:- In-memory mutation (e.g., using a
Map
or object to register routers at build time) will not persist between requests in Nitro, especially in serverless or development environmentsย Nuxt Lifecycle: Nitro Plugins.
- Use Nitro storage for dynamic, persistent data; use static imports for static router definitions.
If you need more advanced dynamic registration, refer to the Nitro storage documentation for details on supported drivers and usage.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@371347799255547916> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@kapa.ai after which hooks the #import becomes available? I tried nitro:config, listen and modules:done, netiher worked
Let me check the knowledge base for you again. One moment! :books:
----------------------
Based on the knowledge sources, the
#imports
alias (which provides access to auto-imported composables and utilities) becomes available after the auto-import system has been set up. According to the documented Nuxt build-time hooks, the relevant hooks for extending or interacting with imports are:
- imports:sources
- imports:extend
- imports:context
- imports:dirs
These hooks are called at setup, allowing modules to extend sources, imports, and directories. The modules:done
hook is called after installing user modules, but the auto-import system is set up before this point. Therefore, the #imports
alias is not guaranteed to be available in nitro:config
, listen
, or modules:done
.
The documentation does not explicitly state the exact hook after which #imports
becomes available for use, but it does indicate that the auto-import system is configured during the setup phase, specifically around the imports:*
hooks. If you need to interact with or extend auto-imports, you should use these imports:*
hooks rather than modules:done
or later hooksย Nuxt Hooks (build time).
I'm sorry, but the knowledge sources do not explicitly state the precise moment when the #imports
alias becomes available for general use in your code. However, it is clear that it is set up during the imports-related hooks, before modules:done
.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@371347799255547916> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
what hook is called the latest, in general in nuxt? @kapa.ai
Let me check the knowledge base for you again. One moment! :books:
----------------------
The latest hook called in the general Nuxt lifecycle is the
close
hook. This hook is triggered when the Nuxt instance is gracefully closing, allowing you to perform cleanup tasks or shutdown logic. It is available both at build time and in the Nitro server lifecycleย Nuxt Hooks (build time)ย Lifecycle Hooks.
Example usage:
This is the last documented hook in the Nuxt lifecycle according to the available knowledge sources.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@371347799255547916> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
How to access scanDirs in the context of serverplugin: export default defineNitroPlugin((nitro) => {... @kapa.ai
Let me check the knowledge base for you again. One moment! :books:
----------------------
I'm sorry, but the knowledge sources do not contain explicit information about accessing
scanDirs
in the context of a Nitro server plugin (i.e., within defineNitroPlugin
). There is documentation on how to add scan directories using addServerScanDir
in a Nuxt module setup, but not on how to access or read the current scan directories from within a Nitro pluginย addServerScanDir.
If you need to modify or add scan directories, you should do so in a Nuxt module using the appropriate hooks or utility functions, not from within a Nitro plugin. The Nitro plugin context (nitro
) does not appear to expose scanDirs
based on the available documentation.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@371347799255547916> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@kapa.ai
Let me check the knowledge base for you again. One moment! :books:
Sorry @BenG, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?
@kapa.ai reach scanDirs in nuxt.hook('modules:done', async () => {
Let me check the knowledge base for you again. One moment! :books:
----------------------
I'm sorry, but the knowledge sources do not contain explicit information about how to access
scanDirs
within the nuxt.hook('modules:done', ...)
context. The documentation describes how to add scan directories using addServerScanDir
in a Nuxt module's setup function, but it does not mention a way to read or access the current list of scan directories from within the modules:done
hook or any other hookย addServerScanDir.
If you need to modify scan directories, you should use the provided utility functions during module setup. There is no documented method to "reach" or inspect scanDirs
in the modules:done
hook according to the available sources.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@371347799255547916> Kapa.ai is still learning and improving, please let me know how I did by reacting below.