### Install Schmervice Source: https://github.com/hapipal/schmervice/blob/main/README.md Install the Schmervice package using npm. ```sh npm install @hapipal/schmervice ``` -------------------------------- ### Implement Method-Level Caching with Service.caching Source: https://context7.com/hapipal/schmervice/llms.txt Configure service methods for caching using the static `caching` property or `this.caching()`. This example uses a static declaration for `fetchCoordinates` with a 1-minute TTL and a custom key generator. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); class GeoService extends Schmervice.Service { // Static declaration — applied automatically at instantiation static get caching() { return { fetchCoordinates: { cache: { expiresIn: 60 * 1000, // 1 minute TTL generateTimeout: 5000 // 5 s timeout }, generateKey: (city) => city.toLowerCase().trim() } }; } async fetchCoordinates(city) { // Expensive external API call — will be cached per city const response = await fetch(`https://geocode.example.com/?q=${city}`); return response.json(); } } (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService(GeoService); server.route({ method: 'GET', path: '/geo/{city}', async handler(request) { const { geoService } = request.services(); // Second call for the same city returns cached result return geoService.fetchCoordinates(request.params.city); } }); await server.start(); // caching becomes active after initialization })(); ``` -------------------------------- ### Use Nodemailer as a Schmervice in Hapi Source: https://github.com/hapipal/schmervice/blob/main/README.md Integrates Nodemailer as a named service ('emailService') within a Hapi application. Requires Nodemailer and Hapi to be installed. The `sendmail` transport is configured for testing; check spam folders if emails are not received. ```javascript const Schmervice = require('@hapipal/schmervice'); const Nodemailer = require('nodemailer'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService( Schmervice.withName('emailService', () => { // Sendmail is a simple transport to configure for testing, but if you're // not seeing the sent emails then make sure to check your spam folder. return Nodemailer.createTransport({ sendmail: true }); }) ); server.route({ method: 'get', path: '/email/{toAddress}/{message*}', handler: async (request) => { const { toAddress, message } = request.params; const { emailService } = request.services(); await emailService.sendMail({ from: 'no-reply@yoursite.com', to: toAddress, subject: 'A message for you', text: message }); return { success: true }; } }); await server.start(); console.log(`Start emailing at ${server.info.uri}`); })(); ``` -------------------------------- ### Registering a Service Class Source: https://github.com/hapipal/schmervice/blob/main/API.md Demonstrates how to register a service using a class definition. The service is instantiated immediately upon registration. ```APIDOC ## server.registerService(serviceFactory) ### Description Registers a service with the hapi server. The `serviceFactory` can be a service class, a factory function, a service object, or an array of these. ### Method `server.registerService(serviceFactory)` ### Parameters - **serviceFactory** (Class|Function|Object|Array) - The service or services to register. ### Request Example ```javascript server.registerService( class MyServiceName { constructor(server, options) {} someMethod() {} } ); ``` ``` -------------------------------- ### Registering Multiple Services Source: https://github.com/hapipal/schmervice/blob/main/API.md Demonstrates registering multiple services at once by passing an array of service definitions to `server.registerService()`. ```APIDOC ## server.registerService(serviceFactory) ### Description Registers a service with the hapi server. The `serviceFactory` can be a service class, a factory function, a service object, or an array of these. ### Method `server.registerService(serviceFactory)` ### Parameters - **serviceFactory** (Class|Function|Object|Array) - The service or services to register. ### Request Example ```javascript server.registerService([ class MyServiceName { constructor(server, options) {} someMethod() {} }, { name: 'myOtherServiceName', someOtherMethod: () => {} } ]); ``` ``` -------------------------------- ### Extend Schmervice.Service with Initialize and Teardown Source: https://context7.com/hapipal/schmervice/llms.txt Extend Schmervice.Service to automatically manage database connections. The `initialize` method opens the connection and logs it, while `teardown` closes it. These are automatically registered as server extensions. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); class DatabaseService extends Schmervice.Service { async initialize() { // Called automatically when server.initialize() / server.start() runs this.connection = await openDatabaseConnection(this.options.dbUrl); this.server.log(['db'], 'Database connected'); } async teardown() { // Called automatically when server.stop() runs await this.connection.close(); this.server.log(['db'], 'Database disconnected'); } async findUser(id) { return this.connection.query('SELECT * FROM users WHERE id = $1', [id]); } } (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService(DatabaseService); server.route({ method: 'GET', path: '/users/{id}', async handler(request) { const { databaseService } = request.services(); return databaseService.findUser(request.params.id); } }); await server.start(); // triggers DatabaseService.initialize() // ... await server.stop(); // triggers DatabaseService.teardown() })(); ``` -------------------------------- ### service.initialize() Source: https://github.com/hapipal/schmervice/blob/main/API.md An asynchronous method intended for service initialization. It is called during server startup via `onPreStart` if implemented by an extending class. ```APIDOC ## `async service.initialize()` ### Description This is not implemented on the base service class, but when it is implemented by an extending class then it will be called during `server` initialization (via `onPreStart` [server extension](https://github.com/hapijs/hapi/blob/master/API.md#server.ext()) added when the service is instanced). ### Method `initialize()` ### Parameters None ``` -------------------------------- ### Registering a Service Factory Function Source: https://github.com/hapipal/schmervice/blob/main/API.md Shows how to register a service using a factory function that returns a service object. The factory is called immediately to create the service. ```APIDOC ## server.registerService(serviceFactory) ### Description Registers a service with the hapi server. The `serviceFactory` can be a service class, a factory function, a service object, or an array of these. ### Method `server.registerService(serviceFactory)` ### Parameters - **serviceFactory** (Class|Function|Object|Array) - The service or services to register. ### Request Example ```javascript server.registerService((server, options) => ({ name: 'myServiceName', someMethod: () => {} })); ``` ``` -------------------------------- ### Register and Use a Class-Based Service in Hapi Source: https://github.com/hapipal/schmervice/blob/main/README.md Demonstrates registering a class-based service with Hapi and accessing it in a route handler. Ensure Hapi and Schmervice are registered before defining routes. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService( class MathService extends Schmervice.Service { add(x, y) { this.server.log(['math-service'], 'Adding'); return Number(x) + Number(y); } multiply(x, y) { this.server.log(['math-service'], 'Multiplying'); return Number(x) * Number(y); } } ); server.route({ method: 'get', path: '/add/{a}/{b}', handler: (request) => { const { a, b } = request.params; const { mathService } = request.services(); return mathService.add(a, b); } }); await server.start(); console.log(`Start adding at ${server.info.uri}`); })(); ``` -------------------------------- ### Registering a Service Object Source: https://github.com/hapipal/schmervice/blob/main/API.md Illustrates registering a service directly as an object. The object must have a `name` property or a `Schmervice.name` symbol for identification. ```APIDOC ## server.registerService(serviceFactory) ### Description Registers a service with the hapi server. The `serviceFactory` can be a service class, a factory function, a service object, or an array of these. ### Method `server.registerService(serviceFactory)` ### Parameters - **serviceFactory** (Class|Function|Object|Array) - The service or services to register. ### Request Example ```javascript server.registerService({ name: 'myServiceName', someMethod: () => {} }); ``` ``` -------------------------------- ### Bind All Service Methods to Instance with service.bind() Source: https://context7.com/hapipal/schmervice/llms.txt Use `notificationService.bind()` to create a proxy where all methods are pre-bound to the service instance. This allows safe destructuring of methods without losing the `this` context, as shown in the handler. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); class NotificationService extends Schmervice.Service { send(message) { this.server.log(['notify'], message); return { sent: true, message }; } } const server = Hapi.server(); await server.register(Schmervice); server.registerService(NotificationService); server.route({ method: 'POST', path: '/notify', handler(request) { const { notificationService } = request.services(); // Destructure safely — 'this' inside send() still points to the service const { send } = notificationService.bind(); return send(request.payload.message); // { sent: true, message: '...' } } }); ``` -------------------------------- ### Register Services with Hapi Server Source: https://context7.com/hapipal/schmervice/llms.txt Register one or more services with the server using `server.registerService()`. Services can be defined as classes, factory functions, or plain objects. Services are instantiated immediately and receive `(server, options)` as arguments. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); // 1. Class style — name derived from class name, camel-cased to 'userService' server.registerService( class UserService extends Schmervice.Service { async findById(id) { // e.g. query a database return { id, name: 'Alice' }; } } ); // 2. Factory function style — name must be supplied explicitly server.registerService((srv, opts) => ({ name: 'ConfigService', get(key) { return opts[key] ?? null; } })); // 3. Plain object style server.registerService({ name: 'ConstantsService', maxRetries: 3 }); // 4. Array of mixed types server.registerService([ class OrderService extends Schmervice.Service { create(payload) { return { id: 1, ...payload }; } }, { name: 'HealthService', ping: () => 'pong' } ]); const { userService, configService, constantsService } = server.services(); console.log(await userService.findById(42)); // { id: 42, name: 'Alice' } })(); ``` -------------------------------- ### Register a service Source: https://context7.com/hapipal/schmervice/llms.txt Registers one or more services with the server. Services can be defined as classes, plain objects, factory functions, or arrays of these. Services are instantiated immediately. ```APIDOC ## server.registerService(serviceFactory) — Register a service Registers one or more services with the server. `serviceFactory` can be a class, a plain object, a factory function, or an array of any of these. Services are instantiated immediately; classes receive `(server, options)` as constructor arguments, factory functions receive the same arguments and must return a service object. ```js const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); // 1. Class style — name derived from class name, camel-cased to 'userService' server.registerService( class UserService extends Schmervice.Service { async findById(id) { // e.g. query a database return { id, name: 'Alice' }; } } ); // 2. Factory function style — name must be supplied explicitly server.registerService((srv, opts) => ({ name: 'ConfigService', get(key) { return opts[key] ?? null; } })); // 3. Plain object style server.registerService({ name: 'ConstantsService', maxRetries: 3 }); // 4. Array of mixed types server.registerService([ class OrderService extends Schmervice.Service { create(payload) { return { id: 1, ...payload }; } }, { name: 'HealthService', ping: () => 'pong' } ]); const { userService, configService, constantsService } = server.services(); console.log(await userService.findById(42)); // { id: 42, name: 'Alice' } })(); ``` ``` -------------------------------- ### service.bind() Source: https://github.com/hapipal/schmervice/blob/main/API.md Returns a new service instance with all methods bound to the service instance. This allows for deconstruction of methods without losing the `this` context. ```APIDOC ## `service.bind()` ### Description Returns a new service instance where all methods are bound to the service instance allowing you to deconstruct methods without losing the `this` context. ### Method `bind()` ### Parameters None ``` -------------------------------- ### Access Services from the hapi Response Toolkit Source: https://github.com/hapipal/schmervice/blob/main/API.md Retrieve service instances available within the context of the response toolkit. This is equivalent to calling `server.services()` on the server associated with the route or extension. Services are keyed by their instance names. ```javascript h.services() // or with namespace h.services('pluginName') ``` -------------------------------- ### Retrieve Services from Server, Request, or Toolkit Source: https://context7.com/hapipal/schmervice/llms.txt Retrieve service instances using `server.services()`, `request.services()`, or `h.services()`. Services can be retrieved scoped to the current plugin realm, server-wide, or within a specific plugin's namespace. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService( class MathService extends Schmervice.Service { add(x, y) { return Number(x) + Number(y); } multiply(x, y) { return Number(x) * Number(y); } } ); server.route({ method: 'GET', path: '/add/{a}/{b}', handler(request) { // Access services scoped to this route's plugin realm const { mathService } = request.services(); return mathService.add(request.params.a, request.params.b); } }); server.route({ method: 'GET', path: '/all-services', handler(request, h) { // Access all server-wide non-sandboxed services const all = h.services(true); return Object.keys(all); } }); await server.start(); // GET /add/3/4 → 7 // GET /all-services → ['mathService'] })(); ``` -------------------------------- ### Register the plugin Source: https://context7.com/hapipal/schmervice/llms.txt Registers Schmervice as a hapi plugin, decorating the server, request, and response toolkit with `registerService()` and `services()`. This plugin is safe to register multiple times. ```APIDOC ## await server.register(Schmervice) — Register the plugin Registers Schmervice as a hapi plugin, decorating `server`, `request`, and `h` (response toolkit) with `registerService()` and `services()`. The plugin is safe to register multiple times (e.g. from multiple sub-plugins). ```js const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); const server = Hapi.server(); await server.register(Schmervice); // server.registerService and server.services are now available ``` ``` -------------------------------- ### Register a Service Class with hapi Server Source: https://github.com/hapipal/schmervice/blob/main/API.md Register a service using its class definition. The service is instantiated immediately with the server and plugin options. Ensure the class is named appropriately for service discovery. ```javascript server.registerService( class MyServiceName { constructor(server, options) {} someMethod() {} } ); ``` -------------------------------- ### Register and Use a Functional Service in Hapi Source: https://github.com/hapipal/schmervice/blob/main/README.md Shows how to register a service using a functional style with Hapi, utilizing the Schmervice.name symbol for identification. This approach is an alternative to class-based services. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService( (srv) => ({ [Schmervice.name]: 'mathService', add: (x, y) => { srv.log(['math-service'], 'Adding'); return Number(x) + Number(y); }, multiply: (x, y) => { srv.log(['math-service'], 'Multiplying'); return Number(x) * Number(y); } }) ); server.route({ method: 'get', path: '/add/{a}/{b}', handler: (request) => { const { a, b } = request.params; const { mathService } = request.services(); return mathService.add(a, b); } }); await server.start(); console.log(`Start adding at ${server.info.uri}`); })(); ``` -------------------------------- ### Access Services from the hapi Server Source: https://github.com/hapipal/schmervice/blob/main/API.md Retrieve all non-sandboxed service instances registered with the server or its ancestors. Services are keyed by their instance names. An optional namespace can be provided to access services from a specific plugin's perspective. ```javascript server.services() // or server.services(true) // Access services visible to the root server // or server.services('pluginName') // Access services visible within 'pluginName' ``` -------------------------------- ### Accessing Services via Server Source: https://github.com/hapipal/schmervice/blob/main/API.md Retrieves an object containing all registered service instances, keyed by their instance names. Optionally, a namespace can be provided to access services from a specific plugin's perspective. ```APIDOC ## server.services([namespace]) ### Description Returns an object containing each service instance keyed by their instance names. Services available are those registered by the current server or its ancestors, excluding sandboxed services. ### Method `server.services([namespace])` ### Parameters - **namespace** (string|boolean) - Optional. If a string, returns services visible within the plugin named `namespace`. If `true`, returns all non-sandboxed services visible to the root server. ### Response Example ```javascript // Assuming 'myServiceName' and 'myOtherServiceName' were registered const services = server.services(); console.log(services.myServiceName); console.log(services.myOtherServiceName); ``` ``` -------------------------------- ### Register Service with `Schmervice.withName` Helper Source: https://github.com/hapipal/schmervice/blob/main/API.md Use `Schmervice.withName` to assign a name to a service, especially useful for service objects from other libraries to prevent property conflicts. It can also accept options like `sandbox`. ```javascript server.registerService( Schmervice.withName('myServiceName', () => ({ someMethod: () => {} })) ); // ... const { myServiceName } = server.services(); ``` ```javascript const Nodemailer = require('nodemailer'); const transport = Nodemailer.createTransport(); server.registerService(Schmervice.withName('emailService', transport)); // ... const { emailService } = server.services(); ``` ```javascript server.registerService( Schmervice.withName('privateService', { sandbox: true }, { someMethod: () => {} }) ); // ... // Can access the service in the same plugin that registered it const { privateService } = server.services(); // But cannot access it in other namespaces, e.g. the root namespace, because it is sandboxed const { privateService: doesNotExist } = server.services(true); ``` -------------------------------- ### Register a Service Factory Function with hapi Server Source: https://github.com/hapipal/schmervice/blob/main/API.md Register a service by providing a factory function that returns a service object. The factory is called immediately with server and plugin options. The service object should have a 'name' property or use Schmervice.name for identification. ```javascript server.registerService((server, options) => ({ name: 'myServiceName', someMethod: () => {} })); ``` -------------------------------- ### Accessing Services via Response Toolkit Source: https://github.com/hapipal/schmervice/blob/main/API.md Allows access to services from the `h` (response toolkit) object, based on the realm in which the route or server extension was declared. ```APIDOC ## h.services([namespace]) ### Description Returns an object containing each service instance keyed by their instance names, based on the `h.realm`. This is equivalent to calling `server.services()` on the server associated with the route or extension. ### Method `h.services([namespace])` ### Parameters - **namespace** (string|boolean) - Optional. See [`server.services()`](#serverservicesnamespace) for details. ### Response Example ```javascript // Inside a handler or extension h.services().myServiceName.someMethod(); ``` ``` -------------------------------- ### Wrapping Libraries with `Schmervice.withName` Source: https://context7.com/hapipal/schmervice/llms.txt Use `Schmervice.withName` to assign a name and optional sandbox setting to existing classes, objects, or factory functions. This is ideal for integrating third-party libraries as Schmervice services without altering their original code. ```javascript const Schmervice = require('@hapipal/schmervice'); const Nodemailer = require('nodemailer'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); // Wrap an existing library with a name (and optional sandbox) server.registerService( Schmervice.withName('emailService', { sandbox: false }, () => Nodemailer.createTransport({ sendmail: true }) ) ); // Apply a name to a plain factory returning an async object server.registerService( Schmervice.withName('tokenService', async (srv) => { const secret = await loadSecretFromVault(); return { sign: (payload) => jwt.sign(payload, secret), verify: (token) => jwt.verify(token, secret) }; }) ); server.route({ method: 'POST', path: '/send-email', async handler(request) { const { emailService } = request.services(); await emailService.sendMail({ from: 'noreply@example.com', to: request.payload.to, subject: 'Hello', text: request.payload.body }); return { success: true }; } }); await server.start(); })(); ``` -------------------------------- ### Sandboxed Services with `Schmervice.sandbox` Source: https://context7.com/hapipal/schmervice/llms.txt Control service visibility across hapi plugins using `[Schmervice.sandbox]`. Set to `true` or `'plugin'` for plugin-local access, or `false` or `'server'` (default) for root server access. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); const server = Hapi.server(); await server.register(Schmervice); const myPlugin = { name: 'my-plugin', register(srv) { // This service is private to my-plugin srv.registerService({ [Schmervice.name]: 'internalCache', [Schmervice.sandbox]: true, store: new Map(), get(key) { return this.store.get(key); }, set(key, val) { this.store.set(key, val); } }); // This service is visible to the root server too srv.registerService({ [Schmervice.name]: 'publicApi', [Schmervice.sandbox]: false, version: () => '1.0.0' }); } }; await server.register(myPlugin); console.log(Object.keys(server.services())); // ['publicApi'] console.log(Object.keys(server.services('my-plugin'))); // ['internalCache', 'publicApi'] ``` -------------------------------- ### Accessing Services via Request Source: https://github.com/hapipal/schmervice/blob/main/API.md Provides access to services from the perspective of the request's route declaration. This is equivalent to calling `server.services()` on the server where the request's route was declared. ```APIDOC ## request.services([namespace]) ### Description Returns an object containing each service instance keyed by their instance names, based on the `request.route.realm`. This is equivalent to calling `server.services()` on the server associated with the request's route. ### Method `request.services([namespace])` ### Parameters - **namespace** (string|boolean) - Optional. See [`server.services()`](#serverservicesnamespace) for details. ### Response Example ```javascript // Inside a request handler const services = request.services(); console.log(services.myServiceName); ``` ``` -------------------------------- ### service.caching(options) Source: https://github.com/hapipal/schmervice/blob/main/API.md Configures caching for the service's methods. This method can only be called once. The `options` object maps method names to their caching configurations. ```APIDOC ## `service.caching(options)` ### Description Configures caching for the service's methods, and may be called once. The `options` argument should be an object where each key is the name of one of the service's methods, and each corresponding value is either: - An object `{ cache, generateKey }` as detailed in the [server method options](https://github.com/hapijs/hapi/blob/master/API.md#server.method()) documentation. - An object containing the `cache` options as detailed in the [server method options](https://github.com/hapijs/hapi/blob/master/API.md#server.method()) documentation. Note that behind the scenes an actual server method will be created on `service.server` and will replace the respective method on the service instance, which means that any service method configured for caching must be called asynchronously even if its original implementation is synchronous. In order to configure caching, the service class also must have a `name`, e.g. `class MyServiceName extends Schmervice.Service {}`. ### Method caching(options) ### Parameters #### Request Body - **options** (object) - Required - An object where keys are method names and values are caching configurations. - **cache** (object) - Optional - Cache configuration options. - **generateKey** (function) - Optional - Function to generate cache keys. ``` -------------------------------- ### Access Services from the hapi Request Source: https://github.com/hapipal/schmervice/blob/main/API.md Retrieve service instances available to the request's route. This is equivalent to calling `server.services()` on the server associated with the request's realm. Services are keyed by their instance names. ```javascript request.services() // or with namespace request.services('pluginName') ``` -------------------------------- ### Register Schmervice Plugin with Hapi Server Source: https://context7.com/hapipal/schmervice/llms.txt Register Schmervice as a hapi plugin to enable service registration and retrieval. This decorates the server, request, and response toolkit with `registerService()` and `services()`. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); const server = Hapi.server(); await server.register(Schmervice); // server.registerService and server.services are now available ``` -------------------------------- ### Register Multiple Services with hapi Server Source: https://github.com/hapipal/schmervice/blob/main/API.md Register an array containing multiple service definitions (classes, factory functions, or objects) at once. This is useful for registering several services in a single call. ```javascript server.registerService([ class MyServiceName { constructor(server, options) {} someMethod() {} }, { name: 'myOtherServiceName', someOtherMethod: () => {} } ]); ``` -------------------------------- ### Retrieve services Source: https://context7.com/hapipal/schmervice/llms.txt Retrieves service instances keyed by their camel-cased names. Services can be accessed from `server`, `request`, and the response toolkit `h`. The scope of retrieved services can be controlled by providing a namespace argument. ```APIDOC ## server.services([namespace]) / request.services([namespace]) / h.services([namespace]) — Retrieve services Returns an object of service instances keyed by their camel-cased names. Available on `server`, `request`, and the response toolkit `h`. When called with no argument, returns services visible in the current plugin's realm. When called with `true`, returns all non-sandboxed services registered server-wide. When called with a string, returns services visible within the named plugin's namespace. ```js const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService( class MathService extends Schmervice.Service { add(x, y) { return Number(x) + Number(y); } multiply(x, y) { return Number(x) * Number(y); } } ); server.route({ method: 'GET', path: '/add/{a}/{b}', handler(request) { // Access services scoped to this route's plugin realm const { mathService } = request.services(); return mathService.add(request.params.a, request.params.b); } }); server.route({ method: 'GET', path: '/all-services', handler(request, h) { // Access all server-wide non-sandboxed services const all = h.services(true); return Object.keys(all); } }); await server.start(); // GET /add/3/4 → 7 // GET /all-services → ['mathService'] })(); ``` ``` -------------------------------- ### Schmervice.Service Source: https://github.com/hapipal/schmervice/blob/main/API.md The base class for creating services. It provides access to the Hapi server and options, and manages context. ```APIDOC ## `Schmervice.Service` This class is intended to be used as a base class for services registered with schmervice. However, it is completely reasonable to use this class independently of the [schmervice plugin](#the-hapi-plugin) if desired. ### `new Service(server, options)` Constructor to create a new service instance. `server` should be a hapi plugin's server or root server, and `options` should be the corresponding plugin `options`. This is intended to mirror a plugin's [registration function](https://github.com/hapijs/hapi/blob/master/API.md#plugins) `register(server, options)`. Note: creating a service instance may have side-effects on the `server`, e.g. adding server extensions– keep reading for details. ### `service.server` The `server` passed to the constructor. Should be a hapi plugin's server or root server. ### `service.options` The hapi plugin `options` passed to the constructor. ### `service.context` The context of `service.server` set using [`server.bind()`](https://github.com/hapijs/hapi/blob/master/API.md#server.bind()). Will be `null` if no context has been set. This is implemented lazily as a getter based upon `service.server` so that services can be part of the context without introducing any circular dependencies between the two. ``` -------------------------------- ### Register Sandboxed Service Source: https://github.com/hapipal/schmervice/blob/main/API.md Set `Schmervice.sandbox` to `true` or `'plugin'` to make a service private to the registering plugin's namespace. The default behavior is to make services available in all namespaces up to the root server. ```javascript server.registerService({ [Schmervice.name]: 'privateService', [Schmervice.sandbox]: true, someMethod: () => {} }); // ... // Can access the service in the same plugin that registered it const { privateService } = server.services(); // But cannot access it in other namespaces, e.g. the root namespace, because it is sandboxed const { privateService: doesNotExist } = server.services(true); ``` -------------------------------- ### Register a Service Object with hapi Server Source: https://github.com/hapipal/schmervice/blob/main/API.md Register a pre-defined service object directly. The object must include a 'name' property or use Schmervice.name for identification. This is a straightforward way to add a service. ```javascript server.registerService({ name: 'myServiceName', someMethod: () => {} }); ``` -------------------------------- ### Schmervice.withName(name, [options], serviceFactory) Source: https://github.com/hapipal/schmervice/blob/main/API.md A helper function to assign a name and optional sandbox options to a service created by a factory. It mutates service classes/objects or wraps factory functions. ```APIDOC ## `Schmervice.withName(name, [options], serviceFactory)` This is a helper that assigns `name` to the service instance or object produced by `serviceFactory` by setting the service's [`Schmervice.name`](#schmervicename). When `serviceFactory` is a service class or object, `Schmervice.withName()` returns the same service class or object mutated with `Schmervice.name` set accordingly. When `serviceFactory` is a function, this helper returns a new function that behaves identically but adds the `Schmervice.name` property to its result. If the resulting service class or object already has a `Schmervice.name` then this helper will fail. Following a similar logic and behavior to the above: when `options` is present, this helper also assigns `options.sandbox` to the service instance or object produced by `serviceFactory` by setting the service's [`Schmervice.sandbox`](#schmervicesandbox). If the resulting service class or object already has a `Schmervice.sandbox` then this helper will fail. ```js server.registerService( Schmervice.withName('myServiceName', () => ({ someMethod: () => {} })) ); // ... const { myServiceName } = server.services(); ``` This is also the preferred way to name a service object from some other library, since it prevents property conflicts. ```js const Nodemailer = require('nodemailer'); const transport = Nodemailer.createTransport(); server.registerService(Schmervice.withName('emailService', transport)); // ... const { emailService } = server.services(); ``` An example usage of `options.sandbox`: ```js server.registerService( Schmervice.withName('privateService', { sandbox: true }, { someMethod: () => {} }) ); // ... // Can access the service in the same plugin that registered it const { privateService } = server.services(); // But cannot access it in other namespaces, e.g. the root namespace, because it is sandboxed const { privateService: doesNotExist } = server.services(true); ``` ``` -------------------------------- ### Register Service with Custom Name Source: https://github.com/hapipal/schmervice/blob/main/API.md Use `Schmervice.name` to assign a literal string name to a service, overriding its default name. This is preferred for explicit service identification. ```javascript server.registerService({ [Schmervice.name]: 'myServiceName', someMethod: () => {} }); // ... const { myServiceName } = server.services(); ``` -------------------------------- ### Schmervice.sandbox Source: https://github.com/hapipal/schmervice/blob/main/API.md A symbol to control the scope of a service. When set to `true` or `'plugin'`, the service is only available within the registering plugin's namespace. `false` or `'server'` makes it available more broadly. ```APIDOC ## `Schmervice.sandbox` This is a symbol that can be added as a property to either a service class or service object. When the value of this property is `true` or `'plugin'`, then the service is not available to [`server.services()`](#serverservicesnamespace) for any namespace aside from that of the plugin that registered the service. This effectively makes the service "private" within the plugin that it is registered. The default behavior, which can also be declared explicitly by setting this property to `false` or `'server'`, makes the service available within the current plugin's namespace, and all of the namespaces of that plugin's ancestors up to and including the root server (i.e. the namespace accessed by `server.services(true)`). ```js server.registerService({ [Schmervice.name]: 'privateService', [Schmervice.sandbox]: true, someMethod: () => {} }); // ... // Can access the service in the same plugin that registered it const { privateService } = server.services(); // But cannot access it in other namespaces, e.g. the root namespace, because it is sandboxed const { privateService: doesNotExist } = server.services(true); ``` ``` -------------------------------- ### service.teardown() Source: https://github.com/hapipal/schmervice/blob/main/API.md An asynchronous method intended for service teardown. It is called during server stop via `onPostStop` if implemented by an extending class. ```APIDOC ## `async service.teardown()` ### Description This is not implemented on the base service class, but when it is implemented by an extending class then it will be called during `server` stop (via `onPostStop` [server extension](https://github.com/hapijs/hapi/blob/master/API.md#server.ext()) added when the service is instanced). ### Method `teardown()` ### Parameters None ``` -------------------------------- ### Service.caching Source: https://github.com/hapipal/schmervice/blob/main/API.md A static property on the Service class that can be used to configure service method caching when the service is instantiated. ```APIDOC ## `Service.caching` ### Description This is not set on the base service class, but when an extending class has a static `caching` property (or getter) then its value will be used used to configure service method caching (via [`service.caching()`](#servicecachingoptions) when the service is instanced). ### Property `caching` (static property or getter) ### Parameters None ``` -------------------------------- ### Access Server Context with `service.context` Source: https://context7.com/hapipal/schmervice/llms.txt Use `this.context` within a service to access the object bound to the server via `server.bind()`. This is useful for sharing server-level data across services without creating circular dependencies. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); class AuditService extends Schmervice.Service { log(event) { // this.context is whatever was passed to server.bind() const { db } = this.context; return db.insert('audit_log', { event, ts: Date.now() }); } } const server = Hapi.server(); server.bind({ db: myDatabaseClient }); // set shared context await server.register(Schmervice); server.registerService(AuditService); const { auditService } = server.services(); // auditService.context === { db: myDatabaseClient } ``` -------------------------------- ### Declare Merging for Typed Services in TypeScript Source: https://context7.com/hapipal/schmervice/llms.txt Extend the global `RegisteredServices` interface to provide TypeScript with type information for your registered services. This ensures that `server.services()` and `request.services()` return correctly typed service instances without runtime overhead. ```typescript import Schmervice, { Service, ServiceObject } from '@hapipal/schmervice'; import Hapi from '@hapi/hapi'; import NodeMailer from 'nodemailer'; // Extend the global interface so server.services() / request.services() are typed declare module '@hapipal/schmervice' { interface RegisteredServices { userService: UserService; mailer: ServiceObject; } } class UserService extends Service { async findById(id: number) { return { id, name: 'Alice' }; } } (async () => { const server = Hapi.server(); await server.register(Schmervice); server.registerService(UserService); server.registerService( Schmervice.withName('mailer', () => NodeMailer.createTransport({ sendmail: true })) ); server.route({ method: 'GET', path: '/user/{id}', async handler(request) { const { userService } = request.services(); // typed as UserService return userService.findById(Number(request.params.id)); } }); await server.start(); })(); ``` -------------------------------- ### Literal Service Naming with `Schmervice.name` Source: https://context7.com/hapipal/schmervice/llms.txt Override default camel-casing for service names by setting the static `[Schmervice.name]` property on a class or using it as a key for object services. This ensures the service is accessed using the exact string value provided. ```javascript const Schmervice = require('@hapipal/schmervice'); const Hapi = require('@hapi/hapi'); const server = Hapi.server(); await server.register(Schmervice); // Class with literal name class MyService extends Schmervice.Service { static get [Schmervice.name]() { return 'my-exact-service-name'; } hello() { return 'hi'; } } // Object with literal name server.registerService({ [Schmervice.name]: 'raw_object_service', greet: () => 'hello' }); server.registerService(MyService); const services = server.services(); console.log(services['my-exact-service-name'].hello()); // 'hi' console.log(services['raw_object_service'].greet()); // 'hello' ``` -------------------------------- ### Schmervice.name Source: https://github.com/hapipal/schmervice/blob/main/API.md A symbol used to assign a literal string name to a service, overriding its natural name. This is preferred for service identification. ```APIDOC ## `Schmervice.name` This is a symbol that can be added as a property to either a service class or service object. Its value should be a string, and this value will be taken literally as the service's name without any camel-casing. A service class or object's `Schmervice.name` property is always preferred to its natural class name or `name` property, so this property can be used as an override. ```js server.registerService({ [Schmervice.name]: 'myServiceName', someMethod: () => {} }); // ... const { myServiceName } = server.services(); ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.