What is Deesse Functions?
Introduction to DeesseJS Functions library
Deesse Functions is a TypeScript utility library for building type-safe APIs with powerful context management. It combines simplicity with advanced features to help you create robust, maintainable backends.
Core Philosophy
Deesse Functions is built on three principles:
1. Type Safety First
Catch Errors at Compile Time
Every part of your API is fully typed - from arguments to return values to context. Catch errors at compile time, not runtime.
// Args are validated and typed
const getUser = t.query({
args: z.object({ id: z.number() }),
handler: async (ctx, args) => {
// args.id is guaranteed to be a number
return success({ id: args.id, name: "Alice" });
},
});
// Usage is fully type-checked
const result = await api.getUser({ id: 1 });
const result = await api.getUser({ id: "wrong" }); // ❌ Type error!2. Context as Single Source of Truth
Define Once, Use Everywhere
Define your dependencies once. Context flows automatically to all your queries and mutations.
const { t, createAPI } = defineContext({
database: myDatabase,
userId: "user-123",
emailService: myMailer,
});
// Context is automatically available in all handlers
const createUser = t.mutation({
handler: async (ctx, args) => {
// Access ctx.database, ctx.userId, ctx.emailService
// No need to pass them around manually
},
});3. Batteries Included
No Plugins Required
Essential features are built-in. No need to install plugins or extensions for core functionality.
Queries & Mutations
Native read and write operations - no extensions needed
Intelligent Caching
Built-in caching with automatic revalidation
Event-Driven Architecture
Emit and listen to events across your application
Authorization Checks
Protect your endpoints with built-in authorization
Lifecycle Hooks
Add middleware and side effects at any stage
Automatic Retry
Resilient operations with built-in retry logic
Input Validation
Type-safe validation powered by Zod
Key Features
Type-Safe Context Management
Single Source of Truth
Context provides a single source of truth for your API. Define it once, use it everywhere with full type safety.
const { t, createAPI } = defineContext<{
userId: string;
database: Database;
emailService: EmailService;
}>();
// TypeScript knows exactly what's in context
const sendEmail = t.mutation({
handler: async (ctx, args) => {
ctx.userId; // ✅ Type: string
ctx.database; // ✅ Type: Database
ctx.emailService; // ✅ Type: EmailService
ctx.unknown; // ❌ Type error
},
});Native Queries & Mutations
Built-in support for read and write operations. No extensions required.
// Queries for reading data
const getUser = t.query({
cacheKey: ['users', '{id}'],
staleTime: 60000,
args: z.object({ id: z.number() }),
handler: async (ctx, args) => {
return success(await ctx.database.find(args.id));
},
});
// Mutations for writing data
const updateUser = t.mutation({
invalidate: [['users', '{id}']],
args: z.object({ id: z.number(), name: z.string() }),
handler: async (ctx, args) => {
return success(await ctx.database.update(args.id, args));
},
});Intelligent Caching
Built-in Caching with Invalidation
Built-in caching with automatic invalidation and revalidation. Configure cache keys and let the system handle the rest.
const getUser = t.query({
cacheKey: ['users', '{id}'],
staleTime: 60000, // Cache for 1 minute
gcTime: 300000, // Remove from memory after 5 minutes
handler: async (ctx, args) => {
// Only called if cache is stale or missing
return success(await fetchUser(args.id));
},
});Result Types
Explicit Error Handling
Handle success and failure cases explicitly and safely with built-in Result types.
import { success, failure } from '@deessejs/functions';
const getUser = t.query({
handler: async (ctx, args) => {
const user = await ctx.database.find(args.id);
if (!user) {
return failure(new Error("User not found"));
}
return success(user);
},
});
// Usage
const result = await api.getUser({ id: 1 });
if (result.success) {
console.log(result.value); // User data
} else {
console.error(result.error); // Error information
}Authorization Checks
Protect your endpoints with built-in authorization.
import { hasPermission } from '@deessejs/functions';
const deleteUser = t.mutation({
authorize: hasPermission('admin'),
handler: async (ctx, args) => {
// Only runs if user has 'admin' permission
return success(await ctx.database.delete(args.id));
},
});Lifecycle Hooks
Middleware at Any Stage
Add middleware and side effects at any stage of request execution.
const createUser = t.mutation({
beforeInvoke: async (ctx, args) => {
console.log('Creating user:', args);
},
onSuccess: async (ctx, args, result) => {
await ctx.events.emit('user:created', result.value);
},
onError: async (ctx, args, error) => {
await ctx.logger.error('Failed to create user', error);
},
handler: async (ctx, args) => {
return success(await ctx.database.create(args));
},
});Event-Driven Architecture
Emit and listen to events across your application.
const createPost = t.mutation({
handler: async (ctx, args) => {
const post = await ctx.database.create(args);
await ctx.events.emit('post:created', post);
return success(post);
},
});
// Listen to events
ctx.events.on('post:created', async (post) => {
await ctx.emailService.sendNotification(post.authorId);
});What Deesse Functions is NOT
Understanding What Deesse Functions is NOT
Deesse Functions is a focused library with specific goals. Understanding what it's not helps you use it effectively.
Not an ORM
Works Alongside Your Database
Deesse Functions doesn't replace your database layer. It works alongside your existing data access code.
// You still use your favorite ORM or query builder
const getUser = t.query({
handler: async (ctx, args) => {
// Works with Prisma, Drizzle, TypeORM, Knex, raw SQL, etc.
return success(await prisma.user.findUnique({ where: { id: args.id } }));
},
});Not a Framework
Library, Not Framework
Deesse Functions is a library, not a framework. It doesn't dictate your application structure.
// Use it however you want
const api = createAPI({
// Organize however makes sense for your project
users: t.router({ /* ... */ }),
posts: t.router({ /* ... */ }),
admin: t.router({ /* ... */ }),
});Not Tied to Any Transport
Protocol Agnostic
Use with HTTP, WebSockets, RPC, or any protocol you need.
// Works with any transport layer
const api = createAPI({ /* ... */ });
// Use with Express, Fastify, Hono, Next.js, etc.
app.post('/api/users/create', async (req, res) => {
const result = await api.users.create(req.body);
res.json(result);
});Design Principles
Functional Approach
Functional Programming Principles
Deesse Functions follows functional programming principles for cleaner, more maintainable code.
- No classes - Pure functions and data objects only
- No interfaces - Type aliases for structural modeling
- Const functions - Arrow functions only
- Immutability - Data never changes unexpectedly
// ✅ Functional style
const getUser = t.query({
handler: async (ctx, args) => {
return success(immutableData);
},
});
// ❌ Not used in Deesse Functions
class UserService {
async getUser() { /* ... */ }
}Simplicity Over Complexity
Major Refactor for Simplicity
The library recently underwent a major refactor to remove unnecessary complexity (HKT) while maintaining full type safety. The result is a simpler, more approachable API.
Before (complex HKT-based approach):
// Complex type-level programming
interface Query<T, C, Args> { /* ... */ }After (simple, standard TypeScript):
// Standard TypeScript generics
const query = t.query<Args, Return>({ /* ... */ });When to Use Deesse Functions
Deesse Functions is ideal for:
Real-Time Applications
Live Data Updates
Applications with live data updates benefit from intelligent caching and automatic revalidation.
const getMessages = t.query({
cacheKey: ['messages', '{channelId}'],
staleTime: 5000, // Revalidate every 5 seconds
handler: async (ctx, args) => {
return success(await ctx.db.getMessages(args.channelId));
},
});Collaborative Systems
Multi-user applications with complex authorization requirements.
const updateDocument = t.mutation({
authorize: async (ctx, args) => {
const doc = await ctx.db.find(args.documentId);
return doc.ownerId === ctx.userId || doc.collaborators.includes(ctx.userId);
},
handler: async (ctx, args) => {
return success(await ctx.db.update(args.documentId, args));
},
});Enterprise Applications
Large applications with complex business logic and multiple services.
const { t, createAPI } = defineContext({
database: db,
cache: redis,
email: mailer,
sms: twilio,
analytics: segment,
logger: winston,
});
const createUser = t.mutation({
handler: async (ctx, args) => {
const user = await ctx.database.create(args);
await ctx.email.sendWelcome(user.email);
await ctx.analytics.track('user:created', { userId: user.id });
ctx.logger.info('User created', { userId: user.id });
return success(user);
},
});Performance-Critical Applications
Excellent Caching Strategies
Applications that need excellent caching strategies and minimal redundant operations.
const getProduct = t.query({
cacheKey: ['products', '{id}'],
staleTime: 300000, // 5 minutes
gcTime: 900000, // 15 minutes
handler: async (ctx, args) => {
// Expensive operation only runs when cache is stale
return success(await ctx.db.getProductWithReviews(args.id));
},
});When NOT to Use Deesse Functions
Consider Alternatives When:
- Simple scripts or CLIs - Overhead isn't justified for one-off scripts
- Static site generators - No dynamic API needed
- Purely serverless functions - If you have no shared state, context might be overkill
Comparison to Traditional Approaches
Express Routes
// Traditional Express
app.get('/users/:id', async (req, res) => {
// No type safety for req.params
// Manual error handling
// No built-in caching
// No authorization helpers
const user = await db.find(req.params.id);
res.json(user);
});
// Deesse Functions
const getUser = t.query({
args: z.object({ id: z.number() }), // Type-safe args
handler: async (ctx, args) => { // Automatic error handling
return success(await ctx.db.find(args.id)); // Built-in Result type
},
});tRPC
// tRPC requires routers
export const appRouter = router({
users: router({
get: procedure.input(z.object({ id: z.number() })).query(async ({ input }) => {
return await db.find(input.id);
}),
}),
});
// Deesse Functions - simpler API
const getUser = t.query({
args: z.object({ id: z.number() }),
handler: async (ctx, args) => {
return success(await ctx.db.find(args.id));
},
});
const api = createAPI({
users: t.router({ get: getUser }),
});Ecosystem
TypeScript
First-class TypeScript support with full type inference
Zod
Schema validation powered by Zod
Vitest
Testing framework integration
Next.js
Full-stack framework compatibility
Any ORM
Works with Prisma, Drizzle, TypeORM, and more
License
MIT - freely use in personal and commercial projects.