TypeScript for Backend (Node.js)
TypeScript for Backend (Node.js)
When you ask AI to build an API or backend service, it will typically generate a Node.js server with TypeScript. This tutorial covers the patterns you'll see in backend code.
Backend Frameworks
AI will likely use one of these popular Node.js frameworks:
| Framework | Style | When You'll See It |
|---|---|---|
| Express | Minimal, flexible | Most common, lots of examples |
| Next.js API Routes | File-based routing | If you're already using Next.js |
| Fastify | Fast, schema-based | Performance-focused projects |
| Hono | Lightweight, modern | Edge/serverless projects |
The concepts are similar across all of them. We'll use Express for examples since it's the most common.
A Basic Express Server
import express, { Request, Response } from "express"; const app = express(); app.use(express.json()); // Parse JSON request bodies // A simple route app.get("/api/hello", (req: Request, res: Response) => { res.json({ message: "Hello, World!" }); }); app.listen(3000, () => { console.log("Server running on port 3000"); });
Key things to notice:
RequestandResponseare imported from Express — these are the TypeScript typesreqcontains the incoming request (URL, headers, body)resis used to send a response back.json()sends a JSON response
Typed Request and Response
For real APIs, you'll want to type what the request and response look like:
// Define your data shapes interface CreateUserBody { name: string; email: string; password: string; } interface UserResponse { id: string; name: string; email: string; createdAt: string; } // Type the route handler app.post("/api/users", async (req: Request<{}, {}, CreateUserBody>, res: Response<UserResponse>) => { const { name, email, password } = req.body; // TypeScript knows these fields exist // Create user in database... const user: UserResponse = { id: "abc123", name, email, createdAt: new Date().toISOString(), }; res.status(201).json(user); });
What to ask your AI: "Create a REST API endpoint for [resource]. It should accept [these fields] and return [this data]."
Route Parameters and Query Strings
URL Parameters
// Route: /api/users/:id app.get("/api/users/:id", async (req: Request<{ id: string }>, res: Response) => { const userId = req.params.id; // TypeScript knows 'id' exists and is a string // Fetch user from database... res.json(user); });
Query Parameters
// Route: /api/products?category=electronics&page=2 interface ProductQuery { category?: string; page?: string; limit?: string; } app.get("/api/products", async (req: Request<{}, {}, {}, ProductQuery>, res: Response) => { const { category, page = "1", limit = "10" } = req.query; // Fetch products with filters... res.json(products); });
CRUD API Pattern
The most common thing you'll ask AI to build is a CRUD API (Create, Read, Update, Delete). Here's the pattern:
interface Product { id: string; name: string; price: number; description: string; } type CreateProductInput = Omit<Product, "id">; // CREATE — POST /api/products app.post("/api/products", async (req: Request<{}, {}, CreateProductInput>, res: Response) => { const product = await db.createProduct(req.body); res.status(201).json(product); }); // READ ALL — GET /api/products app.get("/api/products", async (req: Request, res: Response) => { const products = await db.getProducts(); res.json(products); }); // READ ONE — GET /api/products/:id app.get("/api/products/:id", async (req: Request<{ id: string }>, res: Response) => { const product = await db.getProduct(req.params.id); if (!product) return res.status(404).json({ error: "Not found" }); res.json(product); }); // UPDATE — PUT /api/products/:id app.put("/api/products/:id", async (req: Request<{ id: string }, {}, Partial<CreateProductInput>>, res: Response) => { const product = await db.updateProduct(req.params.id, req.body); res.json(product); }); // DELETE — DELETE /api/products/:id app.delete("/api/products/:id", async (req: Request<{ id: string }>, res: Response) => { await db.deleteProduct(req.params.id); res.status(204).send(); });
What to ask your AI: "Build a complete CRUD API for [resource] with [these fields]. Include proper error handling and TypeScript types."
Useful TypeScript Utility Types for Backend
You'll see these in AI-generated backend code:
interface User { id: string; name: string; email: string; password: string; createdAt: Date; } // Omit — remove fields (great for hiding password in responses) type PublicUser = Omit<User, "password">; // Pick — select only certain fields type UserPreview = Pick<User, "id" | "name">; // Partial — make all fields optional (great for update endpoints) type UpdateUserInput = Partial<Omit<User, "id" | "createdAt">>; // Required — make all fields required type CompleteUser = Required<User>;
| Utility | What It Does | Common Use |
|---|---|---|
Omit<T, keys> | Remove fields | Hide sensitive data |
Pick<T, keys> | Keep only these fields | Lightweight responses |
Partial<T> | Make all fields optional | Update/patch endpoints |
Required<T> | Make all fields required | Strict validation |
What to ask your AI: "I have this User interface. Create a type for the API response that excludes the password field."
Database Operations
Whether using SQL (PostgreSQL, MySQL) or NoSQL (MongoDB, Firestore), the patterns are similar:
// A typical database service interface TodoService { getAll(): Promise<Todo[]>; getById(id: string): Promise<Todo | null>; create(data: CreateTodoInput): Promise<Todo>; update(id: string, data: Partial<CreateTodoInput>): Promise<Todo>; delete(id: string): Promise<void>; }
Notice the return types:
Promise<Todo[]>— returns multiple todosPromise<Todo | null>— returns one todo or null (not found)Promise<void>— returns nothing (just deletes)
Middleware
Middleware functions run before your route handler — for things like authentication, logging, or validation:
import { Request, Response, NextFunction } from "express"; // Authentication middleware const requireAuth = (req: Request, res: Response, next: NextFunction) => { const token = req.headers.authorization; if (!token) { return res.status(401).json({ error: "No token provided" }); } // Verify token... next(); // Continue to the route handler }; // Use it on specific routes app.get("/api/profile", requireAuth, (req, res) => { res.json({ user: req.user }); });
What to ask your AI: "Add authentication middleware to my API. Protect the [these routes] endpoints."
Environment Variables
Backend code often uses environment variables for secrets and configuration:
// Type your environment variables interface Env { DATABASE_URL: string; JWT_SECRET: string; PORT: string; } // Access them safely const port = process.env.PORT || "3000"; const dbUrl = process.env.DATABASE_URL; if (!dbUrl) { throw new Error("DATABASE_URL environment variable is required"); }
What to ask your AI: "Set up environment variable handling with a .env file. I need variables for [database URL, API key, etc.]."
Prompts for Building Backend with AI
API Setup:
- "Create an Express server with TypeScript. Set up CORS, JSON parsing, and error handling middleware."
- "Set up a Node.js project with TypeScript, Express, and [database]. Include the project structure and configuration files."
Endpoints:
- "Build a REST API for a blog with posts and comments. Posts have a title, body, author, and published date. Comments belong to a post."
- "Create an authentication API with signup, login, and logout endpoints using JWT tokens."
Database:
- "Set up a database connection to [PostgreSQL/MongoDB/Firestore] with TypeScript types for all my models."
- "Create database service functions for CRUD operations on [resource]."
Security:
- "Add input validation to my API endpoints using Zod. Validate the request body before processing."
- "Add rate limiting and request logging to my Express server."
What's Next?
You now understand TypeScript on both frontend and backend. The final tutorial is your TypeScript Cheat Sheet — a quick reference with all the types and patterns, plus AI prompts organized by task.