Skip to content

Scenario-Based MEAN Stack Interview Questions

46 questions with answers. Real-work situations — broken dashboards, flaky tests, unclear requirements — and how a strong mean stack developer would handle them.

How to approach these questions

Clarify the situation, list likely causes, say how you would check each one, then give the fix and how you would stop it happening again.

Solved — scenario-basedStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

An API sometimes returns null for a nested field and the Angular app crashes with cannot read properties of undefined. How do you fix it properly?

Save to ▾
Show answer
Model the API response accurately in TypeScript, marking the field as optional or nullable, and keep strictNullChecks on so the compiler forces you to handle it. Use optional chaining in code and templates, provide defaults with ??, and show a fallback in the UI with @if. Also agree with the backend team on the contract, and validate responses at the boundary if the data is untrusted.
02

A teammate uses any everywhere to silence TypeScript errors. How would you address it?

Save to ▾
Show answer
Explain that any hides real bugs and removes autocompletion, then show concrete fixes: proper interfaces for API models, unknown with narrowing for untrusted data, and generics for reusable helpers. Enable the noImplicitAny compiler option and the ESLint no-explicit-any rule as a warning, fix the most critical files first, and add a review guideline. Keep the conversation constructive by pairing on one module together.
03

Frontend and backend models keep drifting apart, causing runtime bugs. How would you keep them in sync in a MEAN project?

Save to ▾
Show answer
Share types from a single source. In a monorepo, for example with Nx, put DTO interfaces in a shared library used by both the Express API and Angular. Alternatively generate an OpenAPI spec from the backend and generate Angular client types from it, or define schemas once with zod and infer types. Add contract tests in CI so a breaking change fails the build.
04

You are migrating a large JavaScript Node.js codebase to TypeScript. How would you approach it?

Save to ▾
Show answer
Migrate gradually. Add TypeScript with allowJs so JS and TS files coexist, start with loose settings and turn on strict options step by step. Convert leaf modules such as utilities and models first, then services and routes. Install type packages like @types/express, define shared interfaces for request and response shapes, and run type checks in CI. Avoid a big-bang rewrite that blocks feature work.
05

You need a dropdown value that can only be 'pending', 'shipped' or 'delivered'. How do you model it so invalid values are impossible?

Save to ▾
Show answer
Use a string literal union, type OrderStatus = 'pending' | 'shipped' | 'delivered', or a const array with as const and derive the type from it, so the list can also render the dropdown options. Type the component property and API model with OrderStatus. The compiler then rejects typos like 'shiped'. On the backend, mirror the same values with a Mongoose enum validator.
06

A product list with 2,000 rows re-renders slowly whenever any data changes. How do you optimise it?

Save to ▾
Show answer
Use @for with a proper track expression, such as track product._id, so Angular reuses DOM nodes instead of recreating them. Switch the component to OnPush and update data immutably or with signals. Replace function calls in templates with computed signals or pure pipes. For very long lists, use virtual scrolling from the Angular CDK and paginate on the server.
07

You see ExpressionChangedAfterItHasBeenCheckedError in development. What does it mean and how do you fix it?

Save to ▾
Show answer
In development Angular runs an extra check to make sure a binding did not change after it was already checked in the same cycle. The error usually appears when a child or a lifecycle hook like ngAfterViewInit changes parent data during rendering. Fix it by moving the update earlier, such as into ngOnInit, deriving the value with a computed signal, or restructuring data flow, rather than hiding it with setTimeout.
08

The same card layout with an image, title and actions is copied across ten pages. How would you refactor it?

Save to ▾
Show answer
Create a reusable standalone card component with inputs for title, image and data, and use content projection with ng-content and named slots, like select='[card-actions]', so each page can pass its own buttons. Put shared styles in the component. Add a directive or pipe for repeated formatting. This reduces duplication, keeps styling consistent and makes future design changes a one-place edit.
09

Your Angular app's initial bundle is very large and the first page loads slowly. What steps would you take?

Save to ▾
Show answer
Run a production build with source map analysis to find large dependencies. Lazy load feature routes with loadComponent or loadChildren, use @defer for heavy widgets, remove unused libraries and replace large ones, and avoid importing whole utility libraries. Check budgets in angular.json, enable server-side rendering with hydration for public pages, optimise images with NgOptimizedImage, and serve compressed assets through a CDN.
10

An Angular page needs to show different content for admin and normal users. How would you implement it in the template?

Save to ▾
Show answer
Expose the current user's role from an auth service as a signal and read it in the component with a computed value such as isAdmin. In the template, use @if (isAdmin()) to render admin sections and @else for the normal view, or create a small structural directive for permission checks if this repeats. Remember to also protect routes with guards and enforce permissions on the Express API.
11

A third-party chart component inside an Angular page causes performance issues because it triggers too many change detection cycles. What can you do?

Save to ▾
Show answer
In Zone.js apps, run the chart's frequent events outside Angular using NgZone.runOutsideAngular, then re-enter only when the UI must update. Make the host component OnPush, pass immutable inputs and use @defer so the chart loads only when visible. Moving to zoneless change detection solves the root cause, because third-party timers no longer trigger global checks.
12

Users lose a long half-filled form when they accidentally click a menu link. How would you prevent this?

Save to ▾
Show answer
Add a canDeactivate guard on the form route. The guard checks whether the form is dirty and unsaved through a method or signal on the component, and if so asks the user to confirm leaving with a dialog. Return true to allow or false to stay. Also handle browser refresh and tab close with a beforeunload listener, and consider auto-saving drafts to local storage or the server.
13

Admin routes are protected by a guard, but the admin feature code is still downloaded by every user. How do you fix this?

Save to ▾
Show answer
canActivate does not stop the route from matching, so its lazy chunk can still be downloaded, for example by a preloading strategy. Use a canMatch guard on the lazy admin route instead, so the route definition does not match for non-admin users and its code is never loaded; they fall through to another route or a not-authorised page. Keep the real security on the API, since frontend code is always visible.
14

Two components on different pages need to share the selected city filter. How would you implement it?

Save to ▾
Show answer
Create a root-provided FilterService holding the city in a signal, with methods to update it, and inject the service into both components so they share the same instance. For persistence across reloads or shareable links, sync the value to a query parameter or local storage. If many features need shared state with complex updates, consider NgRx SignalStore or NgRx Store.
15

Your app must call different API base URLs for development, staging and production. How do you configure this in Angular?

Save to ▾
Show answer
Use environment files such as environment.ts and environment.prod.ts with fileReplacements in angular.json build configurations, or load a config.json at startup with provideAppInitializer so one build works in every environment. Expose the URL through an InjectionToken and inject it in services or an interceptor. Never put secrets in these files, because everything in the Angular bundle is public.
16

After deploying the Angular app behind a load balancer, navigating directly to /orders/42 gives a 404. What is wrong?

Save to ▾
Show answer
The Angular router handles that path in the browser, but the web server looks for a real file at /orders/42 and finds none. Configure the server to return index.html for unknown routes: try_files $uri $uri/ /index.html in Nginx, a rewrite rule in IIS or static hosting, or a catch-all route in Express that serves the built index.html after API routes. Alternatively use server-side rendering.
17

Implement a type-ahead search box that calls the API only after the user pauses typing and ignores outdated results. Which RxJS approach would you use?

Save to ▾
Show answer
Take the input stream from a FormControl's valueChanges or toObservable of a signal, then pipe debounceTime(300) to wait for a pause, map to trimmed text, filter out terms shorter than two characters, distinctUntilChanged to skip repeats, and switchMap to call the API, which cancels any in-flight request when a new term arrives. Add catchError inside switchMap so one failed request does not kill the stream.
18

Users double-click the Pay button and two payment requests are sent. How would you prevent it on the frontend?

Save to ▾
Show answer
Disable the button while the request is pending, using an isSubmitting signal. In RxJS, model clicks as a stream and use exhaustMap, which ignores new clicks until the current payment request completes. Show a loading state and handle errors so the button re-enables. The backend must still use idempotency keys, because frontend protection alone cannot stop network retries or duplicate tabs.
19

A dashboard needs data from three independent APIs before it can render. How would you load them?

Save to ▾
Show answer
Use forkJoin to run all three requests in parallel and emit once when all complete, giving an object or array of results. Handle errors per request with catchError returning a fallback value, so one failed widget does not block the whole dashboard. If any source emits repeatedly, use combineLatest instead. With signals, you could use three httpResource instances and a computed for the combined loading state.
20

A component subscribes to a service Observable in ngOnInit, and after navigating back and forth several times, the API is called multiple times per action. What is wrong?

Save to ▾
Show answer
The subscriptions are never cleaned up, so each visit adds another live subscriber that reacts to every emission. Use the async pipe or toSignal in the template, or add takeUntilDestroyed() to manual subscriptions. Also check that the service does not create a new long-lived stream per call and that nested subscribes are replaced with switchMap. Angular DevTools and network logs confirm the fix.
21

A shared service exposes state through a BehaviorSubject, and components mutate the emitted object directly, causing bugs. How do you redesign it?

Save to ▾
Show answer
Keep the writable state private and expose only a read-only view: asObservable() for a subject, or asReadonly() for a signal. Provide methods like addItem and removeItem that create new immutable objects with spread syntax and emit them. Immutable updates also work with OnPush change detection. Consider migrating the service to signals with computed selectors, or NgRx SignalStore for larger state.
22

You need to poll an order status API every five seconds until the order is delivered. How would you implement it?

Save to ▾
Show answer
Use timer(0, 5000) piped into switchMap to call the status API, then takeWhile(status => status !== 'delivered', true) to stop after the final status, including it. Add takeUntilDestroyed so polling stops if the user leaves, and retry with a delay for transient errors. For long waits, server-sent events or websockets are more efficient than polling, which wastes requests.
23

A registration form must check that password and confirm password match. How would you implement this?

Save to ▾
Show answer
Add a cross-field validator at the FormGroup level, because a single control cannot see the other field. The validator reads both values and returns { passwordMismatch: true } when they differ. Show the error under the confirm field when the group has the error and the field is touched. Also validate password strength with a pattern or custom validator, and check it again on the Express backend.
24

An order form lets users add any number of items with product, quantity and price. How do you build it?

Save to ▾
Show answer
Use a FormArray of FormGroups, each with product, quantity and price controls and validators like min(1). Provide addItem and removeItem methods that push or removeAt on the array, and render rows with @for over items.controls. Calculate the total from valueChanges or a computed signal. On submit, send getRawValue() typed as the order DTO, and disable the submit button while invalid or pending.
25

Every API request needs a JWT header, and expired tokens should trigger a refresh without logging the user out. How do you implement this?

Save to ▾
Show answer
Write an auth interceptor that clones each request with the Authorization header from the auth service. In the response path, catch 401 errors, call the refresh endpoint once, store the new access token, and retry the original request. Use a shared refresh Observable with shareReplay or a flag so parallel failed requests wait for one refresh. If refresh fails, clear the session and redirect to login.
26

Users complain that the app shows a blank screen whenever the backend is down. How do you improve this?

Save to ▾
Show answer
Add a global error interceptor that maps network and 5xx errors to a friendly toast or banner, with retry for transient failures. In each data component, handle loading, error and empty states explicitly, using httpResource status signals or catchError with fallback values. Show a retry button, keep cached data visible if available, and log errors to monitoring. A health check banner helps during outages.
27

A form must show fields based on earlier answers, such as company name only when employment type is salaried. How would you build it?

Save to ▾
Show answer
Subscribe to the employment type control's valueChanges, or use a signal, and enable or disable the dependent controls accordingly; disabled controls are excluded from value and validation. Show the fields in the template with @if. For forms driven by configuration, build controls dynamically from a JSON schema into a FormGroup. Remember to clear stale values when a field is hidden.
28

Users upload large documents and want to see upload progress. How do you implement it in Angular?

Save to ▾
Show answer
Send a FormData body with HttpClient post and options reportProgress: true and observe: 'events'. Filter the event stream for HttpEventType.UploadProgress to calculate percent from loaded and total, and for HttpEventType.Response to get the final result. Show a progress bar bound to a signal, allow cancelling by unsubscribing, and validate size and type before upload. For very large files, upload directly to storage with pre-signed URLs.
29

An Express endpoint generates a large PDF report and the whole API slows down while it runs. How do you fix it?

Save to ▾
Show answer
PDF generation is CPU-heavy and blocks the event loop. Move it to a background job: the endpoint creates a job in a queue such as BullMQ with Redis and immediately returns 202 Accepted with a job id. A separate worker generates the PDF, uploads it to storage and updates the job status. Angular polls the status or receives a websocket notification, then downloads the file.
30

Angular calls the Express API on a different port during development and gets CORS errors. What are your options?

Save to ▾
Show answer
In development, the cleanest fix is an Angular dev server proxy: a proxy.conf.json mapping /api to http://localhost:3000, so the browser sees same-origin requests. Alternatively enable the cors middleware in Express with the Angular origin allowed. In production, serve both under one domain through Nginx or configure CORS with an explicit allowlist, enabling credentials only when cookies are used.
31

Your API needs to send welcome emails and SMS after signup, but this makes signup slow. What would you change?

Save to ▾
Show answer
Do not make the user wait for third-party providers. After saving the user, publish an event or add jobs to a queue and return the response immediately. Workers send the email and SMS with retries and backoff, and failures are logged without affecting signup. An EventEmitter is enough for a quick in-process version, but a persistent queue survives restarts and scales across instances.
32

A mobile team and the Angular team both consume your API, and you need to change a response field. How do you avoid breaking clients?

Save to ▾
Show answer
Make additive changes first: add the new field alongside the old one, mark the old field as deprecated in the OpenAPI docs and inform both teams. Track usage, and remove the old field only after all clients have migrated. For larger breaking changes, introduce a new version such as /api/v2 and run both versions in parallel. Contract tests in CI catch accidental breaking changes.
33

How do you design an API endpoint that returns a dashboard summary quickly when the underlying data is large?

Save to ▾
Show answer
Avoid heavy aggregation on every request. Precompute summaries with scheduled jobs or on write events and store them in a summary collection, cache the response in Redis with a short TTL, and return only the fields the dashboard needs. Use indexes for any live queries, add pagination for detail lists, and expose a separate endpoint for drill-down data so the first load stays fast.
34

Your Express API is used by thousands of users and occasionally crashes due to an unhandled promise rejection. How do you make it more resilient?

Save to ▾
Show answer
Find the root cause from logs and fix missing await or catch blocks. Use Express 5, which forwards async errors to error middleware, or an async wrapper in Express 4. Add a central error handler, log unhandledRejection and uncaughtException and then exit cleanly. Run under PM2 or a container orchestrator with multiple instances and health checks so a crash restarts one instance while others keep serving.
35

You are building an e-commerce product catalogue where each category has different attributes, such as size for clothes and RAM for laptops. How would you model it in MongoDB?

Save to ▾
Show answer
Store common fields like name, price, category and stock at the top level, and keep variable specifications in an attributes array of { key, value } pairs, the attribute pattern. Create a compound index on attributes.key and attributes.value so filters work on any attribute. This avoids dozens of sparse fields and lets new categories be added without schema changes, while Mongoose validates the common fields.
36

Users can search products by name, and a regex query has become very slow. What would you do?

Save to ▾
Show answer
A regex without an anchored prefix cannot use a normal index efficiently and scans the collection. For simple word search, create a text index and use $text with a relevance score. For a better search experience with typo tolerance, autocomplete and facets, use Atlas Search or a dedicated search engine. Paginate results, limit fields with projection and debounce requests from the Angular search box.
37

OTP codes must automatically expire ten minutes after creation. How would you implement this with MongoDB?

Save to ▾
Show answer
Store each OTP in its own collection with a createdAt date field and create a TTL index, db.otps.createIndex({ createdAt: 1 }, { expireAfterSeconds: 600 }). MongoDB's background task removes expired documents automatically, though deletion can lag by about a minute, so also check the timestamp during verification. Store a hash of the code rather than plain text, and limit verification attempts.
38

Wallet balance updates sometimes produce wrong totals when two purchases happen at the same time. How do you fix this?

Save to ▾
Show answer
Reading the balance, changing it in code and saving it creates a lost-update race. Use an atomic update instead: updateOne({ _id: userId, balance: { $gte: amount } }, { $inc: { balance: -amount } }) and check modifiedCount to confirm funds were sufficient. If the operation also writes a transaction record, wrap both in a multi-document transaction. Optimistic concurrency with a version field is another option.
39

Your MongoDB collection of sensor readings is growing by millions of documents per day. How would you design for it?

Save to ▾
Show answer
Use a time series collection, which stores readings efficiently in compressed buckets with a timeField and metaField such as deviceId. Set expireAfterSeconds to drop old raw data, and keep hourly or daily aggregates in a separate collection for dashboards. Index on metaField and time, write in batches with insertMany, and plan sharding by device or a hashed key if a single replica set cannot keep up.
40

A list API that uses Mongoose populate on three references has become slow. How would you improve it?

Save to ▾
Show answer
Check how many queries populate triggers and how much data each returns. Select only needed fields in populate, use lean(), and add indexes on the referenced fields. If the list always needs a customer name, store it with the extended reference pattern to avoid the join entirely. For complex filtering on related data, switch to one aggregation with $lookup and $project, and paginate.
41

A penetration test report says your API returns other users' invoices when the invoice id is changed. How do you fix it?

Save to ▾
Show answer
This is broken object-level authorization, the top API risk in the OWASP list. Fix every query to scope by the authenticated user, for example Invoice.findOne({ _id: id, userId: req.user.id }), and return 404 when not found. Centralise ownership checks in services or middleware, add tests that try cross-user access, and review similar endpoints. Avoid relying on unguessable ids as protection.
42

The security team asks you to move JWTs out of localStorage in the Angular app. What design would you propose?

Save to ▾
Show answer
Issue a short-lived access token kept only in memory in an Angular auth service, and a refresh token in an httpOnly, Secure, SameSite cookie scoped to the refresh path. On page load, call the refresh endpoint to get a new access token. An interceptor attaches the access token and refreshes on 401. Rotate refresh tokens and store their hashes server-side so they can be revoked.
43

An npm audit shows several high-severity vulnerabilities in the Express project. How do you handle it?

Save to ▾
Show answer
Read each advisory to see whether the vulnerable code path is actually used in production or only in dev tooling. Run npm audit fix for safe updates, upgrade direct dependencies that pull vulnerable transitive packages, and use overrides in package.json if a fix is not yet released upstream. Test thoroughly after upgrading, enable Dependabot or Renovate for continuous updates and track any accepted risks.
44

Your Angular unit tests take fifteen minutes in CI and developers stop running them. What would you do?

Save to ▾
Show answer
Measure which suites are slow. Migrate from Karma to Vitest for faster runs, avoid importing heavy shared modules in TestBed, mock HTTP and large child components, and replace real timers with fake ones. Run only affected tests on pull requests using Nx affected or similar, parallelise jobs in CI and cache dependencies. Move slow browser flows to a focused E2E suite run separately.
45

After a new release, a bug affects many users. How do you respond and prevent a repeat?

Save to ▾
Show answer
First limit the impact: roll back to the previous version or disable the feature with a flag, and communicate status to support. Then reproduce the bug, fix it with a test that covers the case, and redeploy through the normal pipeline. Afterwards, run a blameless post-mortem to find gaps, such as missing tests, review or monitoring, and add the actions to the backlog.
46

Your MEAN app must handle a festival sale with ten times normal traffic. How do you prepare?

Save to ▾
Show answer
Load test the main flows with k6 or JMeter at expected peak. Serve Angular assets from a CDN, scale API instances horizontally with autoscaling, cache catalogue and home page data in Redis, and scale the MongoDB cluster tier with proper indexes. Move emails and invoices to queues, add rate limiting, set alerts on latency and errors, and freeze risky deployments during the sale.

Frequently asked questions

Why are scenario-based questions important?

They show how you think on real problems. Interviewers use them to separate people who memorised definitions from people who have done the work.

How should I answer them?

Clarify the situation, list likely causes, say how you would check each one, then give the fix and how you would stop it happening again.