Semola has zero runtime dependencies . This is not a coincidence—it’s a core design principle that makes your applications more secure, faster, and easier to maintain.
Why zero dependencies matter
No supply chain attacks
Every dependency you add is a potential security risk. The 2024 left-pad incident and the event-stream compromise showed how quickly malicious code can propagate through npm’s dependency tree.
With Semola, you only audit one package: Semola itself. There are no transitive dependencies to worry about.
# Traditional framework
npm install express
# Installs 50+ packages you didn't ask for
# Semola
bun add semola
# Installs exactly 1 package
Smaller bundle sizes
Zero dependencies means smaller bundle sizes. Your production builds stay lean because you’re not shipping unused code from deep dependency chains.
package.json (Semola)
package.json (typical framework)
{
"dependencies" : {
"semola" : "^0.5.0"
}
}
The Semola package.json shows this commitment:
{
"name" : "semola" ,
"version" : "0.5.1" ,
"peerDependencies" : {
"typescript" : "^5"
}
}
No runtime dependencies. TypeScript is a peer dependency because it’s only needed during development.
Faster installs
No dependencies means no dependency resolution. Your bun install or npm install completes in milliseconds, not minutes.
Tree-shakeable by design
Semola exports modules as separate entry points, so bundlers can eliminate unused code:
// Only imports the errors module
import { ok , err } from 'semola/errors' ;
// Your bundle won't include code from semola/api, semola/queue, etc.
This modular architecture is defined in package.json:
"exports" : {
"./errors" : {
"import" : "./dist/lib/errors/index.js" ,
"types" : "./dist/lib/errors/index.d.ts"
},
"./cache" : {
"import" : "./dist/lib/cache/index.js" ,
"types" : "./dist/lib/cache/index.d.ts"
}
}
How Semola achieves zero dependencies
Native Bun APIs
Semola leverages Bun’s native APIs instead of pulling in third-party packages:
HTTP server : Uses Bun.serve() instead of Express or Fastify
Cookie parsing : Uses Bun.CookieMap instead of cookie-parser
JSON handling : Native Response.json() instead of body-parser
src/lib/api/core/index.ts (Cookie parsing)
src/lib/api/core/index.ts (JSON responses)
const cookieHeader = req . headers . get ( "cookie" ) ?? "" ;
const cookieMap = new Bun . CookieMap ( cookieHeader );
const cookies = Object . fromEntries ( cookieMap );
Simple implementations
Semola keeps implementations minimal. The entire errors module is 28 lines:
export const ok = < T >( data : T ) => {
return [ null , data ] as const ;
};
export const err = < T extends CommonError >( type : T , message : string ) => {
return [{ type , message }, null ] as const ;
};
export const mightThrow = async < T >( promise : Promise < T >) => {
try {
const data = await promise ;
return [ null , data ] as const ;
} catch ( error ) {
return [ error , null ] as const ;
}
};
No need for a heavyweight error handling library when 28 lines does the job.
Framework-agnostic validation
Instead of depending on a specific validation library, Semola uses Standard Schema , an interface that works with any validation library:
src/lib/api/core/types.ts
import type { StandardSchemaV1 } from "@standard-schema/spec" ;
export type RequestSchema = {
params ?: StandardSchemaV1 ;
body ?: StandardSchemaV1 ;
query ?: StandardSchemaV1 ;
headers ?: StandardSchemaV1 ;
cookies ?: StandardSchemaV1 ;
};
@standard-schema/spec is a type-only package with zero runtime code. It adds 0 bytes to your bundle.
The tradeoffs
Zero dependencies isn’t free:
More code to maintain : Semola implements functionality that could be imported
Limited scope : Semola focuses on what Bun does best and avoids complex features
Peer dependencies : You bring your own validation library (Zod, Valibot, etc.)
But for most applications, these tradeoffs are worth it. You get a more secure, performant, and maintainable foundation.
Comparison with other frameworks
Framework Dependencies Install size Security surface Semola 0 ~50KB 1 package Express 31 ~1.2MB 32 packages Fastify 12 ~800KB 13 packages Hono 0 ~40KB 1 package Elysia 3 ~200KB 4 packages
Want to see what’s in your node_modules? Run npm ls --all in an Express project vs. a Semola project. The difference is stark.