ShortIQ

ShortIQ

AI

50 AI Prompts for Node.js REST API Development

A developer-tested set of 50 prompts for building production-grade REST APIs with Node.js, Express, TypeScript, and either MongoDB or PostgreSQL — covering routing, auth, validation, testing, security, and deployment.

May 28, 2026ShortIQ Editorial Team

Advertisement

Before You Start: How to Get Clean Output From These Prompts

The most common mistake when using AI to generate API code is asking for too much in one prompt. "Build me a user authentication system" produces something that technically compiles but does not fit your folder structure, uses the wrong error format, and skips edge cases you will hit in production. These prompts are written to be narrow enough that the output is immediately useful.

They assume Node.js 20+, Express 4, TypeScript, and either Mongoose with MongoDB or Prisma with PostgreSQL. Most prompts work with either database — the database-specific ones are labeled. Each prompt is designed to generate one file or one clearly scoped piece of logic, not an entire module at once.

The habit that makes the biggest difference: tell the AI the exact shape of your error response before asking it to write error handling. If your API returns { success: false, message: string, errors?: object } on failure, paste that structure into the prompt. The AI will then match it consistently across all generated code rather than inventing its own format every time.

  • Paste your existing error handler and response format before asking for route code
  • For database prompts, specify whether you are using Mongoose or Prisma upfront
  • If output uses a different naming convention than your codebase, add "follow camelCase for variables and PascalCase for types" to the prompt
  • Run generated code through your linter before wiring it into routes — most issues are caught immediately

Project Setup and Configuration (Prompts 1–8)

Prompt 1: "Set up a Node.js Express API with TypeScript. Create a package.json with scripts for dev (ts-node-dev with --respawn), build (tsc), and start (node dist/server.js). Install express, typescript, @types/express, @types/node, ts-node-dev, dotenv, and zod. Create a tsconfig.json targeting ES2020 with strict mode enabled, rootDir set to src, and outDir set to dist. Create src/server.ts that starts the Express app on process.env.PORT with a fallback of 5000."

Prompt 2: "Create src/app.ts as the Express application factory. Import and apply express.json(), express.urlencoded({ extended: true }), and a cors middleware allowing requests from process.env.ALLOWED_ORIGIN. Mount a /api/v1 router. Add a catch-all 404 handler that returns { success: false, message: 'Route not found' }. Export the app instance without starting the server — server.ts handles that separately." — Prompt 3: "Set up environment variable validation using zod in src/config/env.ts. Validate: PORT (number with default 5000), NODE_ENV (enum: development, production, test), MONGO_URI or DATABASE_URL, JWT_SECRET (min 32 chars), JWT_EXPIRES_IN (string, default 7d), ALLOWED_ORIGIN (URL string). Throw a descriptive startup error listing every missing variable if validation fails."

Prompt 4: "Create a centralised error handling setup. Define a custom AppError class in src/utils/app-error.ts that extends Error with a statusCode, isOperational boolean, and optional errors array for field-level validation errors. Create a global error handler middleware in src/middleware/error-handler.ts that catches AppError instances and formats them as { success: false, message, errors? }. In production, return a generic message for non-operational errors and log the stack trace without exposing it." — Prompt 5: "Set up a structured logger in src/utils/logger.ts using the winston library. Log JSON format in production and a readable colorized format in development. Create log levels for error, warn, info, and debug. Export the logger and replace all console.log calls in the app with logger.info or logger.error."

  • Prompt 6: "Create a typed asyncHandler wrapper in src/utils/async-handler.ts. It should wrap an async Express route handler and forward any thrown errors to next() automatically. This removes the need for try/catch in every controller. Show the correct TypeScript signature for the wrapper and an example of using it in a route."
  • Prompt 7: "Set up path aliases in tsconfig.json so imports use @/utils, @/middleware, @/models, @/routes, and @/config instead of relative paths. Show how to configure ts-node-dev and the compiled build to resolve these aliases correctly using tsconfig-paths."
  • Prompt 8: "Create a src/routes/index.ts that imports and mounts all route modules under the /api/v1 prefix. Add a /health endpoint that returns { status: 'ok', uptime: process.uptime(), timestamp: new Date() }. This endpoint should not require authentication and is used for load balancer health checks."

Routing and Controller Patterns (Prompts 9–15)

Prompt 9: "Create a user resource with the standard CRUD routes. Folder: src/routes/user.routes.ts for route definitions and src/controllers/user.controller.ts for handler logic. Routes: GET /users, GET /users/:id, POST /users, PUT /users/:id, DELETE /users/:id. Controllers should be thin — call a service function and return the result. Do not put database queries directly in controllers." — Prompt 10: "Create a user service in src/services/user.service.ts with functions: getAllUsers(query), getUserById(id), createUser(data), updateUser(id, data), deleteUser(id). Each function should throw an AppError with the appropriate status code when the operation fails — 404 for not found, 409 for duplicate email. Keep all database interaction in the service layer."

Prompt 11: "Add query parameter support to the GET /users endpoint. Accept page (default 1), limit (default 20, max 100), sortBy (default createdAt), sortOrder (asc or desc), and search (optional string that matches against name and email). Validate all query params with zod before they reach the service. Return the results with a pagination object: { total, page, limit, totalPages, hasNextPage }." — Prompt 12: "Create a route parameter validation middleware. Write a validateId middleware that checks whether req.params.id is a valid MongoDB ObjectId or PostgreSQL UUID (based on your database choice). If invalid, call next with an AppError 400 before the request reaches the controller. Apply it to all routes with :id parameters."

Prompt 13: "Set up API versioning. Show how to structure routes so that /api/v1 and /api/v2 can coexist. The v1 router lives in src/routes/v1/index.ts and the v2 router in src/routes/v2/index.ts. Demonstrate adding a new response field in v2 that does not break v1 consumers." — Prompt 14: "Create a request logger middleware in src/middleware/request-logger.ts. Log the HTTP method, URL, status code, and response time in milliseconds for every request. Use the logger utility instead of console. Skip logging for the /health endpoint to avoid noise in production logs." — Prompt 15: "Add response envelope middleware that wraps all successful JSON responses in { success: true, data: originalResponse }. Apply it globally so controllers can return raw data and the envelope is added automatically. Show how to make an exception for the /health endpoint which should not be wrapped."

Authentication and JWT Middleware (Prompts 16–22)

Prompt 16: "Create a complete authentication flow. Routes: POST /api/v1/auth/register and POST /api/v1/auth/login. Register: validate email and password, hash password with bcrypt (12 rounds), create user, return a signed JWT. Login: find user by email, compare password with bcrypt.compare, return a signed JWT on success or 401 on failure. Sign tokens with jsonwebtoken using JWT_SECRET and JWT_EXPIRES_IN from the environment config." — Prompt 17: "Write a protect middleware in src/middleware/auth.ts. Read the token from the Authorization header (Bearer scheme). Verify it with jsonwebtoken. Attach the decoded user payload to req.user. Return 401 if the header is missing, the token is expired, or verification fails. Use the AppError class so the error handler formats the response consistently."

Prompt 18: "Add a refresh token system. On login, issue both an access token (15 minutes) and a refresh token (7 days). Store the refresh token hash in the database tied to the user record. Add a POST /auth/refresh route that accepts the refresh token, validates it against the stored hash, and issues a new access token. Add a POST /auth/logout route that invalidates the stored refresh token." — Prompt 19: "Create a role-based access control middleware in src/middleware/restrict-to.ts. It should accept one or more role strings (e.g. restrictTo('admin', 'manager')) and return a middleware function that checks req.user.role. If the role is not in the allowed list, return 403 with the message 'You do not have permission to perform this action.'"

Prompt 20: "Implement a password reset flow. Routes: POST /auth/forgot-password (accepts email, generates a reset token, sends an email) and POST /auth/reset-password/:token (accepts newPassword, validates the token, updates the password). Store the reset token as a SHA-256 hash in the database with a 10-minute expiry. Never store or log the raw token." — Prompt 21: "Add an email verification step to registration. After creating the user, generate a verification token, store its hash in the database, and send a verification email. Add GET /auth/verify-email/:token that finds the user by hashed token, marks the email as verified, and clears the token. The protect middleware should check isEmailVerified and return 403 with a clear message if verification is pending." — Prompt 22: "Create a getCurrentUser route at GET /auth/me. It should use the protect middleware and return the current user from req.user — fetching fresh data from the database rather than using the JWT payload alone, since roles or subscription status may have changed since the token was issued."

Validation and Error Handling (Prompts 23–29)

Prompt 23: "Set up a zod validation middleware factory in src/middleware/validate.ts. It should accept a zod schema, validate req.body against it, and call next() on success. On failure, format the zod error into an array of { field, message } objects and pass an AppError 400 with those errors attached. Show how to use it on a route: router.post('/users', validate(createUserSchema), userController.create)." — Prompt 24: "Write zod schemas for the user resource in src/schemas/user.schema.ts. createUserSchema: email (valid email), password (min 8 chars, at least one number and one uppercase letter), name (min 2 chars). updateUserSchema: same fields but all optional. loginSchema: email and password only. Export all three."

Prompt 25: "Create a global unhandled rejection and uncaught exception handler in src/server.ts. For uncaughtException, log the error and immediately exit the process with code 1 — the application is in an unknown state. For unhandledRejection, log the error and gracefully close the HTTP server before exiting. This prevents the server from silently continuing in a broken state." — Prompt 26: "Add Mongoose-specific error handling to the global error handler. Catch CastError (invalid ObjectId) and return 400. Catch duplicate key error (code 11000) and return 409 with a message identifying which field is duplicated. Catch ValidationError and format the inner errors as field-level messages. These are operational errors — format them like AppError instances."

Prompt 27: "Write a sanitizeInput middleware that strips MongoDB operator keys (any key starting with $) from req.body and req.query recursively. This prevents basic NoSQL injection attacks. Apply it globally before route handlers. Add a test case showing that a body containing { email: { $gt: '' } } is sanitized to { email: {} }." — Prompt 28: "Create a request size limiter. Use the express built-in limit option on express.json({ limit: '10kb' }) and express.urlencoded({ limit: '10kb' }). Add a file upload size check if multer is used. Return 413 with a clear error message when the limit is exceeded." — Prompt 29: "Write a not-found handler and a catch-all error handler that covers the case where a route throws a non-AppError (such as a plain TypeError or a third-party library error). In development, include the stack trace in the response for debugging. In production, log it and return a generic 500 message."

Database Integration (Prompts 30–36)

Prompt 30 (Mongoose): "Create a MongoDB connection utility in src/config/database.ts using Mongoose. Connect using process.env.MONGO_URI. Log a success message when connected. On connection error, log the error and exit the process — do not let the API start without a database connection. Add an event listener for disconnection that logs a warning. Call this from server.ts before starting the HTTP server." — Prompt 30 (Prisma): "Create a database connection utility in src/config/database.ts that exports the Prisma client singleton and a connectDB() function. Call connectDB() in server.ts before starting the HTTP server. Log success on connection and exit the process on failure. Handle the same global caching pattern for development hot reload."

Prompt 31 (Mongoose): "Create a User model in src/models/user.model.ts with Mongoose. Fields: name (String, required), email (String, required, unique, lowercase), password (String, required, minlength 8, select: false), role (enum: user, admin, default: user), isEmailVerified (Boolean, default: false), createdAt and updatedAt (timestamps: true). Add a pre-save hook that hashes the password only when the password field is modified. Add an instance method comparePassword(candidatePassword) that uses bcrypt.compare." — Prompt 32: "Add soft delete support to the User model (or Prisma schema). Add a deletedAt nullable timestamp field. Create a middleware that automatically filters out deleted records from all find queries. Add a hardDelete method for admin operations. Show how to bypass the soft delete filter when an admin endpoint needs to see deleted records."

Prompt 33: "Write a generic repository class in src/repositories/base.repository.ts with findAll, findById, create, update, and delete methods. It should accept a Mongoose model or Prisma delegate as a constructor argument. This pattern lets controllers stay consistent regardless of which model they use. Show a concrete UserRepository that extends it." — Prompt 34: "Add database query performance logging. Log any query that takes more than 500ms with the query details and execution time. For Mongoose, use the explain() output. For Prisma, use the query events from the Prisma client constructor. This is only for development and staging — disable it in production." — Prompt 35: "Create a database seeder script in src/scripts/seed.ts that creates 10 sample users with hashed passwords and 1 admin user. The script should check if data already exists before inserting to make it idempotent. Add a seed npm script to package.json. Show how to run it with ts-node and how to clear seeded data." — Prompt 36: "Write a database migration strategy for production. Show how to manage schema changes for Mongoose (using a migration library or manual scripts) or Prisma (prisma migrate deploy). Create a pre-deploy checklist that verifies the migration ran, the connection is healthy, and the most critical models return expected data before the API takes live traffic."

Testing With Jest and Supertest (Prompts 37–43)

Prompt 37: "Set up Jest with TypeScript for a Node.js Express API. Install jest, ts-jest, @types/jest, and supertest. Create jest.config.ts with ts-jest as the preset, testEnvironment set to node, and a setup file at src/tests/setup.ts. The setup file should connect to a test MongoDB URI (or use an in-memory database), clear all collections before each test suite, and disconnect after all tests run." — Prompt 38: "Write integration tests for POST /api/v1/auth/register using supertest. Test cases: registers a new user and returns 201 with a token, returns 400 if email is already registered, returns 400 if password does not meet requirements, returns 400 if required fields are missing. Use supertest to make HTTP requests against the actual Express app — do not mock the database in integration tests."

Prompt 39: "Write unit tests for the user service functions. Mock the User model using jest.mock. Test: getUserById returns the user when found, throws AppError 404 when user does not exist, createUser throws 409 when email is duplicate. For each test, verify the mock was called with the correct arguments and that the thrown errors have the correct statusCode." — Prompt 40: "Write tests for the protect authentication middleware. Test cases: allows requests with a valid JWT, returns 401 when no Authorization header is present, returns 401 when token is expired (use a token signed with a past expiry), returns 401 when the token signature is invalid. Mock jwt.verify where needed to control the output without depending on real tokens." — Prompt 41: "Create a test helper file at src/tests/helpers.ts with: generateTestUser(overrides?) that creates a user in the test database and returns the user and a signed JWT, cleanDatabase() that truncates all collections, and makeAuthRequest(app, method, url, token, body?) that sets the Authorization header automatically."

Prompt 42: "Write tests for the user CRUD endpoints. Test: GET /users returns paginated results, GET /users/:id returns 404 for a non-existent id, PUT /users/:id returns 403 when a non-admin user tries to update another user, DELETE /users/:id returns 404 for invalid ObjectId. Use the test helpers to set up authenticated requests." — Prompt 43: "Set up a test coverage report. Add a coverage script to package.json that runs jest --coverage --coverageDirectory=coverage. Configure coverage thresholds in jest.config.ts: branches 70%, functions 80%, lines 80%. Add a pre-commit hook using husky that runs the test suite before allowing a commit."

Security and Production Hardening (Prompts 44–50)

Prompt 44: "Apply security HTTP headers using Helmet. Install and configure it in src/app.ts. Customize the Content Security Policy to allow API responses. Add express-mongo-sanitize for NoSQL injection prevention and hpp for HTTP parameter pollution protection. Apply all three globally before route handlers." — Prompt 45: "Set up rate limiting using express-rate-limit. Create a general limiter of 100 requests per 15 minutes applied to all routes. Create a stricter auth limiter of 10 requests per 15 minutes applied only to the /auth/login and /auth/register routes. Use a Redis store (via rate-limit-redis) in production so limits work correctly across multiple server instances."

Prompt 46: "Add CORS configuration. In development, allow all origins. In production, only allow origins listed in ALLOWED_ORIGINS (a comma-separated env var). Allow the headers Content-Type and Authorization. Allow methods GET, POST, PUT, PATCH, DELETE, OPTIONS. Handle preflight OPTIONS requests correctly so browsers do not block API calls." — Prompt 47: "Create a graceful shutdown handler for the Node.js process. When SIGTERM or SIGINT is received, stop accepting new connections, wait up to 10 seconds for existing requests to finish, close the database connection, then exit with code 0. Log each step. This prevents dropped requests during deployment or container restarts."

Prompt 48: "Write a Dockerfile for the Node.js API. Use a multi-stage build: stage 1 installs all dependencies and compiles TypeScript to dist/. Stage 2 copies only the compiled output and production dependencies. Use node:20-alpine as the base image. Run the process as a non-root user. Expose port 5000. The final image should be under 150MB." — Prompt 49: "Create a docker-compose.yml for local development. Services: api (builds from the local Dockerfile, mounts the src folder for hot reload with ts-node-dev, sets all required env vars), mongo or postgres (official image with a named volume for data persistence), and optionally redis (for rate limiting or sessions). Show how to run the whole stack with docker-compose up." — Prompt 50: "Write a production deployment checklist for the Node.js API. Cover: environment variables to verify before deploying, the Mongoose or Prisma migration step to run before starting the new server version, how to do a zero-downtime deploy with PM2 cluster mode or Kubernetes rolling update, what health check endpoint the load balancer should poll, and how to roll back if the new version fails the health check within 60 seconds."

FAQ

Do these prompts work with Fastify instead of Express?

Most structural prompts work with minor adjustments. Fastify uses a different plugin system and schema validation approach, so validation and middleware prompts need the most changes. The auth, service layer, and database prompts translate almost directly.

Should I use Mongoose or Prisma for a new Node.js API?

Prisma with PostgreSQL is the better default for new projects in 2026. The type safety is significantly stronger, migration tooling is more mature, and the query API is easier to test. Mongoose is still a good choice if your team has deep MongoDB experience or your data is genuinely document-shaped.

Can these prompts generate tests that actually pass?

Generally yes, but generated tests often need the import paths and mock setup adjusted to match your project structure. Run the tests immediately after generation and fix any path or type errors before moving on — they are usually small fixes.

Is JWT the right auth approach for a REST API?

JWT with short expiry access tokens and refresh token rotation is solid for most APIs. Session-based auth in Redis is worth considering if you need instant revocation — JWT cannot be truly invalidated before expiry without a denylist, which adds infrastructure complexity.

How do I adapt these prompts for a microservices setup?

The individual service prompts work without changes. Add prompts specifically for the cross-service concerns: service-to-service auth (shared secrets or mTLS), event publishing to a message broker, and distributed tracing headers. Those are different enough to warrant their own prompt set.

Related free tools

If you want to turn this topic into action, use one of ShortIQ's free tools for campaign planning, UTM structure, or QR distribution.

Continue Reading

Explore more guides on link shortener SaaS strategy, Bitly alternatives, and white label link management.

Was this article helpful?

Tell us if this guide solved the problem or what was still missing. We use this to improve the blog and only follow up if you explicitly allow it.

We use this to improve tutorials, examples, and technical depth.