From replit-pack
Sets up full-stack web apps on Replit with Express or Flask backend, PostgreSQL database, Replit Auth, object storage, and autoscale deployment.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin replit-packThis skill is limited to using the following tools:
Build a production-ready web app on Replit: Express or Flask server, PostgreSQL database, Replit Auth for user login, Object Storage for file uploads, and Autoscale deployment. This is the primary money-path workflow for shipping apps on Replit.
Generates starter Replit app with Express/Flask server, Replit Database, Object Storage, Auth, and PostgreSQL for new projects or service demos.
Guides app deployment to production: select hosting by stack (Vercel/Next.js, Railway/Python), connect custom domains, set env vars, manage prod DBs, configure DNS.
Guides Flask web app development including routing, Jinja2 templates, blueprints, extensions like SQLAlchemy and Login, REST APIs, and Gunicorn deployment.
Share bugs, ideas, or general feedback.
Build a production-ready web app on Replit: Express or Flask server, PostgreSQL database, Replit Auth for user login, Object Storage for file uploads, and Autoscale deployment. This is the primary money-path workflow for shipping apps on Replit.
.replit and replit.nix configured (see replit-install-auth)my-app/
├── .replit # Run + deployment config
├── replit.nix # System dependencies
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # Express entry point
│ ├── routes/
│ │ ├── api.ts # API endpoints
│ │ ├── auth.ts # Auth routes
│ │ └── health.ts # Health check
│ ├── services/
│ │ ├── db.ts # PostgreSQL pool
│ │ └── storage.ts # Object Storage
│ └── middleware/
│ ├── auth.ts # Replit Auth middleware
│ └── errors.ts # Error handler
└── tests/
# .replit
entrypoint = "src/index.ts"
run = "npx tsx src/index.ts"
modules = ["nodejs-20:v8-20230920-bd784b9"]
[nix]
channel = "stable-24_05"
[env]
NODE_ENV = "development"
[deployment]
run = ["sh", "-c", "npx tsx src/index.ts"]
build = ["sh", "-c", "npm ci"]
deploymentTarget = "autoscale"
# replit.nix
{ pkgs }: {
deps = [
pkgs.nodejs-20_x
pkgs.nodePackages.typescript-language-server
pkgs.postgresql
];
}
// src/services/db.ts
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false },
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
// Initialize schema
export async function initDB() {
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
profile_image TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS posts (
id SERIAL PRIMARY KEY,
user_id TEXT REFERENCES users(id),
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
`);
}
export async function upsertUser(id: string, username: string, image: string) {
return pool.query(
`INSERT INTO users (id, username, profile_image)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO UPDATE SET username = $2, profile_image = $3
RETURNING *`,
[id, username, image]
);
}
export async function createPost(userId: string, title: string, content: string) {
return pool.query(
'INSERT INTO posts (user_id, title, content) VALUES ($1, $2, $3) RETURNING *',
[userId, title, content]
);
}
export async function getPosts(limit = 20) {
return pool.query(
`SELECT p.*, u.username, u.profile_image
FROM posts p JOIN users u ON p.user_id = u.id
ORDER BY p.created_at DESC LIMIT $1`,
[limit]
);
}
export { pool };
// src/middleware/auth.ts
import { Request, Response, NextFunction } from 'express';
import { upsertUser } from '../services/db';
export interface AuthedRequest extends Request {
user: { id: string; name: string; image: string };
}
export async function requireAuth(req: Request, res: Response, next: NextFunction) {
const userId = req.headers['x-replit-user-id'] as string;
if (!userId) return res.status(401).json({ error: 'Login required' });
const name = (req.headers['x-replit-user-name'] as string) || '';
const image = (req.headers['x-replit-user-profile-image'] as string) || '';
// Upsert user in database on every authenticated request
await upsertUser(userId, name, image);
(req as any).user = { id: userId, name, image };
next();
}
// src/routes/api.ts
import { Router } from 'express';
import { requireAuth, AuthedRequest } from '../middleware/auth';
import { createPost, getPosts } from '../services/db';
import { Client as StorageClient } from '@replit/object-storage';
const router = Router();
const storage = new StorageClient();
// Public: list posts
router.get('/posts', async (req, res) => {
const { rows } = await getPosts();
res.json(rows);
});
// Protected: create post
router.post('/posts', requireAuth, async (req, res) => {
const { title, content } = req.body;
const user = (req as AuthedRequest).user;
const { rows } = await createPost(user.id, title, content);
res.status(201).json(rows[0]);
});
// Protected: upload file to Object Storage
router.post('/upload', requireAuth, async (req, res) => {
const user = (req as AuthedRequest).user;
const filename = `uploads/${user.id}/${Date.now()}-${req.body.name}`;
await storage.uploadFromText(filename, req.body.content);
res.json({ path: filename });
});
export default router;
// src/index.ts
import express from 'express';
import { initDB, pool } from './services/db';
import apiRoutes from './routes/api';
const app = express();
app.use(express.json());
// Health check (required for Replit deployments)
app.get('/health', async (req, res) => {
try {
await pool.query('SELECT 1');
res.json({ status: 'ok', uptime: process.uptime() });
} catch {
res.status(503).json({ status: 'degraded' });
}
});
// Auth info endpoint (client-side: GET /__replauthuser)
app.get('/api/me', (req, res) => {
const id = req.headers['x-replit-user-id'];
if (!id) return res.json({ loggedIn: false });
res.json({
loggedIn: true,
id,
name: req.headers['x-replit-user-name'],
image: req.headers['x-replit-user-profile-image'],
});
});
app.use('/api', apiRoutes);
const PORT = parseInt(process.env.PORT || '3000');
initDB().then(() => {
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});
});
| Error | Cause | Solution |
|---|---|---|
| DATABASE_URL undefined | PostgreSQL not provisioned | Create database in Database pane |
| Auth headers empty | Running in dev mode | Auth only works on deployed .replit.app |
| Object Storage 403 | No bucket created | Provision bucket in Object Storage pane |
| Port conflict | Multiple services on same port | Use different ports, set ignorePorts |
For collaboration and admin workflows, see replit-core-workflow-b.