Services
Services
Services in Raindrop are serverless functions that can be accessed via public
URLs or internal service bindings. Each service defined in your
raindrop.manifest
creates a separate serverless function instance. Services
are the fundamental building blocks of your application, allowing you to break
down complex systems into manageable, independent components.
Service Types
Public Services
Public services expose HTTP endpoints that can be accessed through unique URLs. These services act as the entry points to your application, handling external requests and managing public-facing functionality. They’re particularly useful for creating APIs, handling webhooks, or serving content to external clients.
Configuration
To create a public service, add the following configuration to your
raindrop.manifest
:
service "example-service" { domain { cname = "my-unique-service" }}
The cname
field serves as a unique identifier for your service and is required
for all public services. When deployed, your service will be accessible at
<cname>.<org-id>.lmapp.run
.
Service Bindings
Service bindings enable direct communication between services within your application. Unlike public services that communicate over HTTP, service bindings provide a more efficient and secure way for services to interact with each other. They eliminate the need for services to expose public endpoints just to communicate with other parts of your application.
Implementation Example
Below a practical example showing how service bindings work between a public-facing service and an internal service:
application "demo" { service "service-a" { domain { cname="service-a" } } service "service-b" {}}
import { Service } from '@liquidmetal-ai/raindrop-framework';import { Env } from './raindrop.gen';
export default class extends Service<Env> { async fetch(request: Request): Promise<Response> { const env: Env = this.env; try { /* we call the process method of service-b ensure you await the response. Without awaiting the response, service-b would abort the request */ const response = await env.SERVICE_B.process({ "hello": "world" }) return response } catch (e) { console.log(e) return new Response(`Error: ${e}`, { status: 500 }); } }}
import { Service } from '@liquidmetal-ai/raindrop-framework';import { Env } from './raindrop.gen';
export default class extends Service<Env> {
// each service must have a fetch method even if its not used async fetch(request: Request): Promise<Response> { return new Response("not found", { status: 404 }); }
// we create an additional method for other services to call async process(input: any): Promise<Response> { return new Response("Success", { status: 200 }); }}
In this example, Service A exposes a public endpoint and uses a service binding to communicate with Service B. Service B doesn’t need a public endpoint because it’s only accessed internally through the binding.
Key Points About Service Bindings
- Await Required: All service binding calls must be awaited. If not awaited, the service call will be aborted before completion.
- Available Methods: You can call any custom method defined in the service
class through bindings, except for the
fetch
method which is reserved for HTTP requests. - Internal Services: Services without a
cname
are internal-only but still require afetch
method implementation, even though it won’t be directly accessible.