Skip to content

Shelf Integration

Shelf is a modular and flexible web server middleware ecosystem for Dart, perfect for building REST APIs with Genkit. The genkit_shelf package provides the shelfHandler function to easily expose individual Genkit flows as Shelf handlers.

Add the genkit_shelf, shelf, and shelf_router packages to your project:

Terminal window
dart pub add genkit_shelf shelf shelf_router

The primary way to use Genkit with Shelf is to mount flow handlers onto a shelf_router. This gives you full control over your server’s routing, middleware, and configuration.

import 'dart:io';
import 'package:genkit/genkit.dart';
import 'package:genkit_shelf/genkit_shelf.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
import 'package:schemantic/schemantic.dart';
// part 'server.g.dart';
void main() async {
final ai = Genkit();
// 1. Define your flow
final helloFlow = ai.defineFlow(
name: 'hello',
fn: (String name, _) async => 'Hello, $name!',
inputSchema: .string(),
outputSchema: .string(),
);
// 2. Create a Shelf Router
final router = Router();
// 3. Mount the flow handler at a specific path
// 'shelfHandler' converts the Flow into a Shelf Handler
router.post('/hello', shelfHandler(helloFlow));
// 4. Add other application routes
router.get('/health', (_) => Response.ok('OK'));
// 5. Create a handler pipeline (e.g., adding logging)
final handler = const Pipeline()
.addMiddleware(logRequests())
.addHandler(router.call);
// 6. Start the server
final server = await io.serve(handler, InternetAddress.anyIPv4, 8080);
print('Server running on http://localhost:${server.port}');
}

You can also serve AI models (and other Genkit actions) using startFlowServer or shelfHandler. This allows you to expose models as web endpoints that can be consumed by other Genkit applications using defineRemoteModel.

import 'dart:io';
import 'package:genkit/genkit.dart';
import 'package:genkit_google_genai/genkit_google_genai.dart';
import 'package:genkit_shelf/genkit_shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
void main() async {
// Initialize your model plugin
final geminiApi = googleAI();
final geminiFlash = geminiApi.model('gemini-2.5-flash');
// Create a Shelf Router
final router = Router();
// Serve the model using shelfHandler
router.post('/googleai/gemini-2.5-flash', shelfHandler(geminiFlash));
final server = await io.serve(router.call, InternetAddress.anyIPv4, 8080);
print('Server running on http://localhost:${server.port}');
}

Run your server using the Dart CLI:

Terminal window
dart run bin/server.dart

You can test your endpoints using curl or any HTTP client.

Terminal window
curl -X POST http://localhost:8080/hello \
-H "Content-Type: application/json" \
-d '{"data": "World"}'

If your flow supports streaming (defines a streamSchema), shelfHandler automatically handles streaming responses when requested.

Terminal window
curl -X POST "http://localhost:8080/count?stream=true" \
-H "Content-Type: application/json" \
-d '{"data": 5}'

You can use standard Shelf middleware to handle authentication, or communicating with Genkit’s context system using FlowWithContextProvider.

The shelfHandler function accepts an optional contextProvider argument. This function allows you to inspect the incoming Shelf Request and populate the Genkit flow context.

import 'package:shelf/shelf.dart';
// ...
final secureFlow = ai.defineFlow(
name: 'secure',
fn: (String input, ctx) async {
final user = ctx.context?['user'];
if (user == null) {
throw GenkitException(
'Unauthorized access',
status: StatusCodes.permissionDenied,
);
}
return 'Secure data for $user: $input';
},
inputSchema: .string(),
outputSchema: .string(),
);
// Directly pass the context provider to shelfHandler
final authenticatedHandler = shelfHandler(
secureFlow,
contextProvider: (Request request) {
final authHeader = request.headers['Authorization'];
if (authHeader == 'Bearer secret') {
return {'user': 'Admin'};
}
return {};
},
);
router.post('/secure', authenticatedHandler);

You can also use standard Shelf middleware (like shelf_auth) to validate credentials before the request reaches the Genkit handler. However, if you need to pass user information into the flow (e.g., user ID), using contextProvider is the recommended bridge.

For quick prototypes or simple services where you don’t need custom routing control, you can use startFlowServer. This helper function creates a Shelf server, sets up routing for all provided flows, and handles CORS automatically.

import 'package:genkit/genkit.dart';
import 'package:genkit_shelf/genkit_shelf.dart';
void main() async {
final ai = Genkit();
final myFlow = ai.defineFlow(...);
await startFlowServer(
flows: [myFlow],
port: 3400,
cors: {
'origin': '*',
},
);
}

Since a Genkit Shelf application is just a standard Dart HTTP server, you can deploy it to any platform that supports Dart or Docker containers, such as Google Cloud Run.

  1. Create a Dockerfile:

    FROM dart:stable AS build
    WORKDIR /app
    COPY pubspec.* ./
    RUN dart pub get
    COPY . .
    RUN dart compile exe bin/server.dart -o bin/server
    FROM scratch
    COPY --from=build /runtime/ /
    COPY --from=build /app/bin/server /app/bin/
    CMD ["/app/bin/server"]
  2. Build and Deploy (e.g., using Cloud Build and Cloud Run).