Building Dart APIs with Google Cloud Run

Learn how to build backend applications using Dart and Google Cloud Run. By Alhassan Kamil.

Leave a rating/review
Download materials
Save for later

As a Flutter developer, you have a robust framework for developing cross-platform, front-end apps. How cool would it be if you could reuse your knowledge in Dart to create backend applications?

Well, you can! In this tutorial you’re going to build MNote, a note management API that any Flutter app can consume. In the process, you’ll learn how to:

  • Develop and run servers in Dart using the shelf and shelf_router packages.
  • Intercept and manipulate HTTP requests and responses using middleware.
  • Create a Docker image for your project.
  • Deploy a Dart API on Google Cloud Run.
Note: This tutorial assumes you have some Dart experience, are familiar with Unix terminal or Windows Powershell, understand basic HTTP methods and have a Google Cloud account with billing enabled.

Getting Started

Download the project by clicking the Download Materials button at the top or bottom of this tutorial and extract it to a suitable location. This tutorial uses VS Code, but you can use any text editor/IDE.

Open the starter project; its structure should look like this:

The project structure

In summary:

  1. bin/mnote.dart is the entrypoint that bootstraps and serves the application.
  2. lib/controllers/note_controller.dart contains logic for managing notes.
  3. lib/controllers/user_controller.dart holds user authentication logic.
  4. lib/helpers: Utility functions (helper.dart) and middleware (middleware.dart) are here.
  5. lib/models: Contains model files.
  6. lib/routes/app_routes.dart holds top-level app routes definitions.
  7. lib/routes/note_routes.dart defines note routes.
  8. lib/routes/user_routes.dart has definitions for user routes.

Since you now understand the project structure, you’re ready to run it.

Running Your First Dart API Server

While at the root of the starter project, open a new terminal or PowerShell window. Next, run the following to download packages:

dart pub get

Then, start the server using:

dart run

The above command runs bin/mnote.dart, which bootstraps and starts the server. Once the server is running, you’ll see an output like the one below:

Your first API server is running

That means your server is listening for connections.

Open a new terminal window and enter:

curl http://localhost:8080
Note: You may need to use curl.exe instead of curl on Windows. Install cURL if you get an error that it’s not installed.

Click Enter and you should see an output similar to the one below:

CURL response

Congratulations on successfully running your first Dart server!

Let’s see how the output came about.

Since bin/mnote.dart is basically your server, you may want to take a little look into what’s in there to produce such result. Don’t hesitate, open it and have a look at main():

// 1
final app = Router();
app.get('/', (Request request) {
  final aboutApp = {
    'name': 'MNote',
    'version': 'v1.0.0',
    'description': 'A minimal note management API to take and save notes'
  return Response.ok(jsonEncode(aboutApp));

// 2
final handler = const Pipeline().addMiddleware(logRequests()).addHandler(app);

// 3
final mServer = await server.serve(handler, InternetAddress.anyIPv4, 8080);

// 4
print('Server started at http://${}:${mServer.port}');

There you have it! :]

This is what each part does:

  1. Creates a Router. A router ensures that requests entering the application get mapped to functions (correctly called handlers) that can process them. In the case above, it routes any HTTP GET requests on the path / to the second handler argument which basically returns a JSON response.
  2. Constructs a Pipeline handler that adds a logging middleware and registers the router.
  3. Creates a server with the handler that listens for requests from all available addresses on port 8080.
  4. Lastly, prints that the server is listening on the given address-port combination.

Great, you now have a basic server. But this can’t authenticate users and manage notes. In the later sections, you’ll solve that. For now, let’s set up a Google Cloud project.

Setting up a Google Cloud Project

You’ll deploy MNote on Google Cloud Run, a service which is part of the Google Cloud Platform (GCP). So you need to have an active Google Cloud account with billing enabled to proceed.

Creating the Project

You’ll use Cloud Firestore to store notes and user information. As a result, you have to create a Firebase project as that automatically creates a Google Cloud project.

So, navigate to and click Create a project:

Click Create a project

Next, enter MNote into Project name, accept the terms and click Continue:
Enter project name

The next page asks you to enable Google Analytics, disable it and click Create project:

Disable Google Analytics

After a few seconds, the project should be ready. Click Continue:

Project is ready

The project’s Overview page opens. Click the Web button under Get started by adding Firebase to your app:

Create a web app

Enter “MNote” into App nickname and click Register app:

Enter MNote and click Register app

Next, click Continue to console:

Click Continue to console

Back in the console, click Cloud Firestore under Choose a product to add to your app:

Choose Cloud Firestore

Now click Create database, select Start in production mode and click Next:

Create database

Select Start in production mode

Finaly, set the database location and click Enable:

Set your database location

Now the project is ready to use Cloud Firestore. But you’ll need to create a service account to access it in your code.

Creating a Service Account

A service account allows automated access to Google APIs data in place of an API user. So, while in Firebase console, go ahead and click ⚙️ besides Project Overview at the top of the sidebar, then click Project settings:

Click Project settings

The Project settings page opens. Select the Service accounts tab and click Generate new private key:

Generate private key

Click Generate key on the Generate new private key dialog to generate and download the service account JSON file:

Click Generate key

Open the downloaded file in a text editor.

In bin/mnote.dart, add a getCredentials() function above main() with the following code:

import 'package:googleapis_auth/auth_io.dart';

ServiceAccountCredentials getCredentials() {
  return ServiceAccountCredentials.fromJson({
    'private_key_id': '<key ID from service account file>',
    'private_key': '<Your project\'s service account private key>',
    'client_email': '[something@]',
    'client_id': '<ID from service account file>',
    'type': 'service_account'

Replace the following in the function:

  • with the corresponding private_key_id value from the service account file you downloaded.
  • with the private_key value.
  • [something@] with the client_email value.
  • And with the client_id value.

Lastly, replace the value of projectId in lib/helpers/helper.dart with the project_id value from the service account file:

static const projectId = 'mnote-c7379';

Now you can use Cloud Firestore in your code. Set up billing for the project to finish up.