
nodejs-backend-typescript
Node.js backend development with TypeScript, Express/Fastify servers, routing, middleware, and database integration
- Installs
- 745
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill nodejs-backend-typescript- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads nodejs-backend-typescript automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Node.js Backend Development with TypeScript
progressive_disclosure: entry_point: summary: "TypeScript backend patterns with Express/Fastify, routing, middleware, database integration" when_to_use: - "When building REST APIs with TypeScript" - "When creating Express/Fastify servers" - "When needing server-side TypeScript" - "When building microservices" quick_start: - "npm init -y && npm install -D typescript @types/node tsx" - "npm install express @types/express zod" - "Create tsconfig.json with strict mode" - "npm run dev" token_estimate: entry: 75 full: 4700
TypeScript Setup
Essential Configuration
tsconfig.json (strict mode recommended):
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
package.json scripts:
{
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"test": "vitest"
}
}
Development Dependencies
npm install -D typescript @types/node tsx vitest
npm install -D @types/express # or @types/node (Fastify has built-in types)
Express Patterns
Basic Express Server
src/server.ts:
import express, { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
const app = express();
const port = process.env.PORT || 3000;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Type-safe request handlers
interface TypedRequest<T> extends Request {
body: T;
}
// Routes
app.get('/health', (req: Request, res: Response) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Start server
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Router Pattern
src/routes/users.ts:
import { Router } from 'express';
import { z } from 'zod';
import { validateRequest } from '../middleware/validation';
const router = Router();
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2),
age: z.number().int().positive().optional(),
});
router.post(
'/users',
validateRequest(createUserSchema),
async (req, res, next) => {
try {
const userData = req.body; // Type-safe after validation
// Database insert logic
res.status(201).json({ id: 1, ...userData });
} catch (error) {
next(error);
}
}
);
export default router;
Middleware Patterns
src/middleware/validation.ts:
import { Request, Response, NextFunction } from 'express';
import { z, ZodSchema } from 'zod';
export const validateRequest = (schema: ZodSchema) => {
return (req: Request, res: Response, next: NextFunction) => {
try {
req.body = schema.parse(req.body);
next();
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({
error: 'Validation failed',
details: error.errors,
});
} else {
next(error);
}
}
};
};
src/middleware/auth.ts:
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
interface JwtPayload {
userId: string;
email: string;
}
declare global {
namespace Express {
interface Request {
user?: JwtPayload;
}
}
}
export const authenticate = (
req: Request,
res: Response,
next: NextFunction
) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
Error Handling
src/middleware/errorHandler.ts:
import { Request, Response, NextFunction } from 'express';
export class AppError extends Error {
constructor(
public statusCode: number,
message: string,
public isOperational = true
) {
super(message);
Object.setPrototypeOf(this, AppError.prototype);
}
}
export const errorHandler = (
err: Error,
req: Request,
res: Response,
next: NextFunction
) => {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
error: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
});
}
console.error('Unexpected error:', err);
res.status(500).json({
error: 'Internal server error',
...(process.env.NODE_ENV === 'development' && {
message: err.message,
stack: err.stack,
}),
});
};
Fastify Patterns
Basic Fastify Server
src/server.ts:
import Fastify from 'fastify';
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
import { Type } from '@sinclair/typebox';
const fastify = Fastify({
logger: {
level: process.env.LOG_LEVEL || 'info',
},
}).withTypeProvider<TypeBoxTypeProvider>();
// Type-safe route with schema validation
fastify.route({
method: 'POST',
url: '/users',
schema: {
body: Type.Object({
email: Type.String({ format: 'email' }),
name: Type.String({ minLength: 2 }),
age: Type.Optional(Type.Integer({ minimum: 0 })),
}),
response: {
201: Type.Object({
id: Type.Number(),
email: Type.String(),
name: Type.String(),
}),
},
},
handler: async (request, reply) => {
const { email, name, age } = request.body;
// Auto-typed and validated
return reply.status(201).send({ id: 1, email, name });
},
});
const start = async () => {
try {
await fastify.listen({ port: 3000, host: '0.0.0.0' });
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
Plugin Pattern
src/plugins/database.ts:
import { FastifyPluginAsync } from 'fastify';
import fp from 'fastify-plugin';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
declare module 'fastify' {
interface FastifyInstance {
db: ReturnType<typeof drizzle>;
}
}
const databasePlugin: FastifyPluginAsync = async (fastify) => {
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle(pool);
fastify.decorate('db', db);
fastify.addHook('onClose', async () => {
await pool.end();
});
};
export default fp(databasePlugin);
Hooks Pattern
src/hooks/auth.ts:
import { FastifyRequest, FastifyReply } from 'fastify';
import jwt from 'jsonwebtoken';
declare module 'fastify' {
interface FastifyRequest {
user?: {
userId: string;
email: string;
};
}
}
export const authHook = async (
request: FastifyRequest,
reply: FastifyReply
) => {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) {
return reply.status(401).send({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as {
userId: string;
email: string;
};
request.user = decoded;
} catch (error) {
return reply.status(401).send({ error: 'Invalid token' });
}
};
Request Validation
Zod with Express
import { z } from 'zod';
const userSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
profile: z.object({
firstName: z.string(),
lastName: z.string(),
age: z.number().int().positive(),
}),
tags: z.array(z.string()).optional(),
});
type CreateUserInput = z.infer<typeof userSchema>;
router.post('/users', async (req, res) => {
const result = userSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error.format(),
});
}
const user: CreateUserInput = result.data;
// Type-safe user object
});
TypeBox with Fastify
import { Type, Static } from '@sinclair/typebox';
const UserSchema = Type.Object({
email: Type.String({ format: 'email' }),
password: Type.String({ minLength: 8 }),
profile: Type.Object({
firstName: Type.String(),
lastName: Type.String(),
age: Type.Integer({ minimum: 0 }),
}),
tags: Type.Optional(Type.Array(Type.String())),
});
type User = Static<typeof UserSchema>;
fastify.post('/users', {
schema: { body: UserSchema },
handler: async (request, reply) => {
const user: User = request.body; // Auto-validated
return { id: 1, ...user };
},
});
Authentication
JWT Authentication
src/services/auth.ts:
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
interface TokenPayload {
userId: string;
email: string;
}
export class AuthService {
private static JWT_SECRET = process.env.JWT_SECRET!;
private static JWT_EXPIRES_IN = '7d';
static async hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 10);
}
static async comparePassword(
password: string,
hash: string
): Promise<boolean> {
return bcrypt.compare(password, hash);
}
static generateToken(payload: TokenPayload): string {
return jwt.sign(payload, this.JWT_SECRET, {
expiresIn: this.JWT_EXPIRES_IN,
});
}
static verifyToken(token: string): TokenPayload {
return jwt.verify(token, this.JWT_SECRET) as TokenPayload;
}
}
Session-based Auth (Express)
import session from 'express-session';
import RedisStore from 'connect-redis';
import { createClient } from 'redis';
const redisClient = createClient({
url: process.env.REDIS_URL,
});
redisClient.connect();
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
},
})
);
declare module 'express-session' {
interface SessionData {
userId: string;
}
}
Database Integration
Drizzle ORM
src/db/schema.ts:
import { pgTable, serial, varchar, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 255 }).notNull(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
src/db/client.ts:
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './schema';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
export const db = drizzle(pool, { schema });
src/repositories/userRepository.ts:
import { eq } from 'drizzle-orm';
import { db } from '../db/client';
import { users, NewUser } from '../db/schema';
export class UserRepository {
static async create(data: NewUser) {
const [user] = await db.insert(users).values(data).returning();
return user;
}
static async findByEmail(email: string) {
return db.query.users.findFirst({
where: eq(users.email, email),
});
}
static async findById(id: number) {
return db.query.users.findFirst({
where: eq(users.id, id),
});
}
static async list(limit = 10, offset = 0) {
return db.query.users.findMany({
limit,
offset,
columns: {
passwordHash: false, // Exclude sensitive fields
},
});
}
}
Prisma
prisma/schema.prisma:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
passwordHash String @map("password_hash")
createdAt DateTime @default(now()) @map("created_at")
posts Post[]
@@map("users")
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int @map("author_id")
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now()) @map("created_at")
@@map("posts")
}
src/services/userService.ts:
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export class UserService {
static async createUser(data: { email: string; name: string; password: string }) {
const passwordHash = await AuthService.hashPassword(data.password);
return prisma.user.create({
data: {
email: data.email,
name: data.name,
passwordHash,
},
select: {
id: true,
email: true,
name: true,
createdAt: true,
},
});
}
static async getUserWithPosts(userId: number) {
return prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
},
},
});
}
}
API Design
REST API Patterns
Pagination:
import { z } from 'zod';
const paginationSchema = z.object({
page: z.coerce.number().int().positive().default(1),
limit: z.coerce.number().int().positive().max(100).default(20),
});
router.get('/users', async (req, res) => {
const { page, limit } = paginationSchema.parse(req.query);
const offset = (page - 1) * limit;
const [users, total] = await Promise.all([
UserRepository.list(limit, offset),
UserRepository.count(),
]);
res.json({
data: users,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
});
});
Filtering and Sorting:
const filterSchema = z.object({
status: z.enum(['active', 'inactive']).optional(),
search: z.string().optional(),
sortBy: z.enum(['createdAt', 'name', 'email']).default('createdAt'),
sortOrder: z.enum(['asc', 'desc']).default('desc'),
});
router.get('/users', async (req, res) => {
const filters = filterSchema.parse(req.query);
const users = await db.query.users.findMany({
where: and(
filters.status && eq(users.status, filters.status),
filters.search && ilike(users.name, `%${filters.search}%`)
),
orderBy: [
filters.sortOrder === 'asc'
? asc(users[filters.sortBy])
: desc(users[filters.sortBy]),
],
});
res.json({ data: users });
});
Error Response Format
interface ErrorResponse {
error: string;
message: string;
statusCode: number;
details?: unknown;
timestamp: string;
path: string;
}
export const formatError = (
err: AppError,
req: Request
): ErrorResponse => ({
error: err.name,
message: err.message,
statusCode: err.statusCode,
...(err.details && { details: err.details }),
timestamp: new Date().toISOString(),
path: req.path,
});
Environment Configuration
Type-safe Environment Variables
src/config/env.ts:
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
REDIS_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
});
export type Env = z.infer<typeof envSchema>;
export const env = envSchema.parse(process.env);
Usage:
import { env } from './config/env';
const port = env.PORT; // Type-safe, validated
Testing
Vitest Setup
vitest.config.ts:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
setupFiles: ['./src/tests/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
},
},
});
Integration Tests with Supertest
src/tests/users.test.ts:
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import request from 'supertest';
import { app } from '../server';
import { db } from '../db/client';
describe('User API', () => {
beforeAll(async () => {
// Setup test database
await db.delete(users);
});
afterAll(async () => {
// Cleanup
});
it('should create a new user', async () => {
const response = await request(app)
.post('/users')
.send({
email: 'test@example.com',
name: 'Test User',
password: 'password123',
})
.expect(201);
expect(response.body).toMatchObject({
email: 'test@example.com',
name: 'Test User',
});
expect(response.body).toHaveProperty('id');
expect(response.body).not.toHaveProperty('passwordHash');
});
it('should return 400 for invalid email', async () => {
const response = await request(app)
.post('/users')
.send({
email: 'invalid-email',
name: 'Test User',
password: 'password123',
})
.expect(400);
expect(response.body).toHaveProperty('error');
});
});
Unit Tests
src/services/auth.test.ts:
import { describe, it, expect } from 'vitest';
import { AuthService } from './auth';
describe('AuthService', () => {
it('should hash password correctly', async () => {
const password = 'mySecurePassword123';
const hash = await AuthService.hashPassword(password);
expect(hash).not.toBe(password);
expect(hash.length).toBeGreaterThan(50);
});
it('should verify password correctly', async () => {
const password = 'mySecurePassword123';
const hash = await AuthService.hashPassword(password);
const isValid = await AuthService.comparePassword(password, hash);
expect(isValid).toBe(true);
const isInvalid = await AuthService.comparePassword('wrongPassword', hash);
expect(isInvalid).toBe(false);
});
it('should generate valid JWT token', () => {
const token = AuthService.generateToken({
userId: '123',
email: 'test@example.com',
});
expect(token).toBeTruthy();
const decoded = AuthService.verifyToken(token);
expect(decoded).toMatchObject({
userId: '123',
email: 'test@example.com',
});
});
});
Production Deployment
Docker Setup
Dockerfile:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "dist/server.js"]
docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- REDIS_URL=redis://redis:6379
- JWT_SECRET=${JWT_SECRET}
depends_on:
- db
- redis
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
PM2 Clustering
ecosystem.config.js:
module.exports = {
apps: [{
name: 'api',
script: './dist/server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
}],
};
Best Practices
Project Structure
src/
├── server.ts # Entry point
├── config/
│ └── env.ts # Environment config
├── routes/
│ ├── index.ts # Route aggregator
│ ├── users.ts
│ └── posts.ts
├── middleware/
│ ├── auth.ts
│ ├── validation.ts
│ └── errorHandler.ts
├── services/
│ ├── auth.ts
│ └── user.ts
├── repositories/
│ └── userRepository.ts
├── db/
│ ├── client.ts
│ └── schema.ts
├── types/
│ └── index.ts
└── tests/
├── setup.ts
├── users.test.ts
└── auth.test.ts
Key Principles
- Separation of Concerns: Routes → Controllers → Services → Repositories
- Type Safety: Use TypeScript strict mode, Zod for runtime validation
- Error Handling: Centralized error handler, custom error classes
- Security: Helmet, rate limiting, input validation, CORS
- Logging: Structured logging (pino, winston), request IDs
- Testing: Unit tests for services, integration tests for APIs
- Documentation: OpenAPI/Swagger for API documentation
Express vs Fastify
Use Express when:
- Large ecosystem of middleware needed
- Team familiarity is priority
- Prototype/MVP development
- Legacy codebase compatibility
Use Fastify when:
- Performance is critical (2-3x faster)
- Type safety is important (built-in TypeScript support)
- Schema validation required (JSON Schema built-in)
- Modern async/await patterns preferred
- Plugin architecture needed
Performance Tips
- Use connection pooling for databases
- Implement caching (Redis, in-memory)
- Enable compression (gzip, brotli)
- Use clustering for CPU-intensive tasks
- Implement rate limiting
- Optimize database queries (indexes, query analysis)
- Use CDN for static assets
- Enable HTTP/2 in production
FAQ
What does the nodejs-backend-typescript skill do?
Node.js backend development with TypeScript, Express/Fastify servers, routing, middleware, and database integration
How do I install nodejs-backend-typescript?
Run npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill nodejs-backend-typescript in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/nodejs-backend-typescript/ (personal) or .claude/skills/nodejs-backend-typescript/ (project).
Which AI agents can use nodejs-backend-typescript?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is nodejs-backend-typescript safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains nodejs-backend-typescript?
It is published by bobmatnyc in bobmatnyc/claude-mpm-skills. It has 745 recorded installs.
More from bobmatnyc/claude-mpm-skills
drizzle-orm
bobmatnyc/claude-mpm-skills
Type-safe SQL ORM for TypeScript with zero runtime overhead
playwright-e2e-testing
bobmatnyc/claude-mpm-skills
Playwright modern end-to-end testing framework with cross-browser automation, auto-wait, and built-in test runner
drizzle
bobmatnyc/claude-mpm-skills
Type-safe SQL ORM for TypeScript with zero runtime overhead
pytest
bobmatnyc/claude-mpm-skills
pytest - Python's most powerful testing framework with fixtures, parametrization, plugins, and framework integration for FastAPI, Django, Flask
drizzle-migrations
bobmatnyc/claude-mpm-skills
Migration-first database development workflow using Drizzle ORM for TypeScript/J...
tailwind-css
bobmatnyc/claude-mpm-skills
Tailwind CSS utility-first framework for rapid UI development with responsive design and dark mode
Popular backend & apis skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
entra-app-registration
microsoft/azure-skills
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.
google-agents-cli-adk-code
google/agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).
embedded-captions
heygen-com/hyperframes
Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.
entra-agent-id
microsoft/azure-skills
Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).
neon
neondatabase/agent-skills
Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.