Skip to content

Serve a monolithic application via amplify gen 2

1

I have a monolithic application that needs to run on amplify gen 2, but the issue is, it has a single entry point that serves the frontend as well. The frontend is on /build and the backend on /dist. The server uses hono for api, and react(vite) for frontend.

Currently when i deploy with amplify, I have to select the build directory which in our case, serves only the frontend, leaving the backend, but i want to be able to run the backend since it serves the front-end within the same domain using /api for my api

Here is how my entry.server is supposed to serve

    .route('/api', api)
    .use('/static/*', serveStatic({ root: isProd ? './build/' : './' }))
    .use('/assets/*', serveStatic({ root: isProd ? './build/' : './' }))
    .get('/*', (c) => c.html(html));
if (isProd) {
    serve({ ...app, port: 8080 }, (info) => {
        console.log(`Listening on http://localhost:${info.port}`);
    });

here is my scripts

  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build && rollup -c rollup.config.mjs",
    "start": "cross-env NODE_ENV=production node dist/src/entry.server.js",
    "lint": "eslint . --max-warnings 0",
    "preview": "vite preview"
  },
1 Answer
0

Hi Emmanuel,

Thank you for your detailed question! It’s great that you’re exploring AWS Amplify Gen 2 for deploying your monolithic application. Let’s break this down step by step with detailed code examples and additional context to help you implement the solution smoothly.


Clarifying the Issue

You have a monolithic application that needs to run on AWS Amplify Gen 2. The application includes both a frontend (served from the /build directory) and a backend (served from the /dist directory). The server uses Hono for the API and React (via Vite) for the frontend. You are trying to serve both frontend and backend components simultaneously under the same domain, specifically ensuring that API routes like /api work as intended.

Currently, AWS Amplify mandates choosing a single build directory, which leads to the backend not being accessible since it serves only the frontend.


Key Terms

  • AWS Amplify Gen 2: A hosting service for modern web apps, offering integration with CI/CD and serverless resources.
  • Hono: A lightweight, high-performance web framework.
  • React: A JavaScript library for building user interfaces, often used for single-page applications.
  • Vite: A modern frontend build tool optimized for speed and developer experience.
  • Monolithic Application: An architecture where the frontend, backend, and API are combined in a single codebase or deployment.

The Solution (Our Recipe)

Steps at a Glance:

  1. Separate frontend and backend builds into distinct deployment workflows.
  2. Deploy the frontend with Amplify and use Amazon API Gateway for the backend API routes.
  3. Configure custom domain routing for seamless integration of frontend and backend services.
  4. Integrate frontend and backend.

Step-by-Step Guide:

1. Separate Frontend and Backend Builds

Why this step?
Separating the builds allows Amplify to handle the static frontend, while the backend is managed by a service more suited for dynamic requests, like AWS Lambda or API Gateway.

Code Example: Update your package.json to define separate build scripts for the frontend and backend:

"scripts": {
    "build:frontend": "vite build",
    "build:backend": "tsc && rollup -c rollup.config.mjs"
}

Then run the following commands to generate builds for both:

npm run build:frontend
npm run build:backend

Resulting Directory Structure:

project-root/
├── build/         # Frontend build output
├── dist/          # Backend build output
├── src/           # Source code
├── package.json
└── rollup.config.mjs

2. Deploy Frontend with Amplify

Why this step?
Amplify Hosting is optimized for static files (e.g., frontend build outputs). It’s ideal for serving React applications with minimal effort.

Amplify Configuration:
In the Amplify Console, specify the build directory as the artifact base directory. Add an amplify.yml to your repository for the frontend build:

version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - npm run build:frontend
  artifacts:
    baseDirectory: /build
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*  

Commit this file to your repository, connect the repository to Amplify Hosting, and Amplify will automatically build and deploy your frontend.


3. Deploy Backend Using Amazon API Gateway

Why this step?
API Gateway is ideal for managing API routes, providing flexibility for dynamic backend logic and integration with Lambda functions.

Code Example: Create a Lambda function for your backend:

import { app } from './dist/app.js';

export const handler = async (event) => {
    return await app.fetch(event);
};

Deploy the backend using the AWS CLI:

zip -r backend.zip ./dist
aws lambda create-function --function-name myBackendAPI \
    --runtime nodejs18.x \
    --role <your-lambda-execution-role> \
    --handler index.handler \
    --zip-file fileb://backend.zip

Set Up API Gateway:

  1. Create an HTTP API:
    aws apigatewayv2 create-api \
      --name MyAPI \
      --protocol-type HTTP \
      --target arn:aws:lambda:<region>:<account-id>:function:myBackendAPI
  2. Add a route for /api:
    aws apigatewayv2 create-route \
      --api-id <api-id> \
      --route-key "ANY /api/{proxy+}"
    aws apigatewayv2 create-integration \
      --api-id <api-id> \
      --integration-type AWS_PROXY \
      --integration-uri arn:aws:lambda:<region>:<account-id>:function:myBackendAPI
  3. Grant Lambda Permissions:
    aws lambda add-permission \
      --function-name myBackendAPI \
      --action lambda:InvokeFunction \
      --principal apigateway.amazonaws.com \
      --source-arn arn:aws:execute-api:<region>:<account-id>:<api-id>/*

4. Integrate Frontend and Backend

Why this step?
To ensure the frontend and backend work seamlessly under the same domain, you need to configure Amplify's custom domain settings so /api requests are routed to the backend hosted on API Gateway.

Custom Domain Setup in Amplify Console:

  1. Add a Custom Domain:
    In the Amplify Console:

    • Go to the "Domain management" section of your app.
    • Add your custom domain (e.g., example.com) and follow the prompts to verify ownership through your DNS provider.
  2. Add Rewrite and Redirect Rules:
    Configure rules to redirect /api routes to your API Gateway endpoint. Add a rule like this:

    Source AddressTarget AddressTypeStatus Code
    /api/<*>https://<api-id>.execute-api.<region>.amazonaws.com/<*>Rewrite200
  3. Environment Variables for Dynamic API Integration:
    Add the following variable in your Amplify amplify.yml file to dynamically configure the API endpoint:

    frontend:
      environment:
        REACT_APP_API_URL: "https://<api-id>.execute-api.<region>.amazonaws.com"

React Code Example (With Custom Domain):
Your React app can now use the /api endpoint seamlessly:

const API_URL = process.env.REACT_APP_API_URL || '/api';

const fetchData = async () => {
    const response = await fetch(`${API_URL}/example-endpoint`);
    const data = await response.json();
    console.log(data);
};

fetchData();

Closing Thoughts

By following this approach, you leverage the strengths of AWS services: Amplify Hosting for static assets, API Gateway for dynamic backend routing, and Lambda for serverless compute. This hybrid solution ensures a seamless and scalable deployment of your monolithic application.

Feel free to share updates or reach out if you have further questions, Emmanuel. You’re on the right track, and I’m here to help if needed! 😊✨


Take care, Emmanuel, and happy coding! 🚀


Cheers, Aaron 😊✨

answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.