|
| 1 | +# Debugging Applications with the REPL |
| 2 | + |
| 3 | +Interactive debugging is essential for understanding application behavior, inspecting state, and troubleshooting issues at runtime. Watt provides a built-in REPL (Read-Eval-Print Loop) command that lets you connect directly to a running application's worker thread and execute JavaScript code interactively. |
| 4 | + |
| 5 | +This guide will walk you through using the REPL for debugging, from basic usage to advanced techniques for inspecting your application's internals. |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +The `wattpm repl` command starts an interactive Node.js REPL session inside a running application's worker thread. This gives you direct access to: |
| 10 | + |
| 11 | +- The Fastify application instance (`app`) - for service-based applications |
| 12 | +- The application capability object (`capability`) - configuration and methods |
| 13 | +- The global Platformatic object (`platformatic`) - messaging, events, shared context |
| 14 | +- The application configuration (`config`) |
| 15 | +- The application logger (`logger`) |
| 16 | +- All modules and variables in the worker's scope |
| 17 | + |
| 18 | +### Key Features |
| 19 | + |
| 20 | +- **Live inspection**: Examine application state while it's running |
| 21 | +- **Execute code**: Run arbitrary JavaScript in the application context |
| 22 | +- **Access Fastify**: Interact with routes, plugins, decorators, and the server |
| 23 | +- **Debug issues**: Inspect variables, test functions, and trace problems |
| 24 | +- **Zero configuration**: Works out of the box with any Watt application |
| 25 | +- **Remote debugging**: Connect to applications running in any environment |
| 26 | + |
| 27 | +## Prerequisites |
| 28 | + |
| 29 | +Before using the REPL, ensure that: |
| 30 | + |
| 31 | +1. **Watt is installed**: You need `wattpm` CLI installed globally or in your project |
| 32 | +2. **Application is running**: Your Watt application must be running in development or production mode |
| 33 | +3. **Applications are operational**: The applications you want to debug should be started |
| 34 | + |
| 35 | +```bash |
| 36 | +# Install wattpm globally |
| 37 | +npm install -g wattpm |
| 38 | + |
| 39 | +# Verify wattpm installation |
| 40 | +wattpm version |
| 41 | +``` |
| 42 | + |
| 43 | +## Basic Usage |
| 44 | + |
| 45 | +### Starting a REPL Session |
| 46 | + |
| 47 | +To start a REPL session in your application: |
| 48 | + |
| 49 | +```bash |
| 50 | +# Auto-connect if single application, or list available applications |
| 51 | +wattpm repl |
| 52 | + |
| 53 | +# Connect to a specific application |
| 54 | +wattpm repl api-service |
| 55 | + |
| 56 | +# Connect with explicit runtime name |
| 57 | +wattpm repl my-app api-service |
| 58 | + |
| 59 | +# Connect using runtime PID |
| 60 | +wattpm repl 12345 api-service |
| 61 | +``` |
| 62 | + |
| 63 | +When the REPL connects successfully, you'll see a prompt with the application name: |
| 64 | + |
| 65 | +``` |
| 66 | +api-service> |
| 67 | +``` |
| 68 | + |
| 69 | +### Exiting the REPL |
| 70 | + |
| 71 | +To exit the REPL session: |
| 72 | + |
| 73 | +```javascript |
| 74 | +api-service> .exit |
| 75 | +``` |
| 76 | + |
| 77 | +Or press `Ctrl+D` (EOF) or `Ctrl+C` twice. |
| 78 | + |
| 79 | +## Exploring the Application Context |
| 80 | + |
| 81 | +### The `app` Object |
| 82 | + |
| 83 | +The `app` variable provides access to the Fastify application instance: |
| 84 | + |
| 85 | +```javascript |
| 86 | +// Get server address |
| 87 | +api-service> app.server.address() |
| 88 | +{ address: '::', family: 'IPv6', port: 3000 } |
| 89 | + |
| 90 | +// List registered routes |
| 91 | +api-service> app.printRoutes() |
| 92 | +└── / |
| 93 | + ├── api (GET, HEAD) |
| 94 | + │ └── /health (GET, HEAD) |
| 95 | + └── users (GET, HEAD, POST) |
| 96 | + |
| 97 | +// Access registered plugins |
| 98 | +api-service> app.pluginName |
| 99 | +'my-plugin' |
| 100 | + |
| 101 | +// Check if a decorator exists |
| 102 | +api-service> app.hasDecorator('myDecorator') |
| 103 | +true |
| 104 | +``` |
| 105 | + |
| 106 | +### The `platformatic` Object |
| 107 | + |
| 108 | +The `platformatic` variable provides access to the global Platformatic context: |
| 109 | + |
| 110 | +```javascript |
| 111 | +// See what's available |
| 112 | +api-service> Object.keys(platformatic) |
| 113 | +[ 'logger', 'events', 'messaging', 'config', 'sharedContext' ] |
| 114 | + |
| 115 | +// Access the logger |
| 116 | +api-service> platformatic.logger.info('Hello from REPL!') |
| 117 | + |
| 118 | +// Access shared context |
| 119 | +api-service> platformatic.sharedContext.get() |
| 120 | +{ someKey: 'someValue' } |
| 121 | +``` |
| 122 | + |
| 123 | +### The `config` Object |
| 124 | + |
| 125 | +The `config` variable contains the application's configuration: |
| 126 | + |
| 127 | +```javascript |
| 128 | +// Get application ID |
| 129 | +api-service> config.id |
| 130 | +'api-service' |
| 131 | + |
| 132 | +// Inspect configuration |
| 133 | +api-service> config.server |
| 134 | +{ hostname: '0.0.0.0', port: 3000 } |
| 135 | + |
| 136 | +// Check environment |
| 137 | +api-service> config.isProduction |
| 138 | +false |
| 139 | +``` |
| 140 | + |
| 141 | +### The `capability` Object |
| 142 | + |
| 143 | +The `capability` variable provides access to the application's capability with its configuration and methods: |
| 144 | + |
| 145 | +```javascript |
| 146 | +// Check capability type |
| 147 | +api-service> capability.type |
| 148 | +'service' |
| 149 | + |
| 150 | +// Get application root directory |
| 151 | +api-service> capability.root |
| 152 | +'/path/to/my-app/services/api-service' |
| 153 | + |
| 154 | +// Check if this is the entrypoint |
| 155 | +api-service> capability.isEntrypoint |
| 156 | +true |
| 157 | + |
| 158 | +// Access runtime configuration |
| 159 | +api-service> capability.runtimeConfig |
| 160 | +{ ... } |
| 161 | + |
| 162 | +// Get application URL (if listening) |
| 163 | +api-service> capability.url |
| 164 | +'http://127.0.0.1:3000' |
| 165 | + |
| 166 | +// Access the capability's logger |
| 167 | +api-service> capability.logger.info('Debug message') |
| 168 | +``` |
| 169 | + |
| 170 | +### The `logger` Object |
| 171 | + |
| 172 | +The `logger` variable provides access to the application's Pino logger: |
| 173 | + |
| 174 | +```javascript |
| 175 | +// Log at different levels |
| 176 | +api-service> logger.info('Information message') |
| 177 | +api-service> logger.debug({ data: 'object' }, 'Debug with data') |
| 178 | +api-service> logger.warn('Warning message') |
| 179 | +api-service> logger.error(new Error('test'), 'Error occurred') |
| 180 | + |
| 181 | +// Check current log level |
| 182 | +api-service> logger.level |
| 183 | +'info' |
| 184 | +``` |
| 185 | + |
| 186 | +## Advanced Debugging Techniques |
| 187 | + |
| 188 | +### Inspecting Routes |
| 189 | + |
| 190 | +You can examine registered routes and their handlers: |
| 191 | + |
| 192 | +```javascript |
| 193 | +// Get all routes with details |
| 194 | +api-service> app.printRoutes({ includeHooks: true, includeMeta: true }) |
| 195 | + |
| 196 | +// Access route schemas |
| 197 | +api-service> app.getSchemas() |
| 198 | +``` |
| 199 | + |
| 200 | +### Testing Endpoints |
| 201 | + |
| 202 | +Use `app.inject()` to make test requests without going through the network: |
| 203 | + |
| 204 | +```javascript |
| 205 | +// Make a GET request |
| 206 | +api-service> await app.inject({ method: 'GET', url: '/api/health' }) |
| 207 | +{ |
| 208 | + statusCode: 200, |
| 209 | + headers: { 'content-type': 'application/json' }, |
| 210 | + body: '{"status":"ok"}' |
| 211 | +} |
| 212 | + |
| 213 | +// Make a POST request with body |
| 214 | +api-service> await app.inject({ |
| 215 | + method: 'POST', |
| 216 | + url: '/api/users', |
| 217 | + payload : { name : 'Test User', email : '[email protected]' }, |
| 218 | + headers: { 'content-type': 'application/json' } |
| 219 | +}) |
| 220 | +``` |
| 221 | + |
| 222 | +### Accessing Decorators |
| 223 | + |
| 224 | +If your application uses Fastify decorators: |
| 225 | + |
| 226 | +```javascript |
| 227 | +// Access a decorated property |
| 228 | +api-service> app.db |
| 229 | +[Database Connection Object] |
| 230 | + |
| 231 | +// Call a decorated method |
| 232 | +api-service> await app.authenticate({ token: 'test-token' }) |
| 233 | +``` |
| 234 | + |
| 235 | +### Inspecting Plugins |
| 236 | + |
| 237 | +Examine registered plugins: |
| 238 | + |
| 239 | +```javascript |
| 240 | +// List all plugins |
| 241 | +api-service> app.printPlugins() |
| 242 | + |
| 243 | +// Access plugin-specific data |
| 244 | +api-service> app.swagger |
| 245 | +[Swagger Plugin Object] |
| 246 | +``` |
| 247 | + |
| 248 | +### Working with Environment Variables |
| 249 | + |
| 250 | +```javascript |
| 251 | +// Access environment variables |
| 252 | +api-service> process.env.DATABASE_URL |
| 253 | +'postgres://localhost:5432/mydb' |
| 254 | + |
| 255 | +// Check NODE_ENV |
| 256 | +api-service> process.env.NODE_ENV |
| 257 | +'development' |
| 258 | +``` |
| 259 | + |
| 260 | +### Inter-Service Communication |
| 261 | + |
| 262 | +Test communication between applications: |
| 263 | + |
| 264 | +```javascript |
| 265 | +// Send a message to another application |
| 266 | +api-service> await platformatic.messaging.send('other-service', 'ping', { data: 'test' }) |
| 267 | + |
| 268 | +// Check messaging capabilities |
| 269 | +api-service> typeof platformatic.messaging.send |
| 270 | +'function' |
| 271 | +``` |
| 272 | + |
| 273 | +## Common Debugging Scenarios |
| 274 | + |
| 275 | +### Debugging a 500 Error |
| 276 | + |
| 277 | +When an endpoint returns a 500 error: |
| 278 | + |
| 279 | +```javascript |
| 280 | +// Test the endpoint directly |
| 281 | +api-service> const result = await app.inject({ method: 'GET', url: '/api/problematic' }) |
| 282 | + |
| 283 | +// Check the error |
| 284 | +api-service> JSON.parse(result.body) |
| 285 | +{ statusCode: 500, error: 'Internal Server Error', message: 'Cannot read property x of undefined' } |
| 286 | + |
| 287 | +// Inspect the route handler (if accessible) |
| 288 | +api-service> app.routes |
| 289 | +``` |
| 290 | + |
| 291 | +### Checking Database Connections |
| 292 | + |
| 293 | +```javascript |
| 294 | +// If using @platformatic/sql-mapper |
| 295 | +api-service> app.platformatic.entities |
| 296 | +{ User: [Entity], Post: [Entity] } |
| 297 | + |
| 298 | +// Test a query |
| 299 | +api-service> await app.platformatic.entities.User.find({ limit: 1 }) |
| 300 | +``` |
| 301 | + |
| 302 | +### Memory Inspection |
| 303 | + |
| 304 | +```javascript |
| 305 | +// Get memory usage |
| 306 | +api-service> process.memoryUsage() |
| 307 | +{ |
| 308 | + rss: 85622784, |
| 309 | + heapTotal: 52031488, |
| 310 | + heapUsed: 45123456, |
| 311 | + external: 1234567 |
| 312 | +} |
| 313 | + |
| 314 | +// Force garbage collection (if --expose-gc is enabled) |
| 315 | +api-service> global.gc && global.gc() |
| 316 | +``` |
| 317 | + |
| 318 | +### Checking Event Listeners |
| 319 | + |
| 320 | +```javascript |
| 321 | +// List event listeners |
| 322 | +api-service> app.server.eventNames() |
| 323 | +[ 'connection', 'request', 'close' ] |
| 324 | + |
| 325 | +// Check listener count |
| 326 | +api-service> app.server.listenerCount('request') |
| 327 | +1 |
| 328 | +``` |
| 329 | + |
| 330 | +## Best Practices |
| 331 | + |
| 332 | +### Do's |
| 333 | + |
| 334 | +- **Use for debugging**: The REPL is ideal for investigating issues in development |
| 335 | +- **Test hypotheses**: Try out fixes before implementing them in code |
| 336 | +- **Inspect state**: Examine application state at specific points in time |
| 337 | +- **Learn the API**: Explore Fastify and Platformatic APIs interactively |
| 338 | + |
| 339 | +### Don'ts |
| 340 | + |
| 341 | +- **Avoid in production**: Be cautious using REPL in production environments |
| 342 | +- **Don't modify critical state**: Changing application state can cause instability |
| 343 | +- **Don't expose secrets**: Be careful when inspecting environment variables |
| 344 | +- **Don't leave sessions open**: Exit the REPL when you're done debugging |
| 345 | + |
| 346 | +## Troubleshooting |
| 347 | + |
| 348 | +### "Cannot find a matching runtime" |
| 349 | + |
| 350 | +Make sure your application is running: |
| 351 | + |
| 352 | +```bash |
| 353 | +# Check running applications |
| 354 | +wattpm ps |
| 355 | + |
| 356 | +# Start your application |
| 357 | +wattpm start |
| 358 | +``` |
| 359 | + |
| 360 | +### "Cannot find a matching application" |
| 361 | + |
| 362 | +Verify the application name: |
| 363 | + |
| 364 | +```bash |
| 365 | +# List applications in the runtime |
| 366 | +wattpm applications |
| 367 | + |
| 368 | +# Use the correct application name |
| 369 | +wattpm repl correct-service-name |
| 370 | +``` |
| 371 | + |
| 372 | +### REPL Session Disconnects |
| 373 | + |
| 374 | +The REPL session may disconnect if: |
| 375 | + |
| 376 | +- The application restarts |
| 377 | +- The worker thread crashes |
| 378 | +- Network issues occur (for remote applications) |
| 379 | + |
| 380 | +Simply reconnect with `wattpm repl` after the application is running again. |
| 381 | + |
| 382 | +## Related Documentation |
| 383 | + |
| 384 | +- [CLI Commands Reference](../reference/wattpm/cli-commands.md) - Complete CLI documentation |
| 385 | +- [Profiling with Watt](./profiling-with-watt.md) - CPU and heap profiling |
| 386 | +- [Logging Guide](./logging.md) - Application logging |
| 387 | +- [Metrics Guide](./metrics.md) - Application metrics and monitoring |
0 commit comments