Skip to main content
Yosavo Studio by Youssef Ibaichou , home
Expertise

Node.js

I use Node.js for APIs, SaaS backends, integrations, automation and serverless workloads, especially when asynchronous processing and cloud services are central to the application.

API architecture · Async patterns · Serverless · Observability

How I think about it

How I think about Node.js

Node.js is a strong fit for I/O-heavy applications and integration-heavy systems, but good architecture still requires clear boundaries, careful async handling, validation, observability and protection against long-running or blocking work.

  • Never block the event loop — move CPU-heavy work elsewhere.
  • Validate at the edge; trust nothing that comes from outside.
  • Every external call has a timeout, and every retry is safe to repeat.
  • If it isn't logged with context, it didn't happen in production.

Key experience

Where this comes from

  • Nexus Inclusion, the accessibility SaaS platform where I lead delivery, runs a Node.js backend behind a React frontend, integrating Stripe and AWS Bedrock.
  • APIs, integrations and internal business systems built with Node.js and Express.js in my full-stack work.
  • AWS experience including Lambda, which shapes how I design serverless Node.js workloads.

Scope

What I know

  • Layered API structure
  • Express middleware
  • Async/await patterns
  • Schema validation
  • Auth & tenant-aware access
  • Integrations & webhooks
  • Queues & workers
  • AWS Lambda
  • Relational & document databases
  • Structured logging
  • Security hygiene
  • Event loop & performance

Deep dive

Node.js in practice

On this page (12 topics)

API architecture

I keep three layers distinct: routes and controllers that translate HTTP into calls and back, services that hold the business workflow, and data access that knows about the database. Express doesn't enforce any of this, which is why so many Node codebases end up with SQL inside route handlers.

Errors flow through one handling path that maps known error types to status codes and one response shape. Unknown errors become a generic 500 with the details in the logs, not in the response.

Related: API Development expertise

Async programming

async/await makes asynchronous code read sequentially, which hides an easy performance mistake: awaiting independent calls one after another. Independent work runs in parallel with Promise.all — or Promise.allSettled when one failure shouldn't discard the other results; dependent work stays sequential.

The failure modes to design out are unhandled rejections and forgotten awaits. Every promise is awaited or deliberately handled, and loops over large datasets process in batches rather than firing thousands of concurrent requests at a database or provider.

Sequential versus parallel independent calls
// Sequential by accident — total time is the sum of all three calls
async function loadDashboardSlow(id) {
  const user = await getUser(id);
  const plan = await getPlan(id);
  const usage = await getUsage(id);
  return { user, plan, usage };
}

// Independent calls in parallel — total time is roughly the slowest call
async function loadDashboard(id) {
  const [user, plan, usage] = await Promise.all([
    getUser(id),
    getPlan(id),
    getUsage(id),
  ]);
  return { user, plan, usage };
}

Express.js

Express is deliberately minimal, so structure is your responsibility. Middleware handles cross-cutting concerns — authentication, request IDs, rate limiting, body parsing — in a predictable order; routers group endpoints by resource; controllers stay thin and call services; and one error middleware at the end turns thrown errors into consistent responses.

Async route handlers must forward rejected promises to that error middleware. Otherwise a failed request simply hangs until the client times out.

Validation

Every body, query string and route parameter is validated against a schema at the edge, before it reaches business logic, and the validated result is what the rest of the code uses. Validation errors return field-level details in the same format everywhere.

Sanitization depends on where data goes: escaping for HTML, parameterized queries for SQL, argument arrays for shell commands — not one generic “clean” function applied on the way in.

Authentication & authorization

Session cookies suit first-party web apps on the same domain; short-lived tokens suit APIs used by other clients and services. Either way, authentication only answers “who is this?”. Authorization is a separate, explicit step that checks role and permission and, in multi-tenant systems, that the requested resource belongs to the caller's tenant.

Related: Multi-Tenant SaaS expertise

Integrations

Third-party APIs fail in every possible way: slow responses, rate limits, partial outages, changed payloads. Each integration gets a client with timeouts, retries with backoff for safe operations, and mapping from the provider's data model to ours, so their quirks don't leak into business logic.

Webhook handlers should assume events can be delayed, retried or delivered more than once. They verify the signature, acknowledge quickly and process asynchronously — idempotently wherever duplicate execution could cause side effects.

Background processing

Work that's slow, external or retryable doesn't belong in the request cycle. The API validates the request, enqueues a job and returns; a worker picks it up, calls the external service and records the result. Workers run as separate processes, so a backlog can't slow user-facing requests.

Jobs are idempotent, retries use backoff, messages that keep failing land somewhere visible after a retry limit, and payloads carry identifiers and tenant context rather than large objects.

  1. Request

    validated at the edge

  2. API

    enqueues, returns quickly

  3. Async job

    separate worker, retries

  4. External service

    timeout, backoff

  5. Result

    stored, user notified

Asynchronous processing: the API accepts the request and enqueues a job, a separate worker calls the external service, and the result is stored for the user.

Serverless Node.js

AWS Lambda suits Node.js well for event-driven and spiky workloads, but it changes the rules. Functions are short-lived and stateless: anything that must persist goes to a database, cache or storage, and in-memory state may or may not survive between invocations.

Cold starts grow with bundle size and initialization work, so dependencies stay lean and expensive clients are created once, outside the handler. Timeouts are set deliberately below upstream timeouts, concurrency limits protect downstream systems, and retries from asynchronous event sources mean handlers must be idempotent.

A Lambda function that opens a new database connection on every invocation can exhaust a relational database under load. Connection reuse or a connection proxy matters more than the function code.

Related: AWS expertise

Databases

The data model chooses the database, not the language. Relational databases such as PostgreSQL and MySQL fit transactional business data with relationships and reporting needs. MongoDB fits documents read and written as a unit with a flexible structure. DynamoDB fits well-known access patterns at scale.

Whatever the store, queries are parameterized, connection pools are sized for the deployment model — a pool per Lambda instance behaves very differently from a pool per server — and migrations are versioned with the code.

Logging & observability

Production debugging depends on what was logged before the incident. I log structured JSON with a correlation ID that follows a request through the API, queues and workers, plus tenant and user identifiers where appropriate — never secrets, and never full payloads containing personal data. Errors are logged once, with context, at the boundary that handles them.

Security

Secrets come from the environment or a secret manager, never the repository. Inputs are validated; output to HTML, SQL and shell is encoded for its context. Dependencies are a real attack surface in the Node ecosystem, so the dependency list stays short, lockfiles are committed and audit warnings are reviewed rather than ignored.

Login, password reset and expensive endpoints get rate limits, and outbound calls go to allow-listed hosts rather than URLs taken from user input.

Performance

Node handles thousands of concurrent I/O operations on one thread because it never waits on them — and it stops handling anything while CPU-bound code runs. Image processing, large data transformations, encryption loops and report generation belong in worker threads, separate services or background jobs.

Large files and exports are streamed rather than loaded into memory, and memory that grows over time is treated as a bug to investigate, not something a restart hides.

In practice

Common problems I work with

  • Route handlers containing business logic and SQL
  • Independent calls awaited one after another
  • Unhandled promise rejections and requests that hang
  • CPU-heavy work blocking the event loop
  • Webhooks processed twice
  • Lambda functions exhausting database connections

Real products

How it connects to real products

Node.js has been part of SaaS platforms and integration-heavy systems I've led or built.

  • Accessibility Technology / SaaS

    Nexus Inclusion

    Accessibility SaaS with a Node.js backend behind a React frontend, integrating Stripe and AWS Bedrock — where I lead delivery and technical decisions.

  • Full-stack development

    APIs & internal business systems

    Web applications, integrations and internal business systems built with Node.js and Express.js.

See all selected work

Related expertise

AWS

Workload-first cloud architecture: serverless, storage, data, security and cost.

  • Serverless
  • S3 & DynamoDB
  • IAM
  • Cost awareness

API Development

Predictable API contracts: validation, errors, authorization, idempotency and webhooks.

  • API design
  • Error handling
  • Idempotency
  • Webhooks

SaaS Development

Tenancy, permissions, plans, billing and the realities of running SaaS in production.

  • Multi-tenancy
  • Permissions
  • Billing
  • Production delivery

AI Integration

Applied LLM engineering: structured output, validation, cost and security.

  • LLM integration
  • Structured output
  • Validation
  • Cost control

Related services

  • Custom Software Development

    Build reliable web platforms, SaaS products, dashboards, APIs, and internal tools with modern full-stack technologies.

    Learn more
  • AWS Cloud Engineering

    Deploy, optimize, and maintain reliable cloud systems using AWS services and practical cloud architecture.

    Learn more
  • SaaS Product Development

    Design and build SaaS products from MVP to production, including architecture, user management, subscriptions, integrations and cloud infrastructure.

    Learn more
Let's talk

Have a technical challenge?

Whether it's an architecture decision, a delivery problem or something specific to Node.js, I can help you find a practical path forward.

What happens next

  1. Share your goal

    A few lines about the product, problem, or audit need.

  2. Get a quick read

    A practical reply on the best technical path forward.

  3. Move forward

    A clear scope, estimate, or concrete next step.