Skip to content

Use YARP to forward a Twitter API call from within a static Blazor WASM client. #1675

Answered by MihaZupan
JeepNL asked this question in Q&A
Use YARP to forward a Twitter API call from within a static Blazor WASM client. #1675
Apr 28, 2022 · 1 answers · 6 replies

What I want to do:

Making a call to the Twitter API directly from within a static Blazor WASM front-end client (the browser), so without calling a back-end API which makes a call to the Twitter API.

The problem

The Twitter API (v1/v2) doesn't support CORS (yet, they say they are looking into it) they only support back-end (server to server) calls to the Twitter API. So the Twitter API doesn't add the access-control-allow-origin to the response, and when it comes back the browser won't accept the response (preflight & fetch) because of this:

Access to fetch at 'https://api.twitter.com/2/tweets?ids=1503704881361735691&tweet.fields=text' from origin 'https://localhost' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

But with Blazor WASM I think it would be a good idea to make the calls directly to the Twitter API from within the client, so skipping the back-end API step, taking up less server resources, let the user's browser do the hard work. And yeah, this sure does scale 😉

Client/Auth Safety

Twitter API V2 does support a "Public Client" scenario to authenticate, where you only need to send your Client ID and no secret. You then can get an AccessToken (= Bearer, valid for 7200 seconds) and an optional RefreshToken to authorize further calls to the API

A Possible Solution?

I've read at Stack Overflow that people use a reverse proxy to do this, so I directly thought about using YARP for this.

I'd like to use YARP to do this:

  1. Call YARP from within the static Blazor WASM client with the full request (<query> + <authorization header>) the Twitter API v2 needs.
  2. YARP forwards the request to https://api.twitter.com/2/<query> with the authorization: Bearer <Access Token> request header
  3. YARP receives the response from https://api.twitter.com
  4. YARP adds the Access-Control-Allow-Origin header to the response.
  5. YARP passes that response, with that added header, back to the requesting Blazor WASM frontend code.

Where I am now

I've read the YARP documentation, especially Direct Forwarding, Header Routing and Cross-Origin Requests and searched the YARP Q&A Discussions. It's a lot of information and I don't know exactly where to begin but I don't mind putting more effort and time learning in it.

My Questions

  • Is what I want (see steps 1..5 above) possible with YARP?
  • If so, could you maybe point me in the right direction how I should set this up?

Thank you,
Jaap

Hi, yes this is possible to do with YARP.

Call YARP from within the static Blazor WASM client with the full request ( + ) the Twitter API v2 needs.

As long as the browser can make an HTTP request to it, YARP doesn't care that it came from a Blazor browser app.

YARP adds the Access-Control-Allow-Origin header to the response.

I would recommend you take a look at the minimal sample.

The only modification you should need is to the config file.
You must specify the correct destination, and add a Transform that adds the header.

Your config may look something like

"ReverseProxy": {
  "Routes": {
    "catchAllRoute": {
      "ClusterId": "twitterCluster",
      "Match": {
        "Path": "{*…

Replies

1 suggested answer
·
6 replies

Hi, yes this is possible to do with YARP.

Call YARP from within the static Blazor WASM client with the full request ( + ) the Twitter API v2 needs.

As long as the browser can make an HTTP request to it, YARP doesn't care that it came from a Blazor browser app.

YARP adds the Access-Control-Allow-Origin header to the response.

I would recommend you take a look at the minimal sample.

The only modification you should need is to the config file.
You must specify the correct destination, and add a Transform that adds the header.

Your config may look something like

"ReverseProxy": {
  "Routes": {
    "catchAllRoute": {
      "ClusterId": "twitterCluster",
      "Match": {
        "Path": "{**catch-all}"
      },
      "Transforms": [
        {
          "ResponseHeader": "Access-Control-Allow-Origin",
          "Set": "*"
        }
      ]
    }
  },
  "Clusters": {
    "twitterCluster": {
      "Destinations": {
        "twitterV2": {
          "Address": "https://api.twitter.com/2/"
        }
      }
    }
  }
}
6 replies
@MihaZupan

But with Blazor WASM I think it would be a good idea to make the calls directly to the Twitter API from within the client, so skipping the back-end API step, taking up less server resources, let the user's browser do the hard work. And yeah, this sure does scale 😉

To be clear, you still have to host YARP somewhere. YARP takes the place of a back-end API, it's just likely easier to set up.

@JeepNL

Yes, I know I need to host YARP somewhere, I'm renting an Ubuntu VPS at a co-location.

@JeepNL

Although a static Blazor WASM client runs in a browser you need a server (as well) to transfer the client files (& dll's) to the browser. With Blazor WASM you can use "DevServer" i.e. <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0-preview.3.22178.4" PrivateAssets="all" /> in the .csproj or setup a Hosted Blazor WASM project (= Static Client + Web API server)

I use the latter (a hosted solution) and this is my appsettings.json for the (API) server:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.AspNetCore": "Debug"
    }
  },
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1AndHttp2"
    },
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:58080"
      },
      "Https": {
        "Url": "https://localhost:58433",
        "SslProtocols": [ "Tls12", "Tls13" ]
      }
    }
  },
  "AllowedHosts": "*"
}

So the Blazor WASM client runs on (https) port 58433

I've setup YARP (based on the minimal sample) like this (appsettings.json)

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.AspNetCore": "Debug"
    }
  },
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1AndHttp2"
    },
    "Endpoints": {
      "Http": {
        "Url": "http://localhost"
      },
      "Https": {
        "Url": "https://localhost",
        "SslProtocols": [ "Tls12", "Tls13" ]
      }
    }
  },
  "AllowedHosts": "*",
  "ReverseProxy": {
    "Routes": {
      "minimumroute": {
        "ClusterId": "minimumcluster",
        "Match": {
          "Path": "{**catch-all}"
        }
      }
    },
    "Clusters": {
      "minimumcluster": {
        "Destinations": {
          "localhost": {
            "Address": "https://localhost:58433/"
          }
        }
      }
    }
  }
}

When I enter the URL https://localhost in the browser this works. YARP forwards everything to the hosted Blazor WASM solution on port 58433.

What I see in your example is also a "Path": "{**catch-all}" forwarding everything to "Address": "https://api.twitter.com/2/", but how do I connect to my Static Client on port 58433 in that case?

But possible I'm doing this all wrong and instead I should make a http request to the YARP server from the Blazor WASM client, and is that what you show in your sample. I'm thinking now about how I should make that request.

So, I'm still trying!

@JeepNL

IT WORKS! 🚀 YARP RULES AGAIN BIG TIME!

Yeah, my first setup was totally wrong. My 'thinking process' was wrong/mixed up/faulty ... a mess 😉

But your example made all the difference! So thank you so much for that! I could literally copy it.

I got the same error, but yeah, I needed to add CORS to YARP of course, that was the whole point I got the error in the first instance!

And then it worked! It worked! I'm so happy!

Here's my YARP program.cs with CORS (for devs who want to try this too)

WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);

string _origins = "https://127.0.0.1,https://localhost,https://api.twitter.com";
string[] origins = _origins.Split(",");
builder.Services.AddCors(options =>
{
	// see (origins): https://docs.microsoft.com/en-us/aspnet/core/security/cors#set-the-allowed-origins
	options.AddDefaultPolicy(builder =>
	{
		builder.WithOrigins(origins)
		.SetIsOriginAllowedToAllowWildcardSubdomains()
		.AllowCredentials()
		.AllowAnyMethod()
		.AllowAnyHeader()
		.WithExposedHeaders("*");
	});
});

builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

WebApplication? app = builder.Build();

app.UseCors(); 

app.MapReverseProxy();

app.Run();

This is so cool. For example: with Cloudflare Pages (which is free) you've unlimited bandwidth. First of all this is perfect for a Blazor WASM app because the initial download size can vary from 1MB to well, larger, A standard WASM app can quickly become 3MB. I don't mind that, I'm building interactive apps, which Blazor is perfect for IMO.

Now with this setup I don't need to use server processing power to make/generate the Twitter API requests, the user's browsers does all that. The YARP server just forwards/receives the request/response. So, this will absolutely scale. And Blazor WASM scales too very good.

Again thank you!

Answer selected by JeepNL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet
2 participants