Building a Microservices Cloud Backend using Go and PostgreSQL
Tags: go, tutorial, cloud, microservices
Published: 2023-12-06 09:44:01


😬 **Building microservices applications can be a pain.** Normally you need to deal with a lot of boilerplate, and it can be hard to ensure end-to-end type-safety.

💡 **This guide shows you how to deploy a fully type-safe microservices application in Go** — implementing the backend for a Trello application as an example.

This is what our backend architecture will look like, consisting of two services each with their own PostgreSQL database:

![backend architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czv0qpf1gndlx1il1rii.png)

To build our app, we'll be using [Encore](https://encore.dev), a backend development platform that provides a type-safe [Infrastructure SDK](https://encore.dev/docs/primitives) for declaratively defining infrastructure in Go code.

We'll then use Encore to automatically provision and deploy our application.

**🚀 What's on deck:**
- Install Encore
- Create your backend app from a template
- Run locally
- Deploy to Encore's free development cloud

## 💽 Install Encore

Install the Encore CLI to run your local environment:
- **macOS:** `brew install encoredev/tap/encore`
- **Linux:** `curl -L https://encore.dev/install.sh | bash`
- **Windows:** `iwr https://encore.dev/install.ps1 | iex`

## 🔨 Create your app

We'll be starting from a template that has two services, each with a couple of API endpoints and their own databases. (See the architecture diagram above.)

When you have [installed Encore](https://encore.dev/docs/install), create your application from the example app using this command:

```bash
encore app create my-app-name --example=trello-clone
```

## 🏁 Run your app locally

Before running the application, make sure you have synced the project dependencies by running `go mod tidy` from the project root folder.

Also make sure that [Docker](https://docker.com) is installed and running, it's required when running Encore applications locally that use SQL databases.

With that out of the way, let's start the application using this command:

```bash
encore run
```

You should see this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzumjxut8v9n9ltb89k3.png)

### 🕹 Open the local development dashboard

While `encore run` is running, open <http://localhost:9400/> to view Encore's [local developer dashboard](https://encore.dev/docs/observability/dev-dash).

Here you can see automatic API documentation, try your APIs with the API explorer, and study your application behavior using Encore's built-in distributed tracing.

![Local development dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hby5pyzv0ss93eh8ql6m.png)

## 📞 Call the API

Now that your app is running, you can ping the `board.Create` endpoint to create a Trello board. Either from the API explorer in the local dev dashboard, or using cURL:

```bash
curl 'http://localhost:4000/board.Create' -d '{"Name":"my board"}'
```
💡 Once you've called the API, you can view a trace of the request from the local dev dashboard.

![trace in local dev dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yj4kyrmw3gn0g2j2bmvf.jpeg)

## 🤔 How it works: Defining API endpoints

Let's take a look at the code implementing the endpoint we just called:

```go
// Create creates a new board.
//encore:api public
func Create(ctx context.Context, params *CreateParams) (*Board, error) {
	b := &Board{Name: params.Name, Created: time.Now()}
	err := sqldb.QueryRow(ctx, `
		INSERT INTO board (name, created)
		VALUES ($1, $2)
		RETURNING id
	`, params.Name, b.Created).Scan(&b.ID)
	if err != nil {
		return nil, fmt.Errorf("could not create board: %v", err)
	}
	return b, nil
}
```

💡 With Encore you define API endpoints by adding the `//encore:api` annotation to any normal Go function. At compile time, Encore then generates the boilerplate code necessary to turn this into a real API endpoint. ([Learn more in the docs](https://encore.dev/docs/primitives/services-and-apis#defining-apis))

## 🤔 How it works: Creating microservices

With Encore you create a microservice by defining one or more APIs within a regular Go package.

Encore recognizes this as a service, and uses the package name as the service name. When deploying, Encore will automatically [provision the required infrastructure](https://encore.dev/docs/deploy/infra) for each service.

On disk it might look like this:

```
/my-app
├── encore.app          // ... and other top-level project files
│
├── hello               // hello service (a Go package)
│   ├── hello.go        // hello service code
│   └── hello_test.go   // tests for hello service
│
└── world               // world service (a Go package)
    └── world.go        // world service code
```

## 🤔 How it works: Making API calls

Calling an API endpoint looks like a regular function call with Encore. Import the service package as a regular Go package using `import "encore.app/package-name"` and then call the API endpoint like a regular function. Encore will then automatically generate the necessary boilerplate at compile-time.

💡In the example below, we import the service package `hello` and call the `Ping` endpoint using a function call to `hello.Ping`.

```go
import "encore.app/hello" // import service

//encore:api public
func MyOtherAPI(ctx context.Context) error {
    resp, err := hello.Ping(ctx, &hello.PingParams{Name: "World"})
    if err == nil {
        log.Println(resp.Message) // "Hello, World!"
    }
    return err
}
```

**💡This means your development workflow is as simple as building a monolith, even if you use multiple services.** You get all the benefits of function calls, like compile-time checking of all the parameters and auto-completion in your editor, while still allowing the division of code into logical components, services, and systems.

Then when compiling your application, Encore uses [static analysis](https://encore.dev/docs/introduction#meet-the-encore-application-model) to parse all API calls and compiles them to proper API calls.

## 🚀 Deploy to the cloud

Commit the new files to the project's git repo and trigger a deploy to Encore's free development cloud by running:

```bash
git add -A .
git commit -m 'Commit message'
git push encore
```

After triggering the deployment, you will see a URL where you can view its progress in Encore's [Cloud Dashboard](https://app.encore.dev).👈

It will look something like: `https://app.encore.dev/$APP_ID/deploys/...`

From there you can also see metrics, traces, and connect your own AWS or GCP account to use for production deployment.

### 🎉 Great job - you're done!

You now have the start of a scalable Go microservices backend running in the cloud, complete with PostgreSQL databases.

Keep building with these Open Source [App Templates](https://encore.dev/templates).👈

If you have questions or want to share your work, join the developers hangout in [Encore's community on Discord](https://encore.dev/discord).👈