@minimalstuff/adonis-waiting
v0.1.4
Published
AdonisJS v7 package — wait for external services before serving requests
Readme
@minimalstuff/adonis-waiting
AdonisJS v7 package that intercepts incoming requests while external services are not yet ready, redirects to a waiting route, and lets you through once they are.
The package handles the detection logic. Rendering the waiting page is entirely up to you — Edge, Inertia, or anything else.
How it works
- On boot, the provider starts a background interval that runs all registered checkers
- The middleware intercepts every request — if not ready, redirects to the configured waiting route (or returns
503for non-HTML requests) - Your controller receives
readyandredirect— render however you want, implement polling or SSE as needed
Installation
node ace configure @minimalstuff/adonis-waitingThis will:
- Publish
config/waiting.ts - Generate
app/waiting/example_checker.ts - Generate
app/waiting/waiting_controller.ts(stub to fill in) - Register the provider in
adonisrc.ts - Print instructions for middleware and route wiring
Manual setup
Middleware
Add to the router middleware stack in start/kernel.ts:
router.use([
() => import('@adonisjs/core/bodyparser_middleware'),
() => import('@minimalstuff/adonis-waiting/middleware'),
]);Route
Register one route pointing to your controller in start/routes.ts:
router.get('/waiting', [WaitingController, 'render']);Writing the waiting controller
The generated stub at app/waiting/waiting_controller.ts gives you ready and redirect. Implement rendering as you see fit:
// Inertia — pairs with usePolling(2000) on the client
async render({ request, inertia }: HttpContext) {
return inertia.render('waiting/index', {
ready: this.waitingService.isReady(),
redirect: request.input('redirect', '/'),
})
}
// Edge
async render({ request, view }: HttpContext) {
return view.render('waiting/index', {
ready: this.waitingService.isReady(),
redirect: request.input('redirect', '/'),
})
}Inertia polling example
// resources/pages/waiting/index.tsx
import { router, usePage } from '@inertiajs/react';
import { useEffect } from 'react';
export default function WaitingPage() {
const { ready, redirect } = usePage<{ ready: boolean; redirect: string }>()
.props;
useEffect(() => {
if (ready) router.visit(redirect);
}, [ready, redirect]);
usePolling(2000);
return <p>Starting up…</p>;
}Writing a checker
A checker is any class that implements ServiceChecker:
import type { ServiceChecker } from '@minimalstuff/adonis-waiting';
export class RedisChecker implements ServiceChecker {
readonly name = 'redis';
async check(): Promise<boolean> {
try {
await redis.ping();
return true;
} catch {
return false;
}
}
}The check() method is called every checkInterval ms. Return true when ready, false otherwise. Thrown exceptions are treated as false.
A checker can wrap anything: a database connection, an HTTP health endpoint, a file on disk, a CLI command — anything expressible as an async boolean.
Configuration
// config/waiting.ts
import { defineConfig } from '@minimalstuff/adonis-waiting';
import { RedisChecker } from '#waiting/redis_checker';
import { DatabaseChecker } from '#waiting/database_checker';
export default defineConfig({
checkers: [new RedisChecker(), new DatabaseChecker()],
checkInterval: 2_000, // ms between each check cycle, default: 2000
waitingRoute: '/waiting', // route the middleware redirects to, default: '/waiting'
});All checkers must return true in the same cycle for the app to be considered ready.
API reference
ServiceChecker
interface ServiceChecker {
readonly name: string;
check(): Promise<boolean>;
}defineConfig(config: WaitingConfig): WaitingConfig
Type-safe config helper.
WaitingService
Singleton registered in the container. Inject it wherever you need to check readiness state.
import { inject } from '@adonisjs/core';
import { WaitingService } from '@minimalstuff/adonis-waiting';
@inject()
export class WaitingController {
constructor(private readonly waitingService: WaitingService) {}
}| Method | Returns | Description |
| ------------------- | --------- | --------------------------------------------- |
| isReady() | boolean | Whether all checkers passed on the last cycle |
| getWaitingRoute() | string | Configured waiting route |
WaitingMiddleware
Router middleware. Intercepts requests when not ready:
- HTML requests → redirect to
{waitingRoute}?redirect={originalUrl} - Non-HTML requests →
503 { error: 'E_SERVICE_UNAVAILABLE', retryAfter: 2 }
Behaviour notes
- If no checkers are configured, the service is considered ready immediately
- The first check runs at boot — there is a brief window before the first cycle completes where
isReady()returnsfalse - The middleware does not redirect requests whose URL starts with the configured
waitingRoute(avoids redirect loops) - The
redirectquery parameter is your responsibility to sanitise in the controller before passing to the renderer
