Building an Open Graph Image Fetcher with Angular

I’m always interested in understanding how SEO works. This time, I wanted to learn how the Open Graph (OG) image is fetched from a source when we paste a URL into tools like Slack or social media platforms like Facebook or Twitter. By default, these platforms bring some image as a thumbnail or preview. I learned that this image is called an OG image.
In this blog, we built a small solution to simulate fetching an OG image. We also explored an alternative way to fetch the image using a different approach. Let’s dive into the details!

Building an Open Graph Image Fetcher with Angular

In this blog post, we’ll walk through building a simple Open Graph Image Fetcher using Angular. This application will allow users to enter a URL, fetch the Open Graph image associated with that URL, and display it.

Step 1: Setting Up the Angular Project

First, create a new Angular project if you haven’t already:

ng new open-graph-image-fetcher
cd open-graph-image-fetcher

Step 2: Install Dependencies

We’ll need the HttpClientModule for making HTTP requests. So add it accordingly

Step 3: Create the Service

Create a service to handle fetching the Open Graph image. We’ll use the opengraph.io API for this purpose. Note that you need to get an API key from opengraph.io and add it to your environment configuration.

open-graph.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class OpenGraphService {
  private apiKey = environment.openGraphApiKey; // Use API key from environment

  constructor(private http: HttpClient) {}

  fetchOGImage(url: string): Observable<string> {
    const apiUrl = `https://opengraph.io/api/1.1/site/${encodeURIComponent(url)}?app_id=${this.apiKey}`;
    return this.http.get(apiUrl).pipe(
      map((response: any) => {
        console.log('response', response);
        const ogImage = response.hybridGraph.image;
        return ogImage || '';
      })
    );
  }
}

Step 4: Create the Component

Create a component to handle user input and display the fetched Open Graph image.

app.component.ts

import { Component } from '@angular/core';
import { OpenGraphService } from './open-graph.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  ogImage: string | undefined;
  targetURL = 'https://www.example.com';
  loading = false;

  constructor(private openGraphService: OpenGraphService) {}

  fetchOGImage() {
    this.loading = true;
    this.openGraphService.fetchOGImage(this.targetURL).subscribe(
      (image: string) => {
        this.ogImage = image;
        this.loading = false;
      },
      (error) => {
        console.error('Error fetching Open Graph image', error);
        this.loading = false;
      }
    );
  }
}

app.component.html

<div class="container">
  <h1>Open Graph Image Fetcher</h1>
  <div class="content">
    <input type="text" placeholder="Enter URL" [(ngModel)]="targetURL" (keyup.enter)="fetchOGImage()" class="url-input" />
    <button (click)="fetchOGImage()">Fetch Open Graph Image</button>
    <div *ngIf="loading" class="loader"></div>
    <div *ngIf="ogImage && !loading" class="image-container">
      <img [src]="ogImage" alt="Open Graph Image">
    </div>
  </div>
</div>

app.component.scss

.container {
  text-align: center;
  margin-top: 50px;
}

.content {
  display: inline-block;
  text-align: left;
}

.url-input {
  margin-right: 10px; /* Add space between the textbox and the button */
}

.loader {
  border: 16px solid #f3f3f3; /* Light grey */
  border-top: 16px solid #3498db; /* Blue */
  border-radius: 50%;
  width: 120px;
  height: 120px;
  animation: spin 2s linear infinite;
  margin: 20px auto;
}

.image-container {
  height: 300px; /* Fixed height to prevent jumping */
  display: flex;
  justify-content: center;
  align-items: center;
}

.image-container img {
  max-height: 100%;
  max-width: 100%;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

Alternative Approach: Using Cheerio

Another way to fetch Open Graph data is by using Cheerio, a server-side library that parses HTML and extracts data. However, this approach has limitations, such as proxy issues and the requirement of server-side blocks.

Conclusion

In this blog post, we built a simple Open Graph Image Fetcher using Angular and the opengraph.io API. We also explored an alternative approach using Cheerio on a Node.js server. While the Cheerio approach can be useful, it has limitations such as proxy issues and the requirement of server-side blocks.

Find the source here at GitHub

https://github.com/PandiyanCool/open-graph-image

Feel free to expand on this project by adding more features or improving the UI.

Happy coding!

Mocking in Next.js with Jest: How to create mocks for API responses and dependencies

Mocking is an essential part of unit testing in Next.js with Jest. It allows us to create a fake version of a dependency or API response and test our code in isolation. In this blog post, we will explore how to create mocks for API responses and dependencies in Next.js with Jest.

What is mocking?

Mocking is the process of creating a fake version of a dependency or API response that our code depends on. By creating a mock, we can test our code in isolation without relying on external dependencies. This allows us to control the behavior of the mocked dependency or API response and test various scenarios.

Why use mocking?

There are several benefits to using mocking in our tests:

  • Isolation: By mocking dependencies and API responses, we can test our code in isolation without relying on external factors.
  • Control: We can control the behavior of the mocked dependency or API response and test various scenarios.
  • Speed: Mocking can make our tests run faster by reducing the need for external calls.

Creating mocks for API responses

When testing Next.js applications that rely on external APIs, we can create mocks for API responses using Jest’s jest.mock() function. This function allows us to replace the original module with a mock module that returns the data we want.

Here’s an example of how to create a mock for an API response in a Next.js application:

// api.js
import axios from 'axios';

export async function getUsers() {
  const response = await axios.get('/api/users');
  return response.data;
}

// __mocks__/axios.js
const mockAxios = jest.genMockFromModule('axios');

mockAxios.get = jest.fn(() => Promise.resolve({ data: [{ id: 1, name: 'John' }] }));

export default mockAxios;

In this example, we have created a mock for the **axios**module that returns a fake response with a single user. The mock is defined in the **__mocks__**directory, which is automatically recognized by Jest.

To use this mock in our test, we can simply call **jest.mock('axios')**at the beginning of our test file:

// api.test.js
import { getUsers } from './api';
import axios from 'axios';

jest.mock('axios');

describe('getUsers', () => {
  it('returns a list of users', async () => {
    axios.get.mockResolvedValue({ data: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }] });

    const result = await getUsers();

    expect(result).toEqual([{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]);
  });
});

In this test, we have mocked the axios.get() method to return a list of two users. We then call the getUsers() function and assert that it returns the correct data.

Creating mocks for dependencies

In addition to mocking API responses, we can also create mocks for dependencies that our code depends on. This can be useful when testing functions that rely on complex or external dependencies.

Here’s an example of how to create a mock for a dependency in a Next.js application:

// utils.js
import moment from 'moment';

export function formatDate(date) {
  return moment(date).format('MMMM Do YYYY, h:mm:ss a');
}

// __mocks__/moment.js
const moment = jest.fn((timestamp) => ({
  format: () => `Mocked date: ${timestamp}`,
}));

export default moment;

In this example, we have created a mock for the moment module that returns a formatted string with the timestamp value. The mock is defined in the __mocks__ directory, which is automatically recognized by Jest.

To use this mock in our test, we can simply call jest.mock('moment') at the beginning of our test file:

// utils.test.js
import { formatDate } from './utils';
import moment from 'moment';

jest.mock('moment');

describe('formatDate', () => {
  it('returns a formatted date string', () => {
    const timestamp = 1617018563137;
    const expected = 'Mocked date: 1617018563137';

    const result = formatDate(timestamp);

    expect(moment).toHaveBeenCalledWith(timestamp);
    expect(result).toEqual(expected);
  });
});

In this test, we have mocked the moment() function to return a formatted string with the timestamp value. We then call the formatDate() function and assert that it returns the correct string.

Conclusion

Mocking is an essential part of unit testing in Next.js with Jest. It allows us to create a fake version of a dependency or API response and test our code in isolation. In this blog post, we explored how to create mocks for API responses and dependencies in Next.js with Jest. We saw how to use jest.mock() to create mocks for external APIs and how to create mocks for dependencies. By using mocking in our tests, we can test our code in isolation, control the behavior of dependencies and API responses, and make our tests run faster.