Skip to content

Senior MEAN Stack Engineer Interview Questions & Answers

40 questions for Senior MEAN Stack Engineer interviews — 17 medium, 23 hard.

About the Senior MEAN Stack Engineer role

A Senior MEAN Stack Engineer designs scalable architecture for Angular and Node.js applications. You plan lazy-loaded feature structures, zoneless and signal-based migrations, SSR for public pages, API versioning, caching and MongoDB scaling. You set coding and security standards, lead performance work, handle critical production issues and mentor the team, while giving realistic estimates and clear trade-offs to managers and clients.

Frontend architecture Performance tuning Scalable APIs Mentoring Technical estimation
Solved for this roleStart 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

Write an aggregation that finds the top five customers by total spend with their names.

Save to ▾
Show answer
Group, sort, limit, then join. db.orders.aggregate([ { $match: { status: 'paid' } }, { $group: { _id: '$customerId', spend: { $sum: '$total' }, orders: { $sum: 1 } } }, { $sort: { spend: -1 } }, { $limit: 5 }, { $lookup: { from: 'users', localField: '_id', foreignField: '_id', as: 'customer' } }, { $unwind: '$customer' }, { $project: { _id: 0, name: '$customer.name', spend: 1, orders: 1 } } ]) Joining after $limit means only five lookups run.
02

Define a typed Mongoose model in TypeScript with an instance method.

Save to ▾
Show answer
Interface, methods type and schema together. import { Schema, model, Model } from 'mongoose'; import bcrypt from 'bcrypt'; interface IUser { name: string; email: string; password: string; role: 'user' | 'admin' } interface IUserMethods { comparePassword(plain: string): Promise<boolean> } type UserModel = Model<IUser, {}, IUserMethods>; const userSchema = new Schema<IUser, UserModel, IUserMethods>({ name: { type: String, required: true }, email: { type: String, required: true, unique: true, lowercase: true }, password: { type: String, required: true, select: false }, role: { type: String, enum: ['user', 'admin'], default: 'user' } }, { timestamps: true }); userSchema.method('comparePassword', function (plain: string) { return bcrypt.compare(plain, this.password); }); export const User = model<IUser, UserModel>('User', userSchema);
03

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

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

What are @defer blocks and how do they help performance?

Save to ▾
Show answer
@defer lets you lazy load part of a template and its dependencies only when a trigger occurs, such as on viewport, on interaction, on hover, on idle, on timer or a custom condition with when. Angular automatically splits that code into a separate chunk. You can show @placeholder, @loading and @error blocks. It is ideal for heavy below-the-fold widgets like charts, comments and maps.
06

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

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

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

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

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

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

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

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

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

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

What types of indexes does MongoDB support?

Save to ▾
Show answer
MongoDB supports single-field and compound indexes, multikey indexes for array fields, text indexes for keyword search, geospatial 2dsphere indexes for location queries, hashed indexes for sharding, wildcard indexes for unpredictable fields, TTL indexes that delete documents after a time, and unique, partial and sparse options. Atlas also provides Atlas Search for full-text relevance and Vector Search for AI similarity queries.
17

How do toSignal and toObservable bridge RxJS and signals?

Save to ▾
Show answer
toSignal(observable$) subscribes to an Observable and exposes its latest value as a signal, unsubscribing automatically when the injection context is destroyed; you can supply an initialValue or requireSync. toObservable(signal) creates an Observable that emits whenever the signal changes, letting you apply RxJS operators like debounceTime and switchMap. Together they let you keep simple state in signals while using RxJS for async logic.
18

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

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'); }); } }
20

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

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

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()}`); }
23

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

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

How does $lookup work in an aggregation pipeline?

Save to ▾
Show answer
$lookup performs a left outer join with another collection in the same database. The basic form matches localField to foreignField and writes matching documents into an array field. The pipeline form lets you add conditions, filtering and projections on the joined collection using let variables. Often you follow it with $unwind to flatten the array. Index the foreign field so the join stays efficient.
26

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

Describe a time you helped a teammate who was struggling with RxJS.

Save to ▾
Show answer
Situation: a new teammate kept writing nested subscribes and could not follow our streams. Task: help him become productive quickly. Action: I drew marble diagrams for map, switchMap and combineLatest, paired with him to refactor one of his components, and shared a short list of the operators we use most with examples. Result: within two weeks his pull requests used flattening operators correctly and he started reviewing others.
28

Write an Nginx server block that serves Angular and proxies /api to Node.

Save to ▾
Show answer
One domain, no CORS needed. server { listen 80; server_name shop.example.com; root /usr/share/nginx/html; location /api/ { proxy_pass http://api:3000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } location ~* \.(js|css|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } } Angular hashed file names make long caching safe.
29

Create a text index and run a relevance-sorted search in MongoDB.

Save to ▾
Show answer
Text search with scores: db.articles.createIndex({ title: 'text', body: 'text' }, { weights: { title: 5, body: 1 } }) db.articles.find( { $text: { $search: 'angular signals' } }, { score: { $meta: 'textScore' }, title: 1 } ).sort({ score: { $meta: 'textScore' } }).limit(10) Weights make title matches rank higher. A collection can have only one text index, so include all searchable fields in it.
30

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

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

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

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

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

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

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

What are Angular's lifecycle hooks and when is each typically used?

Save to ▾
Show answer
ngOnChanges runs when input values change. ngOnInit runs once after the first inputs are set, used for initial data loading. ngDoCheck allows custom change detection. ngAfterContentInit and ngAfterViewInit run after projected content and the view are ready, used for DOM access. ngOnDestroy runs before removal, used for cleanup. With signals, effect, computed and DestroyRef often replace several of these hooks.
39

How do containers and orchestration fit into deploying a MEAN stack?

Save to ▾
Show answer
Docker packages the Express API and an Nginx image serving the Angular build into portable images with all dependencies. docker compose runs them with MongoDB locally. In production, services like AWS ECS, Azure Container Apps or Kubernetes run multiple replicas, perform rolling updates, restart failed containers and scale on load. Images are built and pushed to a registry by the CI pipeline, tagged by commit.
40

How do you use Mongoose with TypeScript?

Save to ▾
Show answer
Define a TypeScript interface for the document, then create a Schema typed with that interface and a model with model<IUser>('User', userSchema). Mongoose infers types for queries, so findById returns a typed document. For lean queries, results are plain typed objects. Schema options like timestamps add createdAt and updatedAt, and you can type instance methods, statics and virtuals through additional generic parameters.

Frequently asked questions

What does a Senior MEAN Stack Engineer do?

A Senior MEAN Stack Engineer designs scalable architecture for Angular and Node.js applications. You plan lazy-loaded feature structures, zoneless and signal-based migrations, SSR for public pages, API versioning, caching and MongoDB scaling. You set coding and security standards, lead performance work, handle critical production issues and mentor the team, while giving realistic estimates and clear trade-offs to managers and clients.

Which skills are tested in a Senior MEAN Stack Engineer interview?

Expect questions on Frontend architecture, Performance tuning, Scalable APIs, Mentoring, Technical estimation. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Senior MEAN Stack Engineer interview?

Revise Angular Components and Templates, RxJS, Signals and State Management, MongoDB Data Modelling and Queries, Security, Testing and Deployment first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.