Backend Development with Node.js, Express, REST APIs & GraphQL
I build the server-side architecture that powers web applications, mobile apps, and SaaS platforms. Node.js and Express are my primary tools, and I design APIs using both REST and GraphQL depending on what the project actually needs. Every backend I ship is built for production. That means proper error handling, authentication, input validation, rate limiting, and architecture that can handle real traffic without falling over at 2 AM.
Node.js & Express: The Engine Room
Node.js is not just a trend. It is the runtime that powers backends for companies ranging from startups to Netflix and PayPal. I chose it years ago because it lets me write JavaScript across the entire stack, which means faster development, easier debugging, and a single language from the browser to the database. Express sits on top of Node as a minimal, unopinionated framework that gives me full control over routing, middleware, and request handling without the overhead of a bloated framework.
My Express applications follow a clean modular architecture. Routes, controllers, services, and data access layers are separated so that each piece can be tested independently and swapped out when requirements change. Middleware handles cross-cutting concerns like authentication, logging, CORS, and request validation before any business logic runs. This is not something you bolt on later. It is the foundation.
I use Node.js for everything from simple REST APIs serving a mobile app to complex event-driven systems that process webhooks, queue background jobs, and coordinate between multiple third-party services. The non-blocking I/O model makes it exceptionally good at handling concurrent connections, which matters when your application needs to serve hundreds or thousands of simultaneous users without spinning up expensive infrastructure.
If you have a project that needs a fast, reliable API or a backend that integrates with multiple services, Node.js and Express are almost always where I start. The ecosystem is mature, the tooling is excellent, and the deployment options are flexible enough to fit any budget.
API Architecture: REST vs GraphQL
Every backend I build is API-first. That means the server exposes clean, well-documented endpoints that any client can consume, whether that client is a React frontend, a mobile app, a third-party integration, or an internal tool. The question is always which API style fits the project.
REST is my default for most projects. It is simple, well-understood, and works perfectly when your data model maps cleanly to resources. A REST API for a project management tool might have endpoints like /api/projects, /api/projects/:id/tasks, and /api/users/:id. Each endpoint returns exactly what it says. The HTTP methods (GET, POST, PUT, DELETE) map directly to operations. Caching is straightforward. Any developer who touches your codebase will understand the API immediately.
GraphQL shines when the frontend has complex data requirements that would turn a REST API into a mess of custom endpoints and query parameters. Instead of hitting five different endpoints to assemble a dashboard view, the client sends a single query describing exactly what data it needs and the server returns precisely that. No over-fetching. No under-fetching. One round trip.
I implement GraphQL using Apollo Server on the backend, with properly defined schemas, resolvers, and data loaders to prevent the N+1 query problem that trips up most GraphQL implementations. I also set up query complexity analysis and depth limiting to prevent malicious or poorly written queries from hammering your database.
The honest answer is that most small to mid-size projects do not need GraphQL. REST handles them well and is easier to maintain. But when you have a data-rich application with multiple client types that each need different slices of the same data, GraphQL can dramatically simplify both your frontend and backend code.
When to Use REST vs GraphQL
| Factor | REST | GraphQL |
|---|---|---|
| Data complexity | Best for simple, resource-based data models with predictable access patterns | Best for deeply nested or interconnected data that varies by client view |
| Client types | Works well when one or two clients consume the API in similar ways | Ideal when mobile, web, and third-party clients each need different data shapes |
| Caching | HTTP caching works out of the box with ETags and Cache-Control headers | Requires application-level caching strategies like persisted queries |
| Learning curve | Low. Any developer can read and consume a REST API immediately | Moderate. Schema design and resolver patterns require upfront investment |
| Over-fetching | Common. Endpoints return fixed response shapes even when the client only needs a subset | Eliminated. Clients request exactly the fields they need |
| File uploads | Native multipart form support | Requires workarounds or separate REST endpoints for file handling |
| Real-time | Requires separate WebSocket implementation | Built-in subscription support for real-time data |
Authentication & Security
Authentication is one of those things that seems simple until you get it wrong. I implement JWT-based authentication for stateless API access, session-based auth for traditional web applications, and OAuth 2.0 flows for third-party integrations. The choice depends on your application architecture and security requirements.
For most API-driven applications, I use short-lived access tokens paired with secure HTTP-only refresh tokens. The access token handles authentication for individual requests without hitting the database on every call. The refresh token rotates on each use and is stored where JavaScript cannot access it, which prevents the most common token theft attacks.
Beyond authentication, every API I build includes input validation and sanitization on every endpoint, rate limiting to prevent abuse and brute force attacks, CORS configuration that only allows the domains that should be talking to your API, parameterized database queries to prevent SQL injection, and proper error responses that do not leak internal system details to potential attackers.
I also implement role-based access control when the application needs it. Not every user should be able to hit every endpoint. Permissions are enforced at the middleware level so that authorization logic stays consistent and centralized rather than scattered across individual route handlers.
Real-Time Features
Some applications need data to update in real time without the user refreshing the page. Live dashboards, chat systems, notification feeds, collaborative editing, and order tracking all require a persistent connection between the client and the server.
I build real-time features using WebSockets through Socket.io for bidirectional communication and Server-Sent Events for simpler one-way data streams. Socket.io handles the messy reality of real-time connections, including automatic reconnection, fallback transports for restrictive networks, and room-based broadcasting so you can push updates to specific groups of users without flooding everyone.
For applications that need real-time data combined with GraphQL, I implement GraphQL Subscriptions. These let the client subscribe to specific data changes and receive updates pushed from the server the moment something changes. A project management app might subscribe to task status changes so the board updates instantly when a team member moves a card.
Real-time features add complexity to your backend architecture, so I only recommend them when the user experience genuinely benefits. A live chat system needs WebSockets. A contact form does not. I will be honest about what your application actually requires.
Scaling for Production Traffic
Building a backend that works in development is easy. Building one that holds up under production traffic with real users, unpredictable load spikes, and zero tolerance for downtime is a different discipline entirely.
I design backends with horizontal scaling in mind from the start. That means stateless application servers that can be replicated behind a load balancer without session affinity issues. It means caching frequently accessed data in Redis so your database is not getting hammered with the same queries on every request. It means offloading slow operations like email sending, image processing, and report generation to background job queues so API responses stay fast.
Database performance is usually the first bottleneck. I design schemas with proper indexing, write efficient queries, and set up connection pooling from day one. For read-heavy applications, I implement caching layers that keep your database load manageable as traffic grows. When the data model calls for it, I use both SQL databases like PostgreSQL for relational data and NoSQL options like MongoDB for document-oriented workloads.
Monitoring and logging are part of every production deployment. Structured logging with correlation IDs lets me trace a request from the frontend through every service it touches. Health check endpoints, error tracking, and performance metrics give you visibility into how your backend is actually performing, not just whether it is running.
I have built backends that serve thousands of daily active users and handle traffic spikes without breaking a sweat. The key is not overengineering from the start but architecting in a way that gives you a clear, affordable path from your current scale to your next milestone.
Need a Backend That Scales?
Book a free discovery call to discuss your API architecture and backend needs.
Book a Call