Skip to content

Instantly share code, notes, and snippets.

@edoves
Created November 6, 2024 01:20
Show Gist options
  • Save edoves/c0decaf733c5f9ec747fc528125530ea to your computer and use it in GitHub Desktop.
Save edoves/c0decaf733c5f9ec747fc528125530ea to your computer and use it in GitHub Desktop.
Two API layer Explanation

API layer 1

Explanation

When you work with third-party services or external APIs (like weather services, payment gateways, or any other external data source), they often provide you with an API key or some form of authentication. This key is sensitive information—like a password—that allows access to the service.

If you’re fetching data directly from the client side (for instance, a React or Vue application running in the user's browser), you would need to expose this API key to make calls to the third-party service. However, if this key is visible in the client code, anyone who views your client-side code could potentially see and misuse it.

To protect this sensitive information, it’s best practice to create a server-side API layer. This means:

  1. You create a separate endpoint or API on your server that handles the requests.
  2. Your client-side code calls your server API instead of the third-party service directly.
  3. The server then uses the API key (securely stored) to fetch data from the third-party service and send the necessary data back to the client.

By doing this, you keep your API key hidden and secure on the server.

Examples

Example 1: Using an API layer to fetch weather data

Let’s say you’re building a weather app, and you want to fetch weather data from a third-party service like OpenWeatherMap, which requires an API key.

  • Without API Layer (Insecure)

    // In your client-side code (JavaScript running in the browser)
    fetch(`https://api.openweathermap.org/data/2.5/weather?q=London&appid=YOUR_API_KEY`)
        .then(response => response.json())
        .then(data => console.log(data));

    Here, YOUR_API_KEY would be visible to anyone who inspects your website’s JavaScript code.

  • With API Layer (Secure)

    On your server (Node.js/Express example):

    // server.js (or an API route file)
    const express = require('express');
    const fetch = require('node-fetch');
    const app = express();
    
    app.get('/api/weather', async (req, res) => {
        try {
            const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=London&appid=${process.env.OPENWEATHERMAP_API_KEY}`);
            const data = await response.json();
            res.json(data);
        } catch (error) {
            res.status(500).json({ error: 'Failed to fetch weather data' });
        }
    });
    
    app.listen(3000, () => console.log('Server running on port 3000'));

    Then, in your client code:

    // Now the client only talks to your server’s API without knowing the actual API key
    fetch('/api/weather')
        .then(response => response.json())
        .then(data => console.log(data));

In this setup, the client fetches data from '/api/weather' on your server, which then internally calls OpenWeatherMap using the secure API key stored in your environment variables (process.env.OPENWEATHERMAP_API_KEY), keeping it safe from the client.

Example 2: Fetching data from a database with a server-side API

Imagine you have a database with sensitive information, and you want to avoid exposing direct database queries to your client-side app.

  • Without API Layer (Insecure)

    Directly embedding sensitive query logic in your client-side code, such as:

    fetch(`https://your-database-url?query=SELECT * FROM users`)
        .then(response => response.json())
        .then(data => console.log(data));

    This would be a huge security risk because someone could easily inspect and modify the query.

  • With API Layer (Secure)

    On your server (e.g., Node.js/Express with MongoDB or MySQL):

    app.get('/api/users', async (req, res) => {
        try {
            const users = await db.collection('users').find({}).toArray();
            res.json(users);
        } catch (error) {
            res.status(500).json({ error: 'Failed to fetch users' });
        }
    });

    And on the client side, you call:

    fetch('/api/users')
        .then(response => response.json())
        .then(data => console.log(data));

Here, the client only interacts with '/api/users', and the sensitive database logic remains on the server.

Key Points

  • API keys and database queries should never be exposed to the client since they are sensitive and can be misused if accessed by malicious actors.
  • An API layer on the server acts as a secure middleman, keeping your secrets safe and ensuring only the necessary data is shared with the client.

API layer 2

The API (Application Programming Interface) layer acts as a bridge between your application and various resources, such as databases or third-party services. This layer essentially hides the direct interaction with databases and third-party APIs, offering a controlled and secure way to access data and functionalities without exposing sensitive information to the client (e.g., the user's browser or app). Here’s a closer look at some cases where an API layer is helpful, along with examples to illustrate each:

1. Using Third-Party Services

When your application relies on external services, such as payment processors, weather services, or social media platforms, these services usually provide their own APIs. The API layer in your application would be responsible for managing requests to and from these external services, acting as an intermediary.

Example:

Imagine you're building an e-commerce app, and you need to process payments through a service like Stripe. You wouldn't want to embed your Stripe keys directly into the front end, as that would expose them to anyone who views the client-side code.

API Layer Code: Your server-side code would include a route to interact with the Stripe API securely.

// Express.js example of an API endpoint to process payments

app.post('/api/process-payment', async (req, res) => {
    const { amount, currency } = req.body;

    try {
        const paymentIntent = await stripe.paymentIntents.create({
            amount,
            currency,
        });
        res.status(200).send({ paymentIntentId: paymentIntent.id });
    } catch (error) {
        res.status(500).send({ error: 'Payment processing failed' });
    }
});

In this example:

  • The /api/process-payment endpoint is your API layer.
  • The client requests this endpoint without ever accessing the Stripe API directly.
  • This ensures secure handling of sensitive data (like payment credentials) and keeps them hidden from the client.

2. Fetching Data Securely from the Database

For applications that need to access a database, the API layer acts as a buffer to protect database credentials and secure sensitive data. If you expose database access directly in client-side code, anyone could potentially access it and misuse it.

Example:

Let’s say you’re building a dashboard that shows user statistics from your database. You could create an API endpoint that fetches this data securely.

API Layer Code:

// Node.js/Express example to fetch user stats

app.get('/api/user-stats', async (req, res) => {
    try {
        const userStats = await db.collection('stats').findOne({ userId: req.user.id });
        res.status(200).send(userStats);
    } catch (error) {
        res.status(500).send({ error: 'Failed to fetch user stats' });
    }
});

Here:

  • The /api/user-stats endpoint fetches the user's statistics from a database.
  • The client calls this API to get the data, but it doesn’t have direct access to the database.
  • This protects your database credentials and prevents malicious access.

3. Data Aggregation and Processing

In some cases, an API layer aggregates or processes data from multiple sources before sending it to the client. This can improve performance, reduce unnecessary data load, and provide tailored responses.

Example:

If you’re building a weather app that pulls data from several weather APIs, your API layer could combine this data, calculate an average forecast, and send it as a single response to the client.

API Layer Code:

// Combining data from multiple weather APIs

app.get('/api/weather', async (req, res) => {
    try {
        const [weatherData1, weatherData2] = await Promise.all([
            fetch('https://api.weatherapi1.com/forecast').then(res => res.json()),
            fetch('https://api.weatherapi2.com/forecast').then(res => res.json())
        ]);

        const averageTemperature = (weatherData1.temp + weatherData2.temp) / 2;

        res.status(200).send({ temperature: averageTemperature });
    } catch (error) {
        res.status(500).send({ error: 'Failed to fetch weather data' });
    }
});

In this example:

  • The API layer combines data from two sources, calculates an average, and sends the result to the client.
  • This keeps the client-side code simple, as it doesn’t need to manage multiple API calls and complex data processing.

Key Benefits of Using an API Layer:

  1. Security: Keeps sensitive data (like API keys and database credentials) hidden from the client.
  2. Control: Provides a controlled and consistent way to interact with data.
  3. Scalability: Allows you to change backend services or databases without affecting the client-side code.
  4. Data Processing: Enables server-side data processing, reducing the load on the client and improving performance.

The API layer essentially enhances the security, maintainability, and efficiency of your application by creating a structured and secure bridge between the front end and data sources.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment