Docs
Set-up the Backend Server

Set-up the Backend Server

Some of our templates include a custom backend server built with dart_frog.

Based on our current template catalog, you'll find the codebase for a backend server in:

Step-by-step Set-up

If your purchased template includes a backend server, you'll have to complete a few steps to complete the setup and get your backend server up-to-speed.

There are no changes required in the codebase, you will just have to connect your accounts and credentials, and eventually, deploy the backend server to production (if you want).

Here is a step-by-step guide to help you with the setup.

Add your environment variables

In the codebase of the backend server /api, there is a file named .example.env, you should rename it to .env. It will contain all the environment variables that allows your server to connect with Firebase and other external services, like Stripe or Google Maps Platform.

IMPORTANT: As the file will contain API keys, you should not push it to GitHub to avoid leaking your API keys and credentials. The .env is intentionally untracked as specified in .gitignore.

Server Env Variables

As a first step, you will have to replace the placeholders in the .env file with the actual values.

  • IS_PROD: false or true
    • If you're running the app with the development flavor, set it to false.
    • If you're running the app with the production flavor, set it to true.

With false, the backend server will not verify the user auth status and it will only utilize the sample data from the FakeDbClient. However, as you change the value to true, the backend server will connect with Firebase, authenticate the user requests using bearer tokens and fetch the actual data from your Firestore database.

  • PROJECT_ID: After you create your Firebase project and integrate Firebase in your app. Copy the project ID from the Firebase console.

Server Env Project ID

  • Other variables: There might be other variables to configure depending which template you have purchased. For example:
    • DynaMart's backend server requires the Stripe Secret key
    • Hungry's backend server requires the API key to access Google Maps Platform services.

You'll find more instructions on these additional environment variables in the next sections of the documentation.

Ingest the environment variables

After you fill all the environment variables in the .env, use envied and build_runner to complete the set-up that will allow your backend server to read these variables.

If you don't have some of these enviroment variables (e.g. you haven't set-up Stripe or Google Maps Platform), you should go ahead and complete these steps first:

Once you have all the environment variables ready to go:

  • Delete the env.g.dart file (path: api/lib/src/env/env.g.dart)
  • Run the following commands in the terminal:
# Navigate to the `api` folder
cd api
 
# Clear the code generator cache 
dart run build_runner clean
 
# Run the code generator 
dart run build_runner build --delete-conflicting-outputs

Run the server locally

After generating all the enviroment variables, you can start your server locally.

  1. If you don't have the dart_frog CLI, go ahead and install it.
# Open the terminal 
dart pub global activate dart_frog_cli
  1. Run the server locally
# Navigate the api directory
cd api 
 
# Run the server
dart_frog dev

Although your server is running locally, you can still connect it with your Firebase project. Therefore, depending on whether you added "IS_PROD=true" or "IS_PROD=false", the server will connect with the actual database or the sample data.

Obviously, you should only run the server locally, if you're developing the app. Once you release your app to the App Store / Play Store, the app must connect with a production server deployed and accessible from the internet.

Deploy the server with Cloud Run

As your app is already connected with Firebase, you already have a Google Cloud project. Therefore. Cloud Run is the simplest solution to deploy your backend server to production. Check the official docs from dart_frog to see alternative solutions for the deployment.

To deploy the server to Google Cloud Run, install the Install the gcloud CLI. Then, open your terminal and:

  • Authenticate with gcloud
gcloud auth login
  • Build your API for production use
# Navigate to the `api` folder
cd api
 
# Create a production build
dart_frog build
  • Deploy the production build to Cloud Run
# Deploy the production build to Cloud Run 
gcloud run deploy [SERVICE_NAME] \
  --source build \
  --project=[PROJECT_ID] \
  --region=[REGION] \
  --allow-unauthenticated
  • SERVICE_NAME: The name of the service deployed on Cloud Run. You can choose any name, make sure it's relevant to the project. If you're deploying the server for DynaMart, you could call it ecommerce-api.
  • PROJECT_ID: Your Firebase project is connected with a Google Cloud Project. They share the same ID.
  • REGION: The location where the server will be created. The closer to the user of your app, the better. Check the list of REGION(s) available.

API Client

In the local package of your template, you will find a local package named api_client. Its role is to facilitate the communication between the app and the backend server.

Configuration

The template contains the .env.example file at the root level of the project. You should rename it to .env, and, you should not commit it to version control (e.g. exclude it by adding it to .gitignore).

The .env contains three environment variables required to connect the app to the backend server. As you run the backend server locally, you will use the IOS_LOCALHOST_BASE_URL and the ANDROID_LOCALHOST_BASE_URL, but, once your server is deployed to production, you can connect your app with the production version of the backend server.

ANDROID_LOCALHOST_BASE_URL='http://localhost:8080'
IOS_LOCALHOST_BASE_URL='http://localhost:8080'
BASE_URL='URL of YOUR SERVER'

After you fill the variables in the .env, you will have to use envied and build_runner to complete the set-up. Open the terminal, make sure you are at the root level of the project:

  • Delete the env.g.dart file (path: lib/src/env/env.g.dart)
  • Run the following commands in the terminal:
# Clear the code generator cache 
dart run build_runner clean
 
# Run the code generator 
dart run build_runner build --delete-conflicting-outputs

By default, both the development and the production flavor of the application are set-up to send requests to the localhost (e.g. the backend server running locally). Once you have the URL of your backend server, go ahead and modify the instantiation of the ApiClient in the main() so that the app requests are sent to the live backend server.

void main() async {
  unawaited(
    bootstrap(
      () async {
        ...
        final apiClient = ApiClient(
          // TODO: Use the production base URL once it's available
          // baseUrl: Env.BASE_URL,
          baseUrl: Platform.isIOS
              ? Env.IOS_LOCALHOST_BASE_URL
              : Env.ANDROID_LOCALHOST_BASE_URL,
          idTokenStream: authClient.idToken,
          refreshIdToken: authClient.refreshIdToken,
        );
        ...
      },
    ),
  );
}

πŸŽ‰ Congrats, your backend server should be ready-to-go! πŸŽ‰

Breakdown of the Backend Server

Here's a quick breakdown of the main components of the backend server built for these atomsbox's templates. Each directory and file serves a specific function, from handling security and environment configurations to processing specific types of requests like payment operations or external webhook events.

β”œβ”€β”€ README.md
β”œβ”€β”€ main.dart
β”œβ”€β”€ pubspec.yaml
β”œβ”€β”€ lib
β”‚   └── src
β”‚       β”œβ”€β”€ auth
β”‚       β”‚   β”œβ”€β”€ auth_user.dart
β”‚       β”‚   β”œβ”€β”€ authenticator.dart
β”‚       β”‚   └── jwt.dart
β”‚       β”œβ”€β”€ env
β”‚       β”‚   β”œβ”€β”€ env.dart
β”‚       β”‚   └── env.g.dart
β”‚       β”œβ”€β”€ handlers
β”‚       β”‚   └── webhooks
β”‚       β”‚       └── stripe
β”‚       β”‚           β”œβ”€β”€ handle_customer_created_event.dart
β”‚       β”‚           β”œβ”€β”€ ...
β”‚       └── repositories
β”‚           β”œβ”€β”€ order_repository.dart
β”‚           β”œβ”€β”€ ...
└── routes
    β”œβ”€β”€ _middleware.dart
    β”œβ”€β”€ api
    β”‚   └── v1
    β”‚       β”œβ”€β”€ payments
    β”‚       β”‚   β”œβ”€β”€ customers
    β”‚       β”‚   β”‚   └── index.dart
    β”‚       β”‚   β”œβ”€β”€ ...
    β”‚       β”œβ”€β”€ ...
    β”‚       β”‚   β”œβ”€β”€ ...
    └── index.dart

Authentication

The auth directory contains the classes and functions that allows client-server authentication through bearer tokens. These classes enable the backend server to verify who is sending the request (when needed) and to determine whether the requester is authorized to perform such an operation or not.

  1. auth_user.dart: Defines a simple server-side user model.
  2. authenticator.dart: Implements a JwtMiddleware class for the backend server, which validates JSON Web Tokens (JWTs) using public keys retrieved from a Google API endpoint. This allows authorized users access to protected resources based on the validity of their tokens.
  3. jwt.dart: Contains a class JWT for handling JSON Web Tokens, including methods to parse these tokens from strings, decode their contents, verify their signatures with a public key.

Repositories

The repositories directory contains the repositories that abstract the data layer to set-up data manipulation and access without exposing the database directly inside the backend server routes.

Depending on which is the template that you have purchased, you'll find different repositories. To give you an example, the order_repository.dart from DynaMart enables the backend server to perform CRUD operations related to orders (e.g. creating a new order in the database or updating an existing one).

Routes

The routes directory contains all the route handlers for the template. Each route handler corresponds to a specific endpoint. These handlers are responsible for processing incoming requests, performing business logic, and sending appropriate responses back to the client.