Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): provide server context awareness to routing and HttpClient requests #1223

Merged
merged 16 commits into from
Jul 26, 2024

Conversation

brandonroberts
Copy link
Member

@brandonroberts brandonroberts commented Jul 20, 2024

PR Checklist

Closes #844
Closes #995
Closes #1182

What is the new behavior?

  • Adds the provideServerContext provider function from @analogjs/router/server
  • Adds the injectBaseURL(), injectRequest(), and injectResponse() functions from @analogjs/router/tokens
  • Adds the requestContextInterceptor function that is environment aware when making HttpClient requests

By providing the server context, and request interceptor. Any HttpClient request that begins with a / is requested using the full URL on the server, client, and during prerendering without manually setting the VITE_ANALOG_PUBLIC_BASE_URL environment variable.

credit goes to @osnoser1 for the initial implementation

// app.config.ts
import {
  provideHttpClient,
  withFetch,
  withInterceptors,
} from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';
import { provideFileRouter, requestContextInterceptor } from '@analogjs/router';
import { withNavigationErrorHandler } from '@angular/router';

import { cookieInterceptor } from './interceptors/cookies.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideFileRouter(withNavigationErrorHandler(console.error)),
    provideHttpClient(
      withFetch(),
      withInterceptors([
       cookieInterceptor,
       requestContextInterceptor // <-- request context interceptor
      ])
    ),
    provideClientHydration(),
  ],
};
// main.server.ts
import 'zone.js/node';
import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { renderApplication } from '@angular/platform-server';
import { providerServerContext } from '@analogjs/router/server';
import { ServerContext } from '@analogjs/router/tokens';

import { config } from './app/app.config.server';
import { AppComponent } from './app/app.component';

if (import.meta.env.PROD) {
  enableProdMode();
}

export function bootstrap() {
  return bootstrapApplication(AppComponent, config);
}

export default async function render(
  url: string,
  document: string,
  serverContext: ServerContext
) {
  const html = await renderApplication(bootstrap, {
    document,
    url,
    platformProviders: [providerServerContext(serverContext)], // <-- provide server context
  });

  return html;
}

An example service that fetches todos from the API endpoint.

// todos.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Todo } from './todos';

@Injectable({
  providedIn: 'root',
})
export class TodosService {
  http = inject(HttpClient);

  getAll() {
    return this.http.get<Todo[]>('/api/v1/todos');
  }

  getData() {
    return this.http.get<Todo[]>('/assets/data.json');
  }

  getExternalData() {
    return this.http.get<Todo[]>('https://someapi.com/api/v1/users'); 
  }
}

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

[optional] What gif best describes this PR or how it makes you feel?

Copy link

netlify bot commented Jul 20, 2024

Deploy Preview for analog-app ready!

Name Link
🔨 Latest commit c8443aa
🔍 Latest deploy log https://app.netlify.com/sites/analog-app/deploys/66a31c6835cc13000804c156
😎 Deploy Preview https://deploy-preview-1223--analog-app.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Jul 20, 2024

Deploy Preview for analog-docs ready!

Name Link
🔨 Latest commit c8443aa
🔍 Latest deploy log https://app.netlify.com/sites/analog-docs/deploys/66a31c68a5612a0008784f0e
😎 Deploy Preview https://deploy-preview-1223--analog-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Jul 20, 2024

Deploy Preview for analog-ng-app ready!

Name Link
🔨 Latest commit c8443aa
🔍 Latest deploy log https://app.netlify.com/sites/analog-ng-app/deploys/66a31c687c760700082c1810
😎 Deploy Preview https://deploy-preview-1223--analog-ng-app.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@brandonroberts brandonroberts changed the title Feat router server context feat(router): provide server context awareness to routing and HttpClient requests Jul 20, 2024
Copy link

netlify bot commented Jul 20, 2024

Deploy Preview for analog-blog ready!

Name Link
🔨 Latest commit c8443aa
🔍 Latest deploy log https://app.netlify.com/sites/analog-blog/deploys/66a31c68f478e400081070c3
😎 Deploy Preview https://deploy-preview-1223--analog-blog.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

return baseUrl;
}

export function getRequestProtocol(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi, I took this function from here 😄 https://github.com/unjs/h3/blob/bd7831bde071d368cf3f120128cc1823bfb4c3c8/src/utils/request.ts#L238

But I implemented it in the code because Initially, I hadn't access to the H3Event

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In development, we won't have access to the H3Event, but the req and res are the same with the Vite server in development, and Nitro in the production server.

export function requestContextInterceptor(
req: HttpRequest<unknown>,
next: HttpHandlerFn
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qq, is this interceptor only for fetching data before loading a page, right?

In the analog app, I have implemented some API routes, and I made a request from an angular service using the HttpClient, and I didn't have to use an interceptor to prepend the full URL; the behavior I assumed was that as long as the angular app has the correct base URL, I don't have to worry about how the URLs are handled in ssr and csr, all work at the same.

I haven't commented before because I haven't tried the SSR data fetching so far https://analogjs.org/docs/features/data-fetching/server-side-data-fetching

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for any request with HttpClient that begins with a / meaning its meant to be an "internal" request.

By default, even if the base href is set in the index.html, it won't affect HttpClient requests.

On the server, the full URL is used
One the client, we can use the full URL or just leave it as is
During pre-rendering, API requests remain as-is. Any other requests. Use the full URL.

I'm thinking of including support for passing some header to skip this interceptor also.

@brandonroberts
Copy link
Member Author

Preview deployments tested and working with Node, Netlify, Cloudflare, and Vercel.

@nckirik
Copy link
Contributor

nckirik commented Jul 22, 2024

i wonder if the provider function name providerServerContext was intentional.

shouldn't it be provideServerContext or serverContextProvider?

@brandonroberts
Copy link
Member Author

brandonroberts commented Jul 22, 2024

i wonder if the provider function name providerServerContext was intentional.

shouldn't it be provideServerContext or serverContextProvider?

It intentionally begins with provide which aligns with Angular conventions for provider functions like provideHttpClient, provideRouter, etc.

@nckirik
Copy link
Contributor

nckirik commented Jul 22, 2024

i wonder if the provider function name providerServerContext was intentional.

shouldn't it be provideServerContext or serverContextProvider?

It intentionally begins with provide which aligns with Angular conventions for provider functions like provideHttpClient, provideRouter, etc.

I guess, I couldn't express myself 😅 I starts with provider instead of provide. I suspect that was a typo

@brandonroberts
Copy link
Member Author

i wonder if the provider function name providerServerContext was intentional.
shouldn't it be provideServerContext or serverContextProvider?

It intentionally begins with provide which aligns with Angular conventions for provider functions like provideHttpClient, provideRouter, etc.

I guess, I couldn't express myself 😅 I starts with provider instead of provide. I suspect that was a typo

Ahhh, great catch. Yea, I've been staring at it too long 😅

@brandonroberts brandonroberts merged commit b1cdd08 into beta Jul 26, 2024
24 checks passed
@brandonroberts brandonroberts deleted the feat-router-server-context branch July 26, 2024 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants