Category: Development

  • Block Editor Best Practices: WordPress Meetup Saarland

    Block Editor Best Practices: WordPress Meetup Saarland

    WordPress Meetups are always one of the best ways to meet like-minded people, teach people about WordPress, have amazing discussions, and bring more people to the wonderful community. I participated in the 3rd WordPress Meetup in Saarland on 5th September, this time as a speaker. I talked about probably the most controversial feature of WordPress, the Block Editor (also known as Gutenberg). The topic was mainly about Block Editor Best Practices – for users, designers, and developers.

    Recently, we revamped rtCamp‘s website. It was a mammoth task – custom blocks, patterns, templates, and what not. During the process, we discovered some pain points with the block editor and also figured out some best practices. This talk focused on the outcomes of the project.

    During the talk, I realized how much context-switching I needed to do. One of the members in the audience was an artist and had just installed WordPress. They wanted to know the powers of Gutenberg. On the other hand, one of the members of the audience, Fredric Döll has founded Digitenser Consulting, wanted to learn more about how to efficiently create for and with the block editor for their clients.

    Gutenberg is a very powerful tool but it is often misunderstood. It is also important to understand that for some sites, Gutenberg may not make sense. But for the sites where editorial experience is key, it is imperative that the website is planned really well. A robust plan helps with feasible designs which lead to a better overall developer experience.

    The next WordPress Meetup in Saarland will happen on 23.01.2025. If you’re around Saarbrücken at that time, feel free to drop your emails in the comment.

    Note: In the presentation, we discussed negative margins. Gutenberg does have support for negative margins; however, our discussion was more oriented towards user experience. Currrently, negative margins in Gutenberg, have a little UX situation.

    Block Editor Best Practices – Deck

    You can access the presentation slides (Google Slides) this link.

  • How to Cache POST Requests in Nginx

    How to Cache POST Requests in Nginx

    Caching can substantially reduce load times and bandwidth usage, thereby enhancing the overall user experience. It allows the application to store the results of expensive database queries or API calls, enabling instant serving of cached data instead of re-computing or fetching it from the source each time. In this tutorial, we will explore why and how to cache POST requests in Nginx.

    There are only two hard things in Computer Science: cache invalidation and naming things.

    — Phil Karlton

    Caching POST requests: potential hazards

    By default, POST requests cannot be cached. Their (usually) non-idempotent (or “non-safe”) nature can lead to undesired and unexpected consequences when cached. Sensitive data, like passwords, which these requests may contain, risk exposure to other users and potential threats when cached. Additionally, POST requests often carry large payloads, such as file uploads, which can significantly consume memory or storage resources when stored. These potential hazards are the reasons why caching POST requests is not generally advised.

    Source: https://restfulapi.net/idempotent-rest-apis/

    Although it may not be a good idea to cache POST requests, RFC 2616 allows POST methods to be cached provided the response includes appropriate Cache-Control or Expires header fields.

    The question: why would you want to cache a POST request?

    The decision to cache a POST request typically depends on the impact of the POST request on the server. If the POST request can trigger side effects on the server beyond just resource creation, it should not be cached. However, a POST request can also be idempotent/safe in nature. In such instances, caching is considered safe.

    Why and how to cache POST requests

    Recently, while working on a project, I found myself designing a simple fallback mechanism to ensure responses to requests even when the backend was offline. The request itself had no side effects, though the returned data might change infrequently. Thus, using caching made sense.

    I did not want to use Redis for two reasons:

    1. I wanted to keep the approach simple, without involving ‘too many’ moving parts.
    2. Redis does not automatically serve stale cache data when the cache expires or is evicted (invalidate-on-expire).

    As we were using Nginx, I decided to go ahead with this approach (see figure).

    The frontend makes a POST request to the server, which has an Nginx set up as a reverse proxy. While the services are up and running, Nginx caches them for a certain time and in a case where the services are down, Nginx will serve the cache (even if it is stale) from its store.

    http {
        ...
        # Define cache zone
        proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my-cache:20m max_size=1g inactive=3h use_temp_path=off;
        ...
    }
    
    location /cache/me {
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_pass http://service:3000;
    
        # Use cache zone defined in http
        proxy_cache my-cache;
        proxy_cache_lock on;
        
        # Cache for 3h if the status code is 200/201/302
        proxy_cache_valid 200 201 302 3h;
        
        # Serve staled cached responses
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_methods POST;
    
        # ! This is important
        proxy_cache_key "$request_uri|$request_body";
    
        proxy_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
    
        # Add header to the response
        add_header X-Cached $upstream_cache_status;
    }

    Things to consider

    In proxy_cache_key "$request_uri|$request_body", we are using the request URI as well as the body as an identifier for the cached response. This was important in my case as the request (payload) and response contained sensitive information. We needed to ensure that the response is cached on per-user basis. This, however, comes with a few implications:

    1. Saving the request body may cause a downgrade in performance (if the request body is large).
    2. Increased memory/storage usage.
    3. Even if the request body is slightly different, it will cause Nginx to cache a new response. This may cause redundancy and data mismatch.

    Conclusion

    Caching POST requests in Nginx may offer a viable solution for enhancing application performance. Despite the inherent risks associated with caching such requests, careful implementation can make this approach both safe and effective. This tutorial discusses how we can implement POST request caching wisely.

    Want to know how we can monitor server logs like a pro, using Grafana Loki?

    Suggested Readings

    1. Idempotent and Safe APIs
    2. Nginx Proxy Module
    3. Caching POST Requests with Varnish
  • New Relic with WordPress using Event API for better monitoring

    New Relic with WordPress using Event API for better monitoring

    New Relic is a leading application performance monitoring (APM) platform that offers developers invaluable insights into the performance of their applications. APM tools provide us with real-time monitoring, diagnostics, and analytics capabilities that enable us to gain deep visibility into our applications, track down performance issues, and make informed decisions to improve the overall user experience. If you wish to add monitoring to your server, here is how you can use Grafana Loki to monitor your server logs. WordPress is the most popular CMS in the world. Integrating New Relic with WordPress can help developers optimize their code and identify bottlenecks. It also helps ensure that the WordPress applications perform well under different loads and usage scenarios.

    Recently, we shipped a WordPress solution that relies heavily on third-party API. To obfuscate the keys and other confidential data, we used WP REST API as a relay which acts like a proxy (sort of). On the client side, we hit the WP REST API endpoints, which call the third-party API. We use Transients to cache the response on the server side. We also use a caching layer on the client side (using Axios and LocalStorage). The transients (server-side cache) mitigate redundant requests from different users to the third-party API, whereas the client-side cache reduces redundant requests from the same user to the site’s backend.

    In this post, we will learn how to integrate New Relic with WordPress using the Event API.

    Overview

    We could not install and configure New Relic’s PHP Agent to instrument external API calls (because the hosting platform did not allow that). Therefore, we decided to use the Event API. It is a powerful tool that allows us to send custom event data to New Relic Insights, their real-time analytics platform. Using Event API, we can capture and analyze specific events or metrics that are important to our application’s performance and operations.

    Flowchart of how Event API is triggered.

    Event API

    Using the Event API, we can programmatically send structured JSON data to New Relic Insights. The data can then be visualized and queried to gain deeper insights into our application’s behavior. This can include information such as user interactions, system events, errors, custom metrics, or any other relevant data points.

    To use Event API, we need to follow these steps:

    1. Obtain your New Relic Ingest - License API key.
    2. Obtain your Account ID.

    We have to use the following endpoint to POST to the Event API: https://insights-collector.newrelic.com/v1/accounts/{{ ACCOUNT_ID }}/events.

    The API Key needs to be set in the Headers. The JSON payload looks like this:

    {
      "eventType": "myEvent",
      "timestamp": 1652455543000,
      "applicationId": "myApp",
      "data": {
        "key1": "value1",
        "key2": "value2"
      }
    }

    The eventType is what we will use to query the data.

    $access_id = EXTERNAL_API_KEY;
    $response = wp_safe_remote_get( $endpoint );
    
    if ( is_wp_error( $response ) || 200 !== $response['response']['code'] ) {
    			$incident = send_external_api_incident( $endpoint, (string) $response['response']['code'], $response );
    			return new WP_Error(
    				(int) $response['response']['code'],
    				array(
    					'actual_response'    => $response,
    					'new_relic_incident' => $incident,
    				) 
    			);
    		}

    The send_external_api_incident() logic:

    /**
     * Sends API Incident event to New Relic.
     *
     * @param  string          $endpoint
     * @param  string          $response_code
     * @param  array|\WP_Error $response
     * @return array|\WP_Error
     */
    function send_external_api_incident( string $endpoint, string $response_code, array|\WP_Error $response ) {
    	$base_url = 'https://insights-collector.newrelic.com/v1/accounts/' . NEW_RELIC_ACCOUNT_ID . '/events';
    	$body     = [
    		array(
    			'eventType'    => 'ExternalApiIncident',
    			'endpoint'     => $endpoint,
    			'responseCode' => $response_code,
    			'response'     => wp_json_encode( $response ),
    		),
    	];
    	$response = wp_safe_remote_post(
    		$base_url,
    		array(
    			'headers' => array(
    				'Content-Type' => 'application/json',
    				'Api-Key'      => NEW_RELIC_INGEST_KEY,
    			),
    			'body'    => wp_json_encode( $body ),
    		)
    	);
    
    	return $response;
    }

    Checking the results

    Head over to the your New Relic account and click on your application. You can use NRQL queries to query data.

    SELECT * FROM `ExternalApiIncident` since 1 day ago


    In conclusion, integrating New Relic with WordPress application offers a robust solution for monitoring and optimizing application performance. This approach not only enhances the visibility of your application’s internal workings but also ensures a seamless user experience by efficiently tracking and analyzing critical data points. By following the outlined steps and best practices, you can successfully implement this powerful tool, even in environments with certain restrictions. The ability to customize event tracking and gain insights through real-time analytics is invaluable for developers aiming to maintain high-performance standards. Remember, continuous monitoring and improvement are key to staying ahead in the fast-paced digital world. Utilize these insights to keep your application running smoothly, and always be proactive in seeking ways to refine and enhance your system’s performance.

  • Protected Routes in Next.js

    Protected Routes in Next.js

    If you are building a SaaS website that has awesome features or a simple website with minimal user functionality, you know Authentication and Authorization are crucial (difference between authentication and authorization). Protected Routes in Next.js help us ensure that unauthenticated users are not able to see routes/pages intended for logged in (authenticated) users. There are a few approaches to to implement Protected Routes in Next.js, i.e., enforce authentication for a page/route.

    But, first of all – why do we love Next.js? Next.js is arguably the most popular and go-to React framework. It packs some cool stuff including file-based routing, incremental static regeneration, and internationalization (i18n). With Next.js 13, we have got even more power – layouts and Turbopack!

    You might be wondering – why bother protecting routes? We are building a SaaS product with a Next.js frontend and Nest.js backend. We have implemented authentication in the backend but we also need to ensure that forced browsing* is prevented and User Experience is enriched. Actual authentication logic should reside inside our back-end logic. All the API calls must be appropriately authenticated. In our app, whenever there is an unauthenticated request it returns 401 Unauthorized. An ACL is also in place so whenever user requests a resource they do not have access to, the backend returns 403 Forbidden.

    Now, let’s create a route protection flow in Next.js.:
    If a user requests a protected route (something that requires authentication), we redirect them to the login page.
    We should not prevent access if a route is public (supposed to be viewed regardless of the users’ authentication state) like a login page.

    At the end, the goals are simple: safety and security.

    Jodi Rell

    Using RouteGuard

    The concept of a RouteGuard is simple. It is a wrapper component that checks whether the user has access to the requested page on every route change. To track the access, we use one states: authorized. If authorized is true, then the user may see the page or else user is redirected to the login page. To update the state, we have a function authCheck() which prevents access (sets authorized to false) if the user does not have access and the page is not public (e.g. landing page, login page, sign-up page).

    Logic of RouteGuard to implement Protected Routes in Next.js.
    Working of RouteGuard
    import { Flex, Spinner } from '@chakra-ui/react';
    import { useRouter } from 'next/router';
    import publicPaths from '../data/publicPaths';
    import { useAppDispatch, useAppSelector } from '../hooks/storeHooks';
    import { setRedirectLink } from '../redux/AuthSlice';
    import {
      JSXElementConstructor,
      ReactElement,
      useEffect,
      useState,
    } from 'react';
    
    const RouteGuard = (props: {
      children: ReactElement<unknown, string | JSXElementConstructor<unknown>>;
    }) => {
      const { children } = props;
    
      const router = useRouter();
      const [authorized, setAuthorized] = useState(false);
      const user = useAppSelector((state) => state.auth);
    
      const dispatch = useAppDispatch();
    
      useEffect(() => {
        const authCheck = () => {
          if (
            !user.isLoggedIn &&
            !publicPaths.includes(router.asPath.split('?')[0])
          ) {
            setAuthorized(false);
            dispatch(setRedirectLink({ goto: router.asPath }));
            void router.push({
              pathname: '/login',
            });
          } else {
            setAuthorized(true);
          }
        };
    
        authCheck();
    
        const preventAccess = () => setAuthorized(false);
    
        router.events.on('routeChangeStart', preventAccess);
        router.events.on('routeChangeComplete', authCheck);
    
        return () => {
          router.events.off('routeChangeStart', preventAccess);
          router.events.off('routeChangeComplete', authCheck);
        };
      }, [dispatch, router, router.events, user]);
    
      return authorized ? (
        children
      ) : (
        <Flex h="100vh" w="100vw" justifyContent="center" alignItems="center">
          <Spinner size="xl" />
        </Flex>
      );
    };
    
    export default RouteGuard;

    Note: we are using Redux to store the user’s data; authentication is out of the scope of this blog post.

    Implementing the Middleware

    In a scenario where the users’ session expires while they are on a protected page, they will not be able to fetch newer resources (or perform any actions for that matter). That’s, once again, really bad UX. We cannot expect a user to refresh, so we need a way to let them know that their session is no longer valid.

    To implement the same, we will use another awesome Next.js feature – Middlewares! In few words, a middleware sits between your server and the frontend. Middleware allows you to run code before a request is completed, then based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.

    After session expiration, whenever the user makes a request, it will result in 401 Unauthorized. We have implemented a middleware which listens to the response for each request that is being made from the frontend; if the request results in 401 Unauthorized, we dispatch the same action, i.e. log out the user and redirect to the login page.

    Working of the unauthenticatedInterceptor middleware to implement Protected Routes in Next.js.
    Working of the middleware
    import {
      MiddlewareAPI,
      isRejectedWithValue,
      Middleware,
    } from '@reduxjs/toolkit';
    import { logout } from '../redux/AuthSlice';
    import { store } from '../redux/store';
    
    interface ActionType {
      type: string;
      payload: { status: number };
      meta: {};
      error: {};
    }
    
    const unauthenticatedInterceptor: Middleware =
      (_api: MiddlewareAPI) =>
      (next: (action: ActionType) => unknown) =>
      (action: ActionType) => {
        if (isRejectedWithValue(action)) {
          if (action.payload.status === 401 || action.payload.status === 403) {
            console.error('MIDDLEWARE: Unauthorized/Unauthenticated [Invalid token]');
            store.dispatch(logout());
          }
        }
    
        return next(action);
      };
    
    export default unauthenticatedInterceptor;

    Suggested Readings

  • Getting Started with DatoCMS – Creating a simple blog

    Getting Started with DatoCMS – Creating a simple blog

    DatoCMS is a relative newcomer in the CMS industry. There are plenty of Content Management Systems (CMS). A CMS is a powerful piece of software that helps users to create, manage, and modify content on a website without requiring special technical expertise and without the need to code. WordPress is the most popular CMS with over 43% of the market share. Some other examples of CMS are Drupal, Joomla.

    DatoCMS rolled out for public use in 2019, is a cloud-based headless CMS for mobile apps, static websites, and server-side applications. The concept of headless CMS is relatively newer. It was born out of the modern need to serve content across multiple channels, like web apps, mobile apps, IoT devices, and wearable software. A headless CMS attempts to reduce the modules attached to the software since there is no presentation layer attached; the developer has the flexibility of serving the content across a wide range of channels.

    Why DatoCMS?

    DatoCMS is API-first. Every software powered by DatoCMS makes use of two APIs to work with the content – Content Delivery API, and Content Management API. To perform operations on the content programmatically, we make use of the Content Management API. To retrieve the content for displaying purposes, we make use of the Content Delivery API.

    DatoCMS generates a static website, that results in a faster and more secure website. It fits in very well with frameworks, technologies and generators like React, Next.js, Jekyll, Nuxt, Vue, PHP, Middleman, and Ruby on Rails. It is flexible, offers granular permissions, and comes with a GraphQL API.

    DatoCMS does not build and deploy your website, but delegates it to an external CI/CD services. DatoCMS offers integration with the following services out of the box:

    To read more about the general concepts of DatoCMS, please refer the official docs.

    Creating a DatoCMS Account & Starting a New Project

    Well, signing up for any service should be a piece of cake, really! Just head to https://dashboard.datocms.com/signup and you know the drill!

    DatoCMS – Sign Up Page

    Once you sign up, you will be guided to the dashboard. DatoCMS is free for upto 3 projects and that should suit our use-case. DatoCMS Dashboard is easy to navigate, considering that the target audience is non-technical clients. We will start by hitting the ‘New Project’ button.

    DatoCMS Dashboard
    DatoCMS Dashboard

    Click on ‘Demo project’ and you will be lead to the page of starter projects. We will be using Next.js Blog starter project.

    DatoCMS Starter Projects
    DatoCMS – Starter Projects

    Now, click on ‘Start free project’. Name your project and choosing a hosting solution. We will use Vercel to host your app.

    DatoCMS - Starting a Demo Project
    DatoCMS – Starting a Demo Project

    Configuring Vercel for DatoCMS Blog

    Clicking on ‘Click project’ should open a new tab for Vercel configuration. First, we need to create a GitHub repository to enable CI/CD. I will create a private repo but it is absolutely all right to keep your repo public.

    Vercel - Repo Config
    Vercel – Repo Config

    Now, we must integrate DatoCMS with the Vercel app (for obvious reasons).

    Vercel – Add Integration

    The next and the final step is to deploy our Git repo to Vercel. It should happen automatically and you should see a nice-looking message on your DatoCMS Dashboard.

    DatoCMS - Success Message
    DatoCMS – Success Message

    Editing the DatoCMS Blog

    We have successfully deployed our blog. Now, we will modify global settings like Blog Description, SEO, and Favicon from the Dato Editor. Just click on your project in the dashboard and hit ‘Enter Project’. Once you are inside the editor, you will see a lot of settings in the left sidebar. Those are all the customizable options and essentially the content we can edit without having to tweak the codebase. To modify the favicon and SEO settings, we will click on ‘Settings’ option.

    DatoCMS - Global Settings
    DatoCMS – Global Settings

    Now, let’s configure the homepage settings like Blog Title using the ‘Homepage’ option from the left sidebar.

    Exploring DatoCMS Blog Models

    DatoCMS handles editable data as a scheme called ‘Models’, which you can think of as Tables. Each model has some set of user-editable fields, like Post Title, Post Content, Featured Image. Models are modular and provide a user-friendly editing experience.

    To see our models, we will click on ‘Settings’ in the top bar. Once we are there, we will click on ‘Models’ in the left sidebar. Our starter project has four pre-defined models – Author, Blog, Category, and Post. Each model can have a collection of records/instances, like we can have multiple posts on a single website.

    DatoCMS - Models
    DatoCMS – Models

    Our Post model has a number of fields – it has a Title, Author (which is linked to another model ‘Author’), and a structured text field to store the post Content. Then, it has some fieldsets, which are models inside models (sort of). Those handle our previews and post metadata.

    To know more about DatoCMS Models, you can check out DatoCMS’ documentation.

    Adding Content

    Now that we have an overview of what a model is, we will add a few posts. To add content (posts) to our blog, we will click on ‘Content’ in the top menu. Inside the left sidebar, we will have all our models. Before we do that, let’s add an author so that we can link it with our posts.

    DatoCMS - Adding an Author
    DatoCMS – Adding an Author

    Let’s create a post. I will quickly copy my own post about how you can setup Grafana and Loki to monitor your server logs ?. DatoCMS Editor supports rich-text, and we can add things like lists, images, headings, and quotes by pressing the '/' key (just like WordPress). We will add stuff like quotes, and code blocks (DatoCMS Editor has some awesome codeblock editing experience).

    DatoCMS Editor - Adding a New Post
    DatoCMS Editor – Adding a New Post

    Similarly, we can add the other two fieldsets – Preview and Metadata. Now, we are ready to publish!

    Building the Blog

    Once you are done with all the editing, you need to deploy the blog by ‘building’. In the top-right corner, click on ‘Build Status’ and hit ‘Build Now’. This will generate a static site and deploy it on Vercel. The logs can be checked in Settings > Activity Logs.

    DatoCMS - Building the Site
    DatoCMS – Building the Site

    Bravo! Our website is live now ?. Click on ‘Visit Site’ in the ‘Build Status’ dropdown to see your site. You can check mine here.

    DatoCMS Blog
    DatoCMS Blog

    What’s Next?

    In the next blog posts, I plan to go through how we can test our DatoCMS app/website locally, edit the code, and make it more customizable. Also, maybe we can check out how it compares to WordPress ?!?

    I would like to thank the contributors of the DatoCMS Blog Starter Project. Do check out the repo.