Config, Dependencies, and Services
Learn how to interface with the Fragment user.
The config is the basic contract between the Fragment author and the user. It is used to pass configuration to the Fragment, such as API keys and options. It can also be used to let the user react to events happening in the Fragment by providing callback functions.
Dependencies are objects that are available to route handlers. They are provided using the
withDependencies
method in the library definition. Dependencies are server-side only and are not
included in the client bundle. They are private to the Fragment and cannot be used by the user
directly.
Services, like dependencies, are server-side only and are not included in the client bundle.
However, unlike dependencies, they are available to the user as well as the route handlers. Users
can access them by using the services
object that is available as part of the instantiated
Fragment. This means that services are part of the public-facing API of your Fragment.
Example Snippet
In the following snippet:
- The user is asked to provide an API key, as well as an optional callback function to be called when the text is summarized.
- A dependency is provided called
aiWrapper
. ThewithDependencies
method has access to the config object. Using this to construct objects that require an API key is a common pattern. - A service method called
summarizeText
is provided. ThewithServices
method has access to both the config object and the dependencies. This can be used to make internal methods available to the user. Here, the callback function is called if it is provided.
import { defineLibrary } from "@fragno-dev/core";
import { MyOpenAIWrapper } from "./lib/my-openai-wrapper";
export interface MyFragmentConfig {
openaiApiKey: string;
model?: "gpt-5-mini" | "4o-mini" | "gpt-5-nano";
onTextSummarized?: (input: string, output: string, tokensUsed: number) => void;
}
const myFragmentDefinition = defineLibrary<MyFragmentConfig>("my-fragment")
.withDependencies((config) => {
return {
aiWrapper: new MyOpenAIWrapper({
apiKey: config.openaiApiKey,
model: config.model,
}),
};
})
.withServices((config, deps) => {
return {
summarizeText: (text: string) => {
const result = deps.aiWrapper.summarizeText(text);
config.onTextSummarized?.(text, result.summary, result.tokensUsed);
return result;
},
};
});
Using defineRoutes
to work with Dependencies and Services
The defineRoute
function has a companion, defineRoutes
(note the plural), which is a factory
function that can be used to define multiple routes that have access to the dependencies and
services.
The dependencies and services are scoped to the defineRoutes
call. This means that we don't need
global types for the dependencies and services, and can define them locally instead.
Another benefit of this approach is that we can define routes in a single TypeScript file (like the
post-todos.ts
file in the previous section). This makes the codebase easier to understand and
maintain.
We define two types per defineRoutes
call: one for the dependencies and one for the services.
These types are passed to the call as generic parameters.
import { defineRoute, defineRoutes } from "@fragno-dev/core";
import type { Todo } from "../index";
import type { MyOpenAIWrapper } from "../lib/my-openai-wrapper";
type AddTodoRouteDeps = {
aiWrapper: MyOpenAIWrapper;
};
type AddTodoRouteServices = {
summarizeText: (text: string) => { summary: string; tokensUsed: number };
};
export const addTodoRoute = defineRoutes<AddTodoRouteDeps, AddTodoRouteServices>().create(
({ config, deps, services }) => {
const { aiWrapper } = deps;
const { summarizeText } = services;
return [
/* Calls to `defineRoute` that use internal methods on `aiWrapper` or public methods such
as `summarizeText` from the services. */
];
},
);
The create
callback should return one or more routes. The config, dependencies, and services are
available in the callback.
The addTodoRoute
object is a route factory that will be instantiated by a call to createLibrary
.
At that point, the dependencies and services are passed to the route object.
The createLibrary
function
The createLibrary
function is used to create the library instance. It takes the library
definition, the config, and the routes. The createMyFragment
function is exported to the Fragment
user. This is the final, server-side, object that the user can interact with. It's also the object
that contains the services
field.
import { createLibrary } from "@fragno-dev/core";
import type { FragnoPublicConfig } from "@fragno-dev/core";
import { getTodosRoute } from "./routes/get-todos";
import { addTodoRoute } from "./routes/post-todos";
// ... defineLibrary call we've seen above ...
const routes = [getTodosRoute, addTodoRoute] as const;
export function createMyFragment(config: MyFragmentConfig, fragnoConfig: FragnoPublicConfig = {}) {
return createLibrary(myFragmentDefinition, config, routes, fragnoConfig);
}