A fast and opinionated CLI for scaffolding Express.js projects with TypeScript, Prisma, and security-first best practices built-in.
Kickpress helps you create production-ready Express.js APIs in seconds, with full CRUD generation, input validation, type safety, and modern tooling.
- β¨ Features
- β‘ Why Kickpress?
- π Requirements
- π Quick Start
- π¦ Installation
- π¨ Templates & Starters
- π Command Reference
- π Complete Workflow Example
- π Generated Project Structure
- π¨ Generated Code Examples
- π Security & Validation
- π§ Available Scripts
- π οΈ Technology Stack
- ποΈ Database Support
- π§ͺ Testing Your API
- π Troubleshooting
- β FAQ
- π€ Contributing
- π€ Author
- π License
- π¬ Support
- π Instant Setup - Create a complete project in seconds with auto-installation
- π¨ Multiple Templates - REST API, NPM package, CLI tool, or static Web app
- β‘ Starter Content - Blank or pre-built starter (Todo API, Math library, Math CLI)
- π Security First - Built-in input validation with Zod on all endpoints
- π¦ TypeScript First - Full TypeScript support with proper types (JavaScript optional)
- ποΈ Optional Database - SQLite, PostgreSQL, MySQL, or MongoDB via Prisma β available for API, Web, and CLI templates
- π― CRUD Generation - Generate the full stack for any entity in one command
- π Auto-injection - Routes automatically added to your app
- β Add Database Later - Add Prisma to any existing project with
kickpress add db - π Switch Database - Switch between SQLite, PostgreSQL, and MySQL with
kickpress switch db - β‘ Modern Stack - Express, Prisma, Zod, tsx, and express-async-handler
- π‘οΈ Error Handling - Comprehensive error middleware included
- π HTTP Requests - Test files generated for each resource
- βοΈ Zero Configuration - Everything works out of the box
| Task | Manual Setup | With Kickpress |
|---|---|---|
| Project Setup | 15-30 minutes | 30 seconds |
| Prisma Configuration | Manual config | Auto-configured |
| Input Validation | Write from scratch | Auto-generated |
| CRUD Generation | Write from scratch | 1 command |
| Error Handling | Custom impl | Built-in |
| Type Safety | Manual types | Auto-generated |
| Route Registration | Manual import | Auto-injected |
| Add DB Later | Manual wiring | 1 command |
| Switch DB Provider | Manual refactor | 1 command |
- Node.js: v20.0.0 or higher (for
--env-filesupport) - Package Manager: npm, pnpm, or yarn
- Operating System: macOS, Linux, or Windows
- PostgreSQL / MySQL: Required only if using those database options
- MongoDB: Required only if using the MongoDB option β must run as a replica set (see MongoDB setup)
Note: Kickpress uses Node.js native
--env-fileflag. For Node.js < v20, you may need additional configuration.
# Interactive (recommended for first-time users)
npx kickpress init
# One-liner with all defaults (TypeScript, API template, SQLite, pnpm)
npx kickpress init my-api -y
# Navigate and start
cd my-api
pnpm devYour API is now running at http://localhost:3000! π
No installation required β use npx to run directly:
npx kickpress init my-projectOr install globally:
npm install -g kickpress
kickpress init my-projectChoose a template with -t. When running interactively (without -y), you will also be asked to pick a starter β a pre-built starting point for that template.
| Template | Description | Database |
|---|---|---|
api |
REST API with Express, Prisma, Zod, and CRUD helpers | Optional |
npm |
Publishable NPM package with TypeScript declarations | None |
cli |
Command-line tool with Commander.js | Optional |
web |
Static HTML/CSS/JS web app with Express | Optional |
npx kickpress init my-api -t api
npx kickpress init my-lib -t npm
npx kickpress init my-tool -t cli
npx kickpress init my-site -t webWhen you run kickpress init interactively, you'll be asked which starter to use:
| Template | Blank | Starter |
|---|---|---|
api |
Empty project β add resources with kickpress make |
Todo β complete CRUD API with title, completed fields, ready to run |
web |
Empty project | Todo β Todo API + working HTML/CSS/JS frontend |
npm |
hello() export |
Math library β sum, subtract, multiply, divide with error handling |
cli |
hello command |
Math CLI β add 2 4 β 8, subtract, multiply, divide commands |
Note: The Todo starter defaults to SQLite β no connection string needed. Pass
--database mongodbto use MongoDB instead. The-yflag always uses blank.
Creates a complete project with all dependencies and folder structure.
Syntax:
kickpress init [project-name] [options]Alias: in
Arguments:
project-nameβ Name of your project (optional, will prompt if not provided)
Options:
| Flag | Description |
|---|---|
-t, --template <template> |
Template: api | npm | cli | web |
-d, --database <database> |
Database: sqlite | postgresql | mysql | mongodb | none |
--typescript |
Use TypeScript |
--no-typescript |
Use JavaScript instead |
-p, --package-manager <pm> |
Package manager: pnpm | npm | yarn |
-y, --yes |
Accept all defaults (TypeScript, api, sqlite, pnpm, blank starter) |
Examples:
# Interactive (prompts for template, starter, TypeScript, database, package manager)
npx kickpress init
npx kickpress init my-api
# Accept all defaults β no prompts, blank starter
npx kickpress init my-api -y
npx kickpress in my-api -y
# API with SQLite
npx kickpress init my-api -t api -d sqlite
# API with PostgreSQL (db:push shown as next step β run after setting DATABASE_URL)
npx kickpress init my-api -t api -d postgresql
# API with MySQL (db:push shown as next step β run after setting DATABASE_URL)
npx kickpress init my-api -t api -d mysql
# API with MongoDB (db:push runs automatically β requires a running replica set)
npx kickpress init my-api -t api -d mongodb
# API with no database
npx kickpress init my-api -t api -d none
# CLI tool with SQLite database
npx kickpress init my-tool -t cli -d sqlite
# NPM package (no database)
npx kickpress init my-lib -t npm
# JavaScript project
npx kickpress init my-api --no-typescript -t api -d sqliteWhat it does automatically:
- β Creates complete folder structure
- β Generates all configuration files
- β Installs all dependencies
- β Applies selected starter content
- β Generates Prisma Client and pushes schema (SQLite and MongoDB run automatically β PostgreSQL/MySQL show next steps)
- β Sets up error handling middleware
- β Configures TypeScript/JavaScript
Generates the full CRUD stack for any entity β model, service, controller, validation, routes, and HTTP test file β all wired together and injected into src/index.*.
Syntax:
kickpress make <entity> [options]Alias: mk
Arguments:
entityβ Name of the entity (singular, e.g.,user,post,product)
Options:
| Flag | Description |
|---|---|
--route <path> |
Route path (e.g. /todos), skips prompt |
tableName (used for the list variable in the controller, e.g. const people = ...) is always derived from the route β the last path segment, kebab-case converted to camelCase. No separate table-name flag or prompt.
Examples:
# Interactive (prompts for the route path only)
npx kickpress make user
npx kickpress make post
# Non-interactive (useful in scripts/CI)
npx kickpress make todo --route /todos
npx kickpress mk post --route /posts
# Irregular plurals just work β table name comes from whatever you type here
npx kickpress make person --route /peopleWhat it generates:
| File | Description |
|---|---|
prisma/schema.prisma |
Updated with new model stub |
modules/entity/entity.types.ts |
TypeScript interfaces (TS only) |
modules/entity/entity.model.* |
Raw Prisma database operations (constructor-injected client) |
modules/entity/entity.service.* |
Business logic wrapping the model |
modules/entity/entity.controller.* |
HTTP handlers (class, constructor DI) |
modules/entity/entity.validation.* |
Zod schemas and validation middleware |
modules/entity/entity.routes.* |
Wires model β service β controller, owns router |
requests/entity.http |
HTTP test file for all endpoints |
Requires a database to already be configured (kickpress add db ... or select one during init) β make exits early with an error otherwise. Generated code is formatted with Prettier before being written.
Routes are also auto-injected into src/routes/index.*.
Architecture flow:
Request β Routes β Validation β Controller β Service β Model β Prisma β DB
Wires Prisma into a project that was initially created without a database. Works for API, Web, and CLI projects.
Syntax:
kickpress add db [database]Arguments:
databaseβsqlite,postgresql,mysql, ormongodb(optional, will prompt if not provided)
Examples:
# Interactive prompt
npx kickpress add db
# Non-interactive
npx kickpress add db sqlite
npx kickpress add db postgresql
npx kickpress add db mysql
npx kickpress add db mongodbWhat it does:
- β
Installs
@prisma/client,prisma, and the correct database adapter - β
Creates
prisma/schema.prismaandprisma.config.ts - β
Creates
src/lib/prisma.ts(typed Prisma client) - β
Patches
error.middleware.*to handle Prisma error codes (P2002,P2025) β API/Web only - β
Appends
DATABASE_URLto.env - β
Appends Prisma entries to
.gitignore - β
Adds
db:generateanddb:pushscripts topackage.json - β
Adds
db:migrateanddb:studioscripts (SQLite, PostgreSQL, and MySQL only β not MongoDB) - β
Runs
db:generateanddb:pushautomatically (SQLite and MongoDB) - β
For PostgreSQL/MySQL: runs
db:generate, then shows next steps to setDATABASE_URLand rundb:push
Note: This command is safe to run on any Kickpress project (API, Web, or CLI). It will exit early if a database is already configured.
Switches your project between SQLite, PostgreSQL, and MySQL. Updates all configuration β packages, schema provider, .env, and the Prisma client file β without touching your schema models.
β οΈ MongoDB cannot be switched to or from. MongoDB uses Prisma v6, while SQLite/PostgreSQL/MySQL use Prisma v7. These are incompatible. Create a new project instead.
Syntax:
kickpress switch db [database]Arguments:
databaseβsqlite,postgresql, ormysql(optional, will prompt if not provided)
Examples:
# Interactive prompt (shows current provider, lets you pick the target)
npx kickpress switch db
# Non-interactive
npx kickpress switch db postgresql
npx kickpress switch db mysql
npx kickpress switch db sqliteWhat it does:
- β Uninstalls the current database adapter
- β Installs the new database adapter
- β
Updates
providerinprisma/schema.prisma - β
Updates
DATABASE_URLin.env - β
Regenerates
src/lib/prisma.*with the correct adapter - β
Runs
db:generate - β
Runs
db:pushautomatically when switching to SQLite - β Shows next steps when switching to PostgreSQL or MySQL (requires a live connection first)
β οΈ Data migration is manual.switch dbonly switches the schema and configuration. Your existing data is not transferred. Back up your data before switching.
Next steps after switching to PostgreSQL:
# 1. Update your connection string in .env
DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public"
# 2. Push the schema to your PostgreSQL database
pnpm db:push
# 3. Migrate your data manuallyNext steps after switching to MySQL:
# 1. Update your connection string in .env
DATABASE_URL="mysql://user:password@localhost:3306/database"
# 2. Push the schema to your MySQL database
pnpm db:push
# 3. Migrate your data manually1. Create project interactively:
npx kickpress init my-api
# Select: REST API β Todo starter β TypeScript β SQLite β pnpm
cd my-api
pnpm devThe Todo starter generates a complete, working
/todosCRUD API β ready to run with no edits.
2. Test immediately:
curl http://localhost:3000/todos
curl -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{"title":"Buy coffee"}'1. Create project:
npx kickpress init blog-api -t api -d sqlite
cd blog-api2. Generate resources:
npx kickpress make post
# Prompts: route path (e.g. /posts) β table name (posts) is derived from it3. Add fields to your model and validation:
Edit prisma/schema.prisma:
model Post {
id Int @id @default(autoincrement())
title String
content String
author String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Edit src/modules/post/post.validation.ts:
const postCreateSchema = z.object({
title: z.string().min(1).max(255),
content: z.string().min(1),
author: z.string().min(1).max(100),
});4. Push schema and start:
pnpm db:generate
pnpm db:push
pnpm devnpx kickpress init blog-api -t api -d postgresql
cd blog-apiUpdate .env:
DATABASE_URL="postgresql://postgres:mypassword@localhost:5432/blogdb?schema=public"pnpm db:push
pnpm devnpx kickpress init blog-api -t api -d mongodb
cd blog-apiUpdate .env with your connection string (must point to a replica set):
# Local replica set
DATABASE_URL="mongodb://127.0.0.1:27017/blog-api?replicaSet=rs0&directConnection=true"
# MongoDB Atlas
DATABASE_URL="mongodb+srv://user:pass@cluster.mongodb.net/blog-api?retryWrites=true&w=majority"pnpm db:push
pnpm devNote: MongoDB requires a running replica set. For local development, start
mongod --replSet rs0and runrs.initiate()once inmongosh. For production, use MongoDB Atlas.
# Create with database from the start
npx kickpress init my-tool -t cli -d sqlite
cd my-tool
pnpm dev
# Or add a database to an existing CLI project
npx kickpress add db sqlitenpx kickpress init blog-api -t api -d none
cd blog-api
# ... develop, change your mind ...
npx kickpress add db sqlite# Switch from SQLite to PostgreSQL
npx kickpress switch db postgresql
# β Update DATABASE_URL in .env, then run: pnpm db:push
# Switch from SQLite to MySQL
npx kickpress switch db mysql
# β Update DATABASE_URL in .env, then run: pnpm db:push
# Switch back to SQLite
npx kickpress switch db sqlite
# β db:push runs automaticallyMongoDB cannot be switched to or from (different Prisma major version). Create a new project instead.
my-api/
βββ src/
β βββ index.ts # App entry (sealed β never touched by make)
β βββ routes/
β β βββ index.ts # Route barrel (auto-updated by make)
β βββ lib/
β β βββ prisma.ts # Prisma client
β βββ middlewares/
β β βββ error.middleware.ts
β βββ utils/
βββ prisma/
β βββ schema.prisma
βββ requests/
βββ .env
βββ tsconfig.json
βββ package.json
After kickpress make user, each entity gets its own folder under src/modules/:
src/
βββ modules/
βββ user/
βββ user.model.ts # Prisma operations
βββ user.service.ts # Business logic
βββ user.controller.ts # HTTP handlers
βββ user.routes.ts # Wires graph + owns the router
βββ user.types.ts # TypeScript interfaces
βββ user.validation.ts # Zod schemas + middleware
import type PrismaClientInstance from "@/lib/prisma";
import type { User, UserCreateInput, UserUpdateInput } from "@/modules/user/user.types";
export class UserModel {
constructor(private prisma: typeof PrismaClientInstance) {}
async findAll(): Promise<User[]> { return this.prisma.user.findMany(); }
async findOne(id: number): Promise<User | null> { return this.prisma.user.findUnique({ where: { id } }); }
async create(data: UserCreateInput): Promise<User> { return this.prisma.user.create({ data }); }
async update(id: number, data: UserUpdateInput): Promise<User | null> { return this.prisma.user.update({ where: { id }, data }); }
async delete(id: number): Promise<User | null> { return this.prisma.user.delete({ where: { id } }); }
}import { Request, Response } from "express";
import asyncHandler from "express-async-handler";
import { UserService } from "@/modules/user/user.service";
export class UserController {
constructor(private service: UserService) {}
all = asyncHandler(async (_: Request, res: Response) => {
res.json(await this.service.getAll());
});
findOne = asyncHandler(async (req: Request, res: Response) => {
const user = await this.service.getOne(Number(req.params.id));
if (!user) { res.status(404); throw new Error("User not found"); }
res.json(user);
});
create = asyncHandler(async (req: Request, res: Response) => {
res.status(201).json(await this.service.create(req.body));
});
update = asyncHandler(async (req: Request, res: Response) => {
const user = await this.service.getOne(Number(req.params.id));
if (!user) { res.status(404); throw new Error("User not found"); }
res.json(await this.service.update(user.id, req.body));
});
remove = asyncHandler(async (req: Request, res: Response) => {
const user = await this.service.getOne(Number(req.params.id));
if (!user) { res.status(404); throw new Error("User not found"); }
await this.service.delete(user.id);
res.status(204).send();
});
}import { Router } from "express";
import prisma from "@/lib/prisma";
import { UserModel } from "@/modules/user/user.model";
import { UserService } from "@/modules/user/user.service";
import { UserController } from "@/modules/user/user.controller";
import { validateUserCreate, validateUserUpdate, validateUserId } from "@/modules/user/user.validation";
const model = new UserModel(prisma);
const service = new UserService(model);
const controller = new UserController(service);
const router = Router();
router.route("/").get(controller.all).post(validateUserCreate, controller.create);
router.route("/:id")
.get(validateUserId, controller.findOne)
.patch(validateUserId, validateUserUpdate, controller.update)
.delete(validateUserId, controller.remove);
export default router;Standard (fill in your fields after extending the Prisma model):
const userCreateSchema = z.object({
// Add your fields here after extending the Prisma model
});Todo Starter (pre-filled and ready to use):
const todoCreateSchema = z.object({
title: z.string().min(1, "Title is required").max(255),
completed: z.boolean().optional().default(false),
});
const todoUpdateSchema = z.object({
title: z.string().min(1).max(255).optional(),
completed: z.boolean().optional(),
});β SQL Injection Protection β Prisma uses parameterized queries β Input Validation β Zod validates all request data β Type Coercion β Safe type transformation β Error Information Leakage β Safe error messages in production
pnpm dev # Start dev server with hot reload
pnpm build # Compile TypeScript to JavaScript
pnpm start # Start production server
# Database (when configured)
pnpm db:generate # Generate Prisma Client
pnpm db:push # Push schema to database
pnpm db:migrate # Create migration (SQLite, PostgreSQL, MySQL only)
pnpm db:studio # Open Prisma Studio (SQLite, PostgreSQL, MySQL only)
db:migrateanddb:studioare not added for MongoDB projects (Prisma Studio does not support MongoDB).
- Express.js β Fast, minimalist web framework
- TypeScript β Type-safe JavaScript (optional)
- Zod β TypeScript-first schema validation
- Commander.js β CLI argument parsing (CLI template)
- tsx β TypeScript execution engine
- express-async-handler β Async error handling
- Prisma β Next-generation ORM (optional) β v7 for SQLite/PostgreSQL/MySQL, v6.19 for MongoDB
- SQLite β File-based, zero config (with better-sqlite3 adapter)
- PostgreSQL β Production-ready (with @prisma/adapter-pg)
- MySQL β Production-ready (with @prisma/adapter-mariadb)
- MongoDB β Document database via Prisma v6.19 (requires replica set)
Database is optional for api, web, and cli templates and not available for the npm template.
Zero configuration β Kickpress sets it up automatically and runs db:push immediately.
DATABASE_URL="file:./dev.db"Kickpress generates all config files and runs db:generate, then shows you the next steps to run after configuring your connection string.
DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public"Popular hosted options:
# Neon
DATABASE_URL="postgresql://user:pass@ep-cool-name.us-east-2.aws.neon.tech/neondb?sslmode=require"
# Supabase
DATABASE_URL="postgresql://postgres:pass@db.project.supabase.co:5432/postgres?schema=public"
# Railway
DATABASE_URL="postgresql://postgres:pass@containers-us-west.railway.app:5432/railway?schema=public"Kickpress generates all config files and runs db:generate, then shows you the next steps to run after configuring your connection string.
DATABASE_URL="mysql://user:password@localhost:3306/database"Kickpress uses Prisma v6.19 for MongoDB (Prisma v7 MongoDB support is pending). It generates all config files and runs db:generate + db:push automatically once your connection string is set.
MongoDB requires a replica set β Prisma uses multi-document transactions which are only available on replica sets.
# Local replica set
DATABASE_URL="mongodb://127.0.0.1:27017/mydb?replicaSet=rs0&directConnection=true"
# MongoDB Atlas (replica set is built-in)
DATABASE_URL="mongodb+srv://user:pass@cluster.mongodb.net/mydb?retryWrites=true&w=majority"Local replica set setup (one-time):
# Start mongod with replica set mode
mongod --replSet rs0 --dbpath /your/data/path
# In another terminal, initiate the replica set (first time only)
mongosh --eval "rs.initiate()"MongoDB differences from other providers:
- IDs are
String(MongoDB ObjectId) instead ofInt - No
db:migrateordb:studioscripts (not supported) - Cannot use
switch dbto/from MongoDB (different Prisma major version) - Uses
prisma-client-jsgenerator (Prisma v6) instead ofprisma-client(Prisma v7)
npx kickpress init my-api -t api -d none
npx kickpress add db sqlite # add laternpx kickpress switch db postgresql # SQLite β PostgreSQL
npx kickpress switch db mysql # SQLite β MySQL
npx kickpress switch db sqlite # PostgreSQL/MySQL β SQLite
β οΈ Schema and config switch automatically. Data migration is manual.
β οΈ MongoDB cannot be switched to or from. MongoDB uses Prisma v6, while SQLite/PostgreSQL/MySQL use Prisma v7. Create a new project with your desired database instead.
Each generated resource includes an .http file. Use the REST Client VS Code extension:
- Open
requests/user.http - Click "Send Request" above any request
Or use curl:
curl http://localhost:3000/api/users
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"john@example.com"}'pnpm db:generatePORT=3001pg_isready
psql "postgresql://USER:PASSWORD@HOST:PORT/DATABASE"Common causes: wrong credentials, database doesn't exist, SSL required (?sslmode=require).
mongosh "mongodb://127.0.0.1:27017"Common causes:
- Not a replica set: Prisma requires MongoDB to run as a replica set. Start with
mongod --replSet rs0and runrs.initiate()once inmongosh. - Wrong port: If port 27017 is in use by a system MongoDB service, try a different port (
--port 27018) and updateDATABASE_URLaccordingly. - Missing
directConnection=true: Required for local single-node replica sets (?directConnection=true). - Atlas connection: Make sure your IP is allowlisted in MongoDB Atlas Network Access.
Q: Can I use this in production? A: Yes! Use PostgreSQL for production and add authentication, CORS, and rate limiting as needed.
Q: What databases are supported? A: SQLite, PostgreSQL, MySQL, and MongoDB. All four are fully supported.
Q: Can I add a database to a CLI project?
A: Yes! Run npx kickpress add db sqlite (or another provider) from inside your CLI project.
Q: Can I switch from SQLite to PostgreSQL later?
A: Yes! Run npx kickpress switch db postgresql. Your schema models are preserved β only the provider and adapter change. Data migration is manual.
Q: Can I switch to or from MongoDB? A: No. MongoDB uses Prisma v6.19 while SQLite/PostgreSQL/MySQL use Prisma v7. These are not compatible in the same project. Create a new project with your desired database instead.
Q: Why doesn't db:push run automatically for PostgreSQL/MySQL?
A: PostgreSQL and MySQL require a running server and a valid connection string. Kickpress sets up all config files and generates the client, then shows you the exact commands to run once DATABASE_URL is configured.
Q: Does MongoDB work without a replica set?
A: No. Prisma requires MongoDB to run as a replica set (even locally) to support multi-document transactions. For local dev, start mongod --replSet rs0 and run rs.initiate() once in mongosh. For production, use MongoDB Atlas.
Q: Why is MongoDB on Prisma v6 and not v7?
A: Prisma v7 MongoDB support is pending. Kickpress uses Prisma v6.19 for MongoDB projects, which is the latest stable release with full MongoDB support. When Prisma v7 adds MongoDB support, kickpress switch db will be updated accordingly.
Q: What are starters?
A: Starters are optional pre-built starting points. Run kickpress init interactively to pick one β a full Todo CRUD API for api/web, or a math library/CLI for npm/cli. The -y flag always uses blank.
Q: How is this different from NestJS? A: NestJS is a framework. Kickpress generates plain Express.js code with no framework lock-in.
Q: Can I customize the generated code? A: Absolutely β it's all yours to modify.
Contributions are welcome!
- Fork the repository
- Create your feature branch
- Make your changes
- Submit a pull request
Marc Tyson CLEBERT
- Website: marctysonclebert.com
- GitHub: @clebertmarctyson
- Twitter: @ClebertTyson
- Email: contact@marctysonclebert.com
MIT Β© Marc Tyson CLEBERT
- π Report Issues
- π‘ Request Features
- π§ Email: contact@marctysonclebert.com
- β Star on GitHub
- β Buy me a coffee
Made with β and β€οΈ by Marc Tyson CLEBERT