Is that a best practice to host WebApi as a seperate project
Asked Answered
D

2

12

I am working on an Single Page Application with Web Api and Angular JS. I want to grab all the best practices for a great spa applications.

I want to know that is that a best practice to host Web Api project as a seperate project or just include with in same project along with your spa.

Thanks

Draughty answered 21/5, 2015 at 11:14 Comment(1)
Technically it doesn't even need a project. I prefer to use sublime instead of Visual Studio for Angular.Rounded
A
17

I believe your best bet is to have the SPA into a separate project and even better a different repository, since this is more flexible and supports many different development/deployment scenarios, especially as your project grows bigger.

I personally started with the mixed files approach and moved to the separate Repos approach just days after the project went beyond the size of the Todos examples.

Some of the pros of having separate projects/repos are:

  • better search context for search/replace or goto file/class.
  • fewer files per project/solution so visual studio is not crawling to its knees.
  • you can use different editors, more easily, for your spa project if you get better support for javascript/typescript there than in VS (eg. webstorm).
  • file structure is more readable in the small solution explorer window since there is less nesting
  • different people can work independently on the frontend/backend
  • your SPA can have its own versioning and you can mix and match FE/BE versions as required
  • simpler continuous integration since you do not mix frontend/backend build steps
  • you can decide at deployment time if they are going to be hosted on
    the same host, or different machines (after enabling CORS support)
  • Finally it makes your SPA a first class citizen and not just a subfolder of your web api.

On the opposite side I cannot think of any advantage of having the SPA as a subfolder to the WebApi except maybe for very small projects that you want to keep complexity to a minimum.

Acrylonitrile answered 21/5, 2015 at 11:44 Comment(3)
+1. To add one more, as you grow, you may have more clients for your API, and the SPA will be just one of them. So, as you would keep iphone/android code to separate codebases, the same applies for your SPA.Quadric
I am regretting having them in the same project. If you use CI/CD to deploy it starts getting messy. Telling your project to use the /app folder for dev and then the root directory for staging/production works but quickly gets complicated. I am migrating the SPA to it's own project. I can now remove all of the OWIN middleware and let my WebApi project do what it's primarily intended for.Instability
This does mean you have to handle breaking changes as there's no single source of truth anymore. Furthermore this makes it harder to setup automated tests as it introduces extra steps (such as checkout frontend repo). Alot of other points you mention are not entirely correct either, even if the front end is in the same repo that doesn't mean you deploy it to the same machine. Its really a trade-off and I wouldn't say one is better than the other.Lymphoma
A
2

Should one host SPA independently from API?

If you need to scale the front-end independently from the back-end then go with a Content Delivery Network (CDN) that lets you use own domain name, has configurable caching policies and all other modern attributes like 100% SLA, etc.

Your concerns might be

  • Desynchronised deployment of the two essential parts of a single solution.
  • Extra resources (human-hours) to maintain the distributed infrastructure (another point of failure);
  • Trade-offs for delivering essential parameters to the front-end. Either
    • you make the front-end bundle mutable between staging environments and include the parameters or
    • you keep it immutable and need extra time on the start-up to fetch them.

I'd dismiss concerns regarding additional OPTIONS requests for CORS as HTTP/2 brings latency down to 0.

Hosting SPA from WebAPI wouldn't have those concerns (but doesn't allow independent scaling).

How to host SPA from .NET WebAPI?

When it comes to hosting a SPA from a .NET API, there are three extension methods that do all the heavy lifting (starting from .NET Core 2.x and it's still the case in .NET 5):

  • UseSpa serves the default page (index.html) and redirects all requests there.
  • UseStaticFiles serves other than index.html static files under the web root folder (wwwroot, by default). Without this method Kestrel would return index.html in response on all requests for static content.
  • UseSpaStaticFiles does a similar thing but it requires ISpaStaticFileProvider to be registered to resolve location of the static files. You need it if the static files are NOT under the default web root folder wwwroot.

To host SPA from wwwroot you need just UseSpa and UseStaticFiles methods.

Don't forget 2 important things:

  • caching policies for static assets;
  • injecting environment variables on serving the front-end.

So your Configure method in the Startup.cs may have the following additions:

// Serves other than 'index.html' files from wwwroot folder with a cache policy
app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        var headers = ctx.Context.Response.GetTypedHeaders();
        headers.CacheControl = new CacheControlHeaderValue { MaxAge = TimeSpan.FromDays(12*30) };
    }
});
// Serves 'index.html' with no-cache policy and passing variables in a cookie
app.UseSpa(c => c.Options.DefaultPageStaticFileOptions = new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        var response = ctx.Context.Response; 
        response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue
        {
            NoCache = true,
            NoStore = true,
            MustRevalidate = true,
            MaxAge = TimeSpan.Zero
        };
        // Passing a variable to SPA in a short-lived cookie
        response.Cookies.Append("importantVariable", "Value", new CookieOptions { MaxAge = TimeSpan.FromSeconds(30) });
    }
})

See more in this blog post that also considers other options. No problems modifying the index.html & static files for injecting variables if you don't favour cookies.

Also check out this open source project at GitHub that does the trick with hosting SPA and passing parameters in cookies (direct link to Startup configuration and reading cookies in Angular).

Ay answered 21/3, 2021 at 8:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.