Deploy frontend applications
Serve a single-page application from Azion and send its API calls to the backend you already run, under one domain.
A front-end team builds the web interface of a product whose backend already runs elsewhere, in a cloud region, a data center, or a SaaS API. The interface is a single-page application built with a framework such as React, Vue, or Angular, and the backend stays where it is. This page serves the compiled interface from Azion, falls back to the entry HTML for client-side routes, and sends every request under /api/ through a connector to the existing backend, on the same domain. The result is measured by interface load time per region, the time from a commit to a live change, and the latency the routing adds to API calls.
This use case does not cover applications whose backend also runs on Azion. For that, refer to Deploy full-stack applications globally.
Prerequisites
- An Azion account. If you do not have one, create an account.
- A single-page application deployed to Azion with the Azion CLI and its framework preset, from a project folder that holds its
azion.config.cjsfile. The deploy creates the bucket, the application, and the workload that serve the interface. For the React route, refer to Build with React. Vue and Angular follow the same route with their own preset. - A backend that answers HTTPS on port
443under its own hostname.
Required products
| The interface needs | Which means | Product | Documented in |
|---|---|---|---|
| Interface files stored on Azion | The build output, uploaded by azion deploy to a bucket that workloads can only read | Object Storage | Build with React |
| Interface files served from cache | The cache setting the preset’s Deliver Static Assets and Set Cache Policy rule applies | Cache | Azion CLI deploy |
| Client-side routes that load the interface | The preset’s Redirect to index.html rule, which rewrites route requests to /index.html | Application Accelerator | Rewrite Request |
| API calls that reach the existing backend | A connector to the backend, and a rule that sends /api/ to it before any other rule runs | Connectors | Route an API path to a backend from azion.config |
Reference architecture
This page builds the Client-rendered frontend over origin APIs: the browser renders the interface from static files, and every data call crosses Azion to the existing backend.
The diagram carries two flows. The publish flow runs from the repository to the bucket, and it moves only when the team deploys a change. The request flow starts at the browser, and the application’s rules split it three ways: API paths cross to the backend, files of the build come from Cache or the bucket, and every client-side route returns the entry HTML. Only the first path leaves Azion, so a page load reads static files, and every data call reaches the backend origin.
Dataflow
- A push to the repository, or
azion deployfrom the project folder, builds the interface and uploads the build output to the bucket. - A browser’s request reaches the workload on the interface’s domain, and the application runs its request rules in order.
- A request under
/api/matches the first rule, which sends it through the backend connector and ends the request phase, so no later rule rewrites or caches it. - The backend answers the API call, and the browser renders the data into the interface.
- A request for a file of the build, such as a script or a stylesheet, is answered from Cache. On a miss, the application reads the file from the bucket and caches it.
- A request for a client-side route, such as
/orders/42, is rewritten to/index.html, and the router in the browser renders that route.
Platform Resources
- Object Storage: holds the compiled interface in a bucket that workloads can only read. The interface has no server of its own, so the bucket is its origin.
- Cache: keeps the files of the build near the browsers that request them, so repeated page loads read the files from Cache instead of the bucket.
- application: the Platform Resource that serves the interface’s domain and holds the rules that split files, routes, and API calls.
- Rules Engine: the Feature of the application that routes API paths to the connector and rewrites client-side routes to the entry HTML. The API rule runs first and ends the Request Phase, because every matching rule runs in order and a later rule could rewrite an API call.
- connector: the Platform Resource that reaches the backend. It sends the backend’s own name in the
Hostheader, so a backend that serves several sites answers for it. - backend API: the integration that is the existing origin. It stays where it runs, and every data call of the interface reaches it.
- Azion GitHub App: the integration that imports the repository and deploys every push to it, so the interface on Azion follows the repository.
Other designs for this use case
- Server-rendered frontend over origin APIs: for interfaces that need server rendering, such as search-indexed or personalized pages. Functions render each page on request with a framework such as Next.js or Nuxt and call the backend’s APIs, so the backend sits in the request flow and the failure flow of every page that Cache does not hold.
How the design works
This section explains how the API route reaches the backend, and why it runs before every other rule.
The API route
The API route is a connector to the backend and a rule that sends every /api/ request to it. Both go in the project’s azion.config.cjs, the file that azion deploy applies to the application, so the route ships with every deploy of the interface.
The rule is the first entry of the request rules, and it ends the request phase. The preset’s Redirect to index.html rule rewrites route requests to /index.html, and every matching rule of the phase runs, in order. Without a stop, a later rule could rewrite an API call’s path or apply the cache setting of the interface files to it. Finish Request Phase ends the phase, so the rules after it never see an API call.
Each entry of the route takes its value for a reason:
- Connector
name:api-backend, the name the rule’sset_connectorbehavior names. The connector sits beside the storage connector the preset declared. - Connector address:
api.example.com, the backend’s own hostname. connectionOptions.transportPolicy:force_https, because the backend answers HTTPS on port443only.connectionOptions.host:api.example.com, because the default${host}would send the interface’s domain, which a backend that routes requests by name may not answer for.- Rule position: the first entry of
rules.request, before the rules the preset generated, so the preset’sRedirect to index.htmlrule never sees an API call. - Rule criterion:
${uri}starts_with/api/, every API call the interface makes. - Rule behaviors:
set_connectorwithapi-backend, thenfinish_request_phase, so the backend receives the call, and no later rule rewrites or caches it.
Measuring results
| Metric | Where to read it | What working looks like |
|---|---|---|
| Interface load time per region | The pageloadtime field of Edge Pulse measurements, taken in the visitors’ browsers, refer to Edge Pulse quickstart. For the share Azion serves, the requestTime of workloadMetrics grouped by geolocCountryName, refer to Real-Time Metrics GraphQL fields | Page loads stay close across regions, because every location serves the same cached files |
| Time from a commit to a live change | The Console page that follows each deploy, which azion deploy opens. Refer to Azion CLI deploy | A later deploy answers in about two minutes, after the first one has reached every location |
| Latency the routing adds to API calls | The Request Time and Upstream Response Time of HTTP Requests records in Real-Time Events, filtered with request_uri like '/api/%'. Refer to Data sources | The gap between the two values stays small next to the backend’s own response time |
Best practices
- Call the API with relative paths. An interface that calls
/api/ordersreaches the backend through the same domain that served it, so the backend’s hostname appears only in the connector, and moving the backend changes one connector instead of the interface code. - Keep the API rule first, and end the request phase in it. A later rule can still rewrite or cache what an earlier rule routed, because every matching rule runs. Finish Request Phase is what keeps the rewrite and the interface cache off the API path. For how rules run in order, refer to How Applications works.
- Declare the route in the configuration file.
azion deployapplies the rules the file declares to the application. A rule kept in one place goes out with every deploy of the interface. - Send the backend’s own name in the Host header. A backend that serves several sites picks the site by
Host. For when to send${host}and when a literal name, refer to Set the Host header to a name your origin answers for.