Skip to content

Hard MEAN Stack Interview Questions

44 hard questions with answers. Design, trade-off and deep-dive questions asked for senior roles and at top product companies.

Topics in the hard set

RxJS, Signals and State Management: 8 Node.js and Express APIs: 6 MongoDB Data Modelling and Queries: 6 Security, Testing and Deployment: 6 Services, Dependency Injection and Routing: 5 Angular Forms and HTTP Client: 5 TypeScript and Modern JavaScript: 4 Angular Components and Templates: 4
Solved — hardStart 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

What are discriminated unions and why are they useful?

Save to ▾
Show answer
A discriminated union is a union of object types that share a literal property, the tag, such as type Result = { status: 'ok'; data: User } | { status: 'error'; message: string }. Checking result.status narrows the type, so TypeScript knows which fields exist in each branch. Combined with a never check in the default case, the compiler warns you when a new variant is added but not handled.
02

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.
03

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.
04

Write an exhaustive switch over a union type using never.

Save to ▾
Show answer
The never check makes the compiler flag unhandled cases. type Shape = { kind: 'circle'; r: number } | { kind: 'square'; side: number }; function area(s: Shape): number { switch (s.kind) { case 'circle': return Math.PI * s.r ** 2; case 'square': return s.side ** 2; default: { const unreachable: never = s; throw new Error(`Unknown shape ${unreachable}`); } } } Adding a triangle variant now causes a compile error until it is handled.
05

What is zoneless change detection in Angular?

Save to ▾
Show answer
Zoneless mode removes the dependency on Zone.js. Instead of patching browser APIs to guess when something changed, Angular schedules change detection when signals used in templates update, events fire in templates, or markForCheck is called. It is stable in recent Angular versions and the default for new projects. Benefits include a smaller bundle, better performance, clearer stack traces and fewer unnecessary checks.
06

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.
07

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.
08

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.
09

What is hierarchical dependency injection in Angular?

Save to ▾
Show answer
Angular has a tree of injectors that mirrors the application. Environment injectors come from the root and lazy routes, and element injectors come from components and directives. When a dependency is requested, Angular searches upward from the requesting element to the root. Providing a service at component level creates an isolated instance for that subtree. Modifiers like optional, self, skipSelf and host control this lookup.
10

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.
11

Write a role-based canMatch guard that only matches admin users.

Save to ▾
Show answer
Guard factories keep it reusable. import { inject } from '@angular/core'; import { CanMatchFn, Router } from '@angular/router'; export const roleGuard = (role: string): CanMatchFn => () => { const auth = inject(AuthService); const router = inject(Router); return auth.user()?.role === role ? true : router.createUrlTree(['/forbidden']); }; export const routes = [ { path: 'admin', canMatch: [roleGuard('admin')], loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES) } ];
12

Write a resolver that loads a product before the detail page opens.

Save to ▾
Show answer
A functional resolver with error handling: import { inject } from '@angular/core'; import { ResolveFn, Router } from '@angular/router'; import { catchError, EMPTY } from 'rxjs'; export const productResolver: ResolveFn<Product> = (route) => { const router = inject(Router); return inject(ProductService).getById(route.paramMap.get('id')!).pipe( catchError(() => { router.navigate(['/not-found']); return EMPTY; }) ); }; // { path: 'products/:id', resolve: { product: productResolver }, loadComponent: ... } // with input binding: product = input.required<Product>();
13

How do you decide where state should live in a large Angular application?

Save to ▾
Show answer
Situation: our team argued over putting everything in a global store. Task: as tech lead I had to set guidelines. Action: I proposed rules: local UI state in components with signals, feature state in feature-scoped services or SignalStore, truly global data like the user and settings in root services, and server data cached in services. I documented examples and reviewed the first PRs. Result: fewer bugs and simpler components.
14

What is linkedSignal and when would you use it?

Save to ▾
Show answer
linkedSignal creates a writable signal whose value resets based on a source signal, while still allowing local updates. For example, selectedOption can default to the first item of an options list and reset when the list changes, but the user can still pick another option. It solves cases where computed is read-only and effect-based syncing would be awkward or cause extra change cycles.
15

What are the resource and httpResource APIs in Angular?

Save to ▾
Show answer
resource and httpResource bring asynchronous data into the signal world. You give them reactive parameters, such as a signal holding a product id, and they fetch data whenever those parameters change, cancelling stale requests. They expose signals like value, isLoading, error and status, and provide reload. httpResource builds on HttpClient, so interceptors still apply. They reduce manual subscribe-and-set code in components.
16

Explain the difference between switchMap, mergeMap, concatMap and exhaustMap.

Save to ▾
Show answer
All map each source value to an inner Observable but differ in concurrency. switchMap cancels the previous inner Observable, ideal for search. mergeMap runs all inner Observables in parallel, good for independent uploads. concatMap queues them and runs one at a time in order, good for sequential saves. exhaustMap ignores new values while one is active, ideal for preventing duplicate login or submit clicks.
17

What is NgRx and when is it worth using in an Angular application?

Save to ▾
Show answer
NgRx is a state management library family for Angular. NgRx Store follows the Redux pattern with actions, reducers, selectors and effects for side effects, giving predictable state and powerful DevTools. NgRx SignalStore offers a lighter, signal-based way to build stores with state, computed values and methods. It is worth it for large apps with complex shared state and many developers; small apps do fine with services and signals.
18

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.
19

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.
20

Use httpResource to load a product whenever the selected id signal changes.

Save to ▾
Show answer
The request reruns automatically when the id changes. import { Component, input } from '@angular/core'; import { httpResource } from '@angular/common/http'; @Component({ selector: 'app-product-detail', template: ` @if (product.isLoading()) { <p>Loading...</p> } @else if (product.error()) { <p>Could not load product</p> } @else if (product.value(); as p) { <h2>{{ p.name }}</h2> } ` }) export class ProductDetailComponent { id = input.required<string>(); product = httpResource<Product>(() => `/api/products/${this.id()}`); }
21

How would you convince your team to adopt signals in an existing RxJS-heavy codebase?

Save to ▾
Show answer
Situation: our team was hesitant to change working RxJS patterns. Task: I wanted to reduce subscription bugs and prepare for zoneless mode. Action: I built a proof of concept converting one complex component, measured fewer lines and change detection cycles, and proposed a gradual plan: signals for component state, RxJS kept for streams, toSignal at boundaries. Result: the team agreed to use signals in all new features.
22

What is a ControlValueAccessor and why would you implement one?

Save to ▾
Show answer
ControlValueAccessor is the interface that connects a form control to a UI element. Implementing it, with writeValue, registerOnChange, registerOnTouched and setDisabledState, and providing NG_VALUE_ACCESSOR, lets your custom component, such as a star rating, date picker or phone input, work with formControlName and ngModel like a native input, including validation and touched state. It is essential for reusable form component libraries.
23

What are Signal Forms in Angular?

Save to ▾
Show answer
Signal Forms are a newer, experimental forms API introduced in recent Angular versions that builds the form model on signals. You create a form from a signal holding the data model, and field values, validity and errors are all exposed as signals, with schema-based validation rules. They aim to reduce boilerplate and fit zoneless, signal-based apps. Reactive forms remain the stable choice for production projects today.
24

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.
25

Build a dynamic FormArray for adding and removing phone numbers.

Save to ▾
Show answer
Expose the array and helper methods. export class ContactFormComponent { private fb = inject(NonNullableFormBuilder); form = this.fb.group({ name: ['', Validators.required], phones: this.fb.array([this.fb.control('', Validators.pattern(/^\d{10}$/))]) }); get phones() { return this.form.controls.phones; } addPhone() { this.phones.push(this.fb.control('', Validators.pattern(/^\d{10}$/))); } removePhone(i: number) { this.phones.removeAt(i); } } // <div formArrayName='phones'> // @for (c of phones.controls; track $index) { // <input [formControlName]='$index'> <button type='button' (click)='removePhone($index)'>x</button> // } // </div>
26

Write an async validator that checks whether an email is already registered.

Save to ▾
Show answer
Return an Observable and debounce the server call. export function emailTaken(api: UserService): AsyncValidatorFn { return (control: AbstractControl) => timer(400).pipe( switchMap(() => api.emailExists(control.value)), map((exists) => (exists ? { emailTaken: true } : null)), catchError(() => of(null)) ); } email = new FormControl('', { nonNullable: true, validators: [Validators.required, Validators.email], asyncValidators: [emailTaken(inject(UserService))], updateOn: 'blur' }); The control is PENDING while the check runs.
27

What is the difference between a monolith and microservices for a Node.js backend?

Save to ▾
Show answer
A monolith is one deployable API containing all modules, which is simpler to build, test and deploy, and is right for most early-stage products. Microservices split domains like orders, payments and notifications into separate services communicating over HTTP or message queues, allowing independent scaling and deployments, but adding network failures, distributed data and operational complexity. Many teams start with a modular monolith and split later.
28

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.
29

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.
30

Write a Socket.IO server event and an Angular service that listens for it.

Save to ▾
Show answer
Server side: io.on('connection', (socket) => { socket.on('join', (orderId) => socket.join(`order:${orderId}`)); }); // when status changes io.to(`order:${order.id}`).emit('status', { id: order.id, status: order.status }); Angular service: @Injectable({ providedIn: 'root' }) export class OrderSocketService { private socket = io(environment.wsUrl); watch(orderId: string) { this.socket.emit('join', orderId); return new Observable<OrderStatus>((sub) => { this.socket.on('status', (s) => sub.next(s)); return () => this.socket.off('status'); }); } }
31

Write a BullMQ queue and worker to send emails in the background.

Save to ▾
Show answer
Producer in the API, worker in a separate process. import { Queue, Worker } from 'bullmq'; const connection = { host: process.env.REDIS_HOST, port: 6379 }; export const emailQueue = new Queue('emails', { connection }); // in the signup controller await emailQueue.add('welcome', { to: user.email }, { attempts: 3, backoff: { type: 'exponential', delay: 2000 } }); // worker.ts new Worker('emails', async (job) => { await mailer.send(job.data.to, 'Welcome!', templates.welcome()); }, { connection }); Failed jobs retry automatically with backoff.
32

Describe a time you had to optimise a slow backend under pressure.

Save to ▾
Show answer
Situation: during a sales campaign our order API latency jumped to five seconds. Task: fix it while traffic was high. Action: I checked metrics, found a report query running on every order, moved it to a cached summary updated every minute, and added a missing index. I communicated progress to the support team every thirty minutes. Result: latency returned below 300 ms within two hours and the campaign continued smoothly.
33

What schema design patterns are common in MongoDB?

Save to ▾
Show answer
Common patterns include embedding for one-to-few relations, referencing for one-to-many and many-to-many, the subset pattern that embeds only the most recent items, the extended reference pattern that copies frequently needed fields like a customer name into orders, the computed pattern that stores precalculated totals, the bucket pattern that groups time-series readings, and the outlier pattern for rare documents with huge arrays.
34

What are read concern and write concern in MongoDB?

Save to ▾
Show answer
Write concern controls how many replica set members must acknowledge a write before it is considered successful; w: 'majority' protects against losing data on failover, while w: 1 is faster but riskier. Read concern controls the consistency of data read, such as local for the latest on one node or majority for data acknowledged by most members. Choose stronger settings for payments and orders.
35

What are change streams in MongoDB and how can a MEAN app use them?

Save to ▾
Show answer
Change streams let an application subscribe to real-time inserts, updates and deletes on a collection, database or deployment, using the oplog of a replica set. A Node.js service can watch the orders collection and push status changes to Angular clients over Socket.IO, or sync data to search indexes and caches. Streams are resumable using a resume token after a restart.
36

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.
37

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.
38

Use a MongoDB transaction in Mongoose to place an order and reduce stock together.

Save to ▾
Show answer
withTransaction retries on transient errors. const session = await mongoose.startSession(); try { await session.withTransaction(async () => { const res = await Product.updateOne( { _id: productId, stock: { $gte: qty } }, { $inc: { stock: -qty } }, { session } ); if (res.modifiedCount === 0) throw new Error('Out of stock'); await Order.create([{ userId, productId, qty }], { session }); }); } finally { await session.endSession(); } Transactions require a replica set, which Atlas provides by default.
39

What is CSRF and how do Angular and Express handle it?

Save to ▾
Show answer
Cross-site request forgery abuses cookies the browser sends automatically to make unwanted requests. Angular's HttpClient supports the double-submit cookie pattern: it reads a token from a cookie named XSRF-TOKEN and sends it in the X-XSRF-TOKEN header on mutating requests, which the server verifies. Combined with SameSite cookies and not using GET for changes, this protects cookie-based auth. Header-based JWTs are not vulnerable in the same way.
40

What is Angular SSR and why would you enable it?

Save to ▾
Show answer
Angular server-side rendering renders pages to HTML on a Node.js server, so users and search engines see content immediately. The browser then hydrates the page, attaching Angular to the existing DOM; incremental hydration can hydrate parts only when needed using @defer triggers. Route-level render modes allow some pages to be prerendered at build time and others rendered per request. It improves SEO and first load for public pages.
41

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.
42

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.
43

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.
44

Write a GitHub Actions job that tests and builds the Angular app.

Save to ▾
Show answer
Typical CI for the frontend: name: web-ci on: [pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npx ng lint - run: npx ng test --watch=false - run: npx ng build --configuration production - uses: actions/upload-artifact@v4 with: name: web-dist path: dist/ The artifact can be deployed by a later job after approval.

Frequently asked questions

Who should practise hard MEAN Stack questions?

Candidates targeting senior roles or top product companies, and anyone who finds the medium set easy.

How should I practise these?

Answer each question aloud before opening the answer, mark it as solved only when your answer matches the key idea, and save misses to Last-Minute Revision.

Other difficulty levels