SDK Utilities for External Apps
Use storage, logging, error handling, and schema utilities from external backends like Next.js or Express
Use @agentuity/server and @agentuity/core utilities in external apps, scripts, or backends that integrate with Agentuity.
Storage Access
Access Agentuity storage (KV, Vector, Stream) directly from your Next.js or Express backend using your SDK key.
Add these environment variables to your .env:
AGENTUITY_SDK_KEY=your_sdk_key # From Agentuity console or project .env
AGENTUITY_REGION=use # From agentuity.json (region field)Finding Your Region
Your region is in your Agentuity project's agentuity.json file. If you forget to set AGENTUITY_REGION, the SDK throws a helpful error telling you what's missing.
import {
KeyValueStorageService,
VectorStorageService,
StreamStorageService,
} from '@agentuity/core';
import { createServerFetchAdapter, getServiceUrls, createLogger } from '@agentuity/server';
const logger = createLogger('info');
const adapter = createServerFetchAdapter({
headers: {
Authorization: `Bearer ${process.env.AGENTUITY_SDK_KEY}`,
},
}, logger);
const urls = getServiceUrls(process.env.AGENTUITY_REGION!);
export const kv = new KeyValueStorageService(urls.catalyst, adapter);
export const vector = new VectorStorageService(urls.catalyst, adapter);
export const stream = new StreamStorageService(urls.stream, adapter);Use the client in your Next.js backend:
import { type NextRequest } from 'next/server';
import { kv } from '@/lib/agentuity-storage';
interface ChatSession {
messages: Array<{ role: string; content: string }>;
createdAt: string;
}
export async function GET(request: NextRequest) {
const sessionId = request.nextUrl.searchParams.get('id');
if (!sessionId) {
return Response.json({ error: 'Missing session ID' }, { status: 400 });
}
const result = await kv.get<ChatSession>('sessions', sessionId);
if (!result.exists) {
return Response.json({ error: 'Not found' }, { status: 404 });
}
return Response.json(result.data);
}
export async function POST(request: NextRequest) {
const { sessionId, messages } = await request.json();
await kv.set('sessions', sessionId, {
messages,
createdAt: new Date().toISOString(),
}, { ttl: 86400 });
return Response.json({ success: true });
}Vector Storage
import { type NextRequest } from 'next/server';
import { vector } from '@/lib/agentuity-storage';
export async function POST(request: NextRequest) {
const { query, limit = 10 } = await request.json();
const results = await vector.search('documents', {
query,
limit,
});
return Response.json(results);
}Stream Storage
Use streams for large data exports or file transfers:
import { type NextRequest } from 'next/server';
import { stream } from '@/lib/agentuity-storage';
export async function GET(request: NextRequest) {
const id = request.nextUrl.searchParams.get('id');
if (!id) {
return Response.json({ error: 'Missing stream ID' }, { status: 400 });
}
const data = await stream.download(id);
return new Response(data, {
headers: { 'Content-Type': 'application/octet-stream' },
});
}Alternative: HTTP Routes
If you want to centralize storage logic in your Agentuity project (for middleware, sharing across multiple apps, or avoiding SDK key distribution), use HTTP routes instead.
import { createRouter } from '@agentuity/runtime';
const router = createRouter();
interface ChatSession {
messages: Array<{ role: string; content: string }>;
createdAt: string;
updatedAt: string;
}
// Get a session by ID
router.get('/:id', async (c) => {
const sessionId = c.req.param('id');
const result = await c.var.kv.get<ChatSession>('sessions', sessionId);
if (!result.exists) {
return c.json({ error: 'Session not found' }, 404);
}
return c.json(result.data);
});
// Create or update a session
router.post('/:id', async (c) => {
const sessionId = c.req.param('id');
const body = await c.req.json<Partial<ChatSession>>();
const existing = await c.var.kv.get<ChatSession>('sessions', sessionId);
const session: ChatSession = existing.exists
? { ...existing.data, ...body, updatedAt: new Date().toISOString() }
: { messages: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), ...body };
await c.var.kv.set('sessions', sessionId, session, { ttl: 86400 * 7 });
c.var.logger.info('Session updated', { sessionId });
return c.json(session);
});
// Delete a session
router.delete('/:id', async (c) => {
const sessionId = c.req.param('id');
await c.var.kv.delete('sessions', sessionId);
c.var.logger.info('Session deleted', { sessionId });
return c.json({ success: true });
});
export default router;Securing Storage Routes
Add authentication middleware to protect storage endpoints:
import { createRouter, createMiddleware } from '@agentuity/runtime';
const router = createRouter();
const requireAuth = createMiddleware(async (c, next) => {
const apiKey = c.req.header('x-api-key');
if (!apiKey || apiKey !== process.env.STORAGE_API_KEY) {
c.var.logger.warn('Unauthorized storage access attempt');
return c.json({ error: 'Unauthorized' }, 401);
}
await next();
});
router.use('/*', requireAuth);
// Routes now require x-api-key header
router.get('/:id', async (c) => {
// ... same as above
});
export default router;Routes also have access to c.var.vector and c.var.stream for Vector and Stream storage.
Logging with createLogger
Create structured loggers for scripts or external services:
import { createLogger } from '@agentuity/server';
// Create a logger with info level, no timestamps, dark color scheme
const logger = createLogger('info', false, 'dark');
logger.info('Processing request', { userId: '123' });
logger.error('Failed to connect', { error: err.message });
// Child logger with persistent context
const requestLogger = logger.child({ requestId: 'req_abc' });
requestLogger.info('Starting'); // Includes requestId in all logsParameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
level | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'info' | Minimum log level |
showTimestamp | boolean | false | Include ISO timestamps |
colorScheme | 'light' | 'dark' | 'dark' | Terminal color scheme |
context | Record<string, unknown> | {} | Default context for all logs |
Safe JSON Serialization
safeStringify handles circular references and BigInt values that break JSON.stringify:
import { safeStringify } from '@agentuity/core';
// Circular references become "[Circular]"
const obj = { name: 'Alice' };
obj.self = obj;
safeStringify(obj); // '{"name":"Alice","self":"[Circular]"}'
// BigInt converts to string
safeStringify({ id: 9007199254740991n }); // '{"id":"9007199254740991"}'
// Pretty-print with indentation
safeStringify(data, 2);Error Handling Utilities
RichError
Enhanced errors with context and pretty printing:
import { RichError } from '@agentuity/core';
const error = new RichError({
message: 'Request failed',
statusCode: 500,
endpoint: '/api/users',
cause: originalError,
});
error.plainArgs; // { statusCode: 500, endpoint: '/api/users' }
error.cause; // originalError (the cause chain)
error.prettyPrint(); // Formatted multi-line output with stack traces
error.toJSON(); // Serializable object for logging
error.toString(); // Same as prettyPrint()StructuredError
Type-safe, discriminated errors with a _tag for pattern matching:
import { StructuredError, isStructuredError } from '@agentuity/core';
// Create error types
const NotFoundError = StructuredError('NotFound');
const ValidationError = StructuredError('ValidationError')<{
field: string;
code: string;
}>();
// With a default message (message is preset, cannot be overridden)
const UpgradeRequired = StructuredError('UpgradeRequired', 'Upgrade required to access this feature');
// Throw with context
throw new ValidationError({
field: 'email',
code: 'INVALID_FORMAT',
message: 'Email format is invalid',
});
// Type-safe handling
if (isStructuredError(err) && err._tag === 'ValidationError') {
logger.error('Validation failed', { field: err.field, code: err.code });
}Schema Conversion
Convert Zod or Agentuity schemas to JSON Schema for tooling or OpenAPI generation:
import { toJSONSchema } from '@agentuity/server';
import { z } from 'zod';
const schema = z.object({ name: z.string(), age: z.number() });
const jsonSchema = toJSONSchema(schema);
// { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' } }, ... }Key Points
- Direct SDK is the simplest way to access storage from external backends
- Required env vars:
AGENTUITY_SDK_KEYandAGENTUITY_REGION(fromagentuity.json) - HTTP routes are an alternative for centralized logic, middleware, or sharing across apps
- Works with any framework: Next.js, Express, Remix, etc.
See Also
- HTTP Routes: Route creation with
createRouter - Route Middleware: Authentication patterns
- RPC Client: Typed client generation
Need Help?
Join our Community for assistance or just to hang with other humans building agents.
Send us an email at hi@agentuity.com if you'd like to get in touch.
Please Follow us on
If you haven't already, please Signup for your free account now and start building your first agent!