Home› Web Platform› Complete Guide
Complete Guide

Complete Web Platform Tutorial

This complete guide covers all 9 Web Platform tutorials on TheCodeForge, organised by topic.

Learning Roadmap
Beginner → Build a strong foundation
Intermediate → Deepen your understanding with practical topics
Advanced → Master advanced concepts and real-world applications
9
Topics
5
Beginner
4
Intermediate
0
Advanced
Jump to section
WordPress (5)GraphQL (4)

WordPress and GraphQL sit at opposite ends of web engineering and share one property that shapes every error in this track: both hide the database behind a layer that makes expensive things look cheap. In WordPress it is a plugin architecture where any of forty plugins can hook a query onto every page load. In GraphQL it is a resolver graph where a client asking for one extra nested field can multiply your query count by the size of a list.

So the debugging skill is the same on both sides: make the invisible work visible. A white screen is not a mystery once errors are being logged. An N+1 explosion is not a mystery once you count queries per request. Most of the fixes below are about installing that visibility first and only then changing code.

The white screen is not an error — it is a hidden error

A blank page means PHP hit a fatal error while output buffering was on and display of errors was off. The error exists; you just cannot see it. So the first move is never to start deactivating plugins by guesswork — it is to turn logging on and read the actual message, which usually names the file and line directly.

Once you can see it, the shape of the problem is almost always one of three: exhausted memory, a fatal in a plugin or theme after an update, or a PHP version incompatibility where a plugin uses syntax the installed PHP rejects.

SymptomRead this firstUsual cause
Blank white pagewp-content/debug.log, then the PHP-FPM error logFatal error in a plugin or theme, or exhausted memory
Error establishing a database connectionCredentials in wp-config.php, then whether MySQL is accepting connections at allWrong host or socket, MySQL down, or connection limit reached on shared hosting
Too many redirectssiteurl and home in wp_optionsSite URL and the actual scheme or domain disagree, often after adding TLS behind a proxy
REST API 401 while logged inWhether the nonce is being sent, and the Authorization header survives the serverMissing X-WP-Nonce, or Apache stripping Authorization before PHP sees it
php
// wp-config.php - log the fatal instead of hiding it.
// Logs to wp-content/debug.log; keep display off on a live site.
define( 'WP_DEBUG',         true );
define( 'WP_DEBUG_LOG',     true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

// Raise the ceiling only after confirming memory is the cause
define( 'WP_MEMORY_LIMIT',     '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );

Too many redirects is a disagreement about your own address

WordPress canonicalises requests: if the incoming URL does not match the stored siteurl, it redirects to the stored one. Put it behind a TLS-terminating proxy and the loop is immediate — siteurl says https, the proxy forwards plain HTTP, PHP sees a non-TLS request and redirects to HTTPS, the proxy forwards it again, forever.

The fix is to tell PHP what the proxy already knows, and to do it before WordPress boots.

php
// wp-config.php - trust the proxy's forwarded scheme, above the
// require of wp-settings.php so it applies before WordPress canonicalises.
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
     && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
    $_SERVER['HTTPS'] = 'on';
}

// If you are locked out of wp-admin entirely, override both and get in,
// then set them properly in Settings and remove these lines.
define( 'WP_HOME',    'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );
In practiceOnly trust X-Forwarded-Proto when the request genuinely cannot reach PHP except through your proxy. If the origin is directly reachable, a client can set that header itself, and you have handed it control over how your application understands its own scheme.

GraphQL's N+1 problem, and why DataLoader is the standard answer

A GraphQL resolver is called once per field per object. Ask for 50 posts and each post's author and the author resolver runs 50 times, each issuing its own query. The query is fast; there are simply 51 of them. Nothing in the schema hints at this, which is why N+1 is the defining performance problem of GraphQL APIs.

DataLoader fixes it by batching within a tick of the event loop: individual load(id) calls are collected, dispatched as one query, and the results distributed back to the callers. The critical detail is lifetime — a loader must be created per request, because its cache is a per-request cache. Sharing one across requests serves stale data across users.

javascript
import DataLoader from 'dataloader';

// One loader per request. Never module scope - that is a cross-user cache.
function createLoaders(db) {
  return {
    authorById: new DataLoader(async (ids) => {
      const rows = await db.users.whereIn('id', ids);      // ONE query
      const byId = new Map(rows.map(r => [r.id, r]));
      return ids.map(id => byId.get(id) ?? null);          // order must match
    }),
  };
}

// server setup
context: ({ req }) => ({ loaders: createLoaders(db), user: req.user }),

// resolver - looks identical to the naive version, batches underneath
Post: {
  author: (post, _args, ctx) => ctx.loaders.authorById.load(post.authorId),
}

DataLoader must return results in the same order as the requested keys, with a null or error placeholder for missing ones. Returning the database's natural order is the most common implementation bug, and it silently attaches the wrong author to the wrong post.

Non-nullable fields and query depth: two schema decisions with teeth

Cannot return null for non-nullable field is the schema enforcing a promise you made. Because GraphQL nullability propagates upward, a null in a non-null field nulls its parent, and if the parent is also non-null the error climbs until it finds something nullable — which is why one missing row can blank an entire response. The lesson is to be honest in the schema: mark a field non-null only when your resolver can always produce it.

Query depth is the mirror image. A schema where a post has an author who has posts is a cycle, and a client can nest it as deep as it likes, so a small query can demand an enormous amount of work. Depth limits, complexity scoring and a persisted-query allow-list are the three standard defences, and public APIs generally need at least two of them.

ControlStopsCost
Depth limitDeeply nested cyclic queriesSimple to add; blunt, and a wide shallow query still gets through
Complexity scoringExpensive queries regardless of shape, by weighting fields and list sizesNeeds per-field weights and tuning, but is the control that matches actual cost
Persisted queriesAnything the client did not register in advanceStrongest control; only workable when you own every client
Pagination limitsUnbounded list arguments multiplying everything below themCheap, and should be a default on every list field

Frequently Asked Questions

How do I find which plugin caused a white screen without taking the site down?
Enable WP_DEBUG_LOG first and read debug.log — the fatal error names the file, which names the plugin, with no deactivation needed. If the log is empty because the failure happens before logging initialises, check the PHP-FPM error log. Bisecting plugins should be the last resort, not the first move.
Why does the WordPress REST API return 401 for a user who is clearly logged in?
Cookie authentication for the REST API also requires a nonce. Without X-WP-Nonce, WordPress treats the request as unauthenticated even with a valid session cookie. The other frequent cause is the server stripping the Authorization header before PHP sees it, which needs a rewrite rule to pass it through.
Does DataLoader replace caching?
No — it is request-scoped batching that happens to deduplicate within one request. It removes N+1 query patterns; it does nothing for repeated requests from different users. You still want a real cache layer above it, and keeping the two separate is what lets you reason about staleness.
Should every GraphQL field be nullable?
Nullable by default is the safer stance, with non-null reserved for fields a resolver can genuinely always produce — an ID, a created timestamp. Because nullability propagates upward, over-using non-null means a single failing leaf can null out a large part of the response. Being honest about what can be absent costs a little client-side handling and buys much better partial-failure behaviour.
Is GraphQL still worth adopting over REST?
It earns its complexity when many different clients need different shapes of the same data, or when round trips are expensive — mobile especially. It costs you caching simplicity, rate limiting that HTTP gave you for free, and the N+1 and complexity problems in this track. For a single first-party client with predictable needs, REST plus a couple of purpose-built endpoints is usually less total work.
What is the single most valuable habit for WordPress performance?
Counting queries per page load. Install a query monitor, look at the count and the slowest queries, and you will usually find one plugin responsible for a large share of both. Almost every WordPress optimisation discussion becomes concrete the moment that number is on screen.

WordPress

GraphQL

Also Explore
JavaScript 185 tutorials → PHP 56 tutorials → Database 139 tutorials → System Design 145 tutorials → Frontend 13 tutorials → Security 16 tutorials →
Start from the beginning

Every tutorial starts with a plain-English analogy — then real code, then interview questions.

Browse Web Platform Tutorials →