# https://encore.dev llms-full.txt ## Encore.ts Framework Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) ![](https://encore.dev/assets/img/dithered-clouds.png) ## Quick Start Guide Build your first Encore.ts application in minutes [Get started](https://encore.dev/docs/ts/quick-start) Encore.ts is an open source backend framework for building type-safe distributed system. It provides a declarative approach to working with essential backend primitives like APIs, microservices, databases, queues, caches, cron jobs, and storage buckets. The framework comes with a lot of built-in tooling for a productive end-to-end developer experience: - **Local Environment Management**: Encore automatically sets up and runs your local development environment and all local infrastructure. - **Enhanced Observability**: Encore comes with tools like a [Local Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash), [tracing](https://encore.dev/docs/ts/observability/tracing), and a database explorer for monitoring application behavior. - **Automatic Documentation**: Generates and maintains [up-to-date documentation](https://encore.dev/docs/ts/observability/service-catalog) for APIs and services, and created [architecture diagrams](https://encore.dev/docs/ts/observability/flow) for your system. - **AI Integration:** Encore comes with built-in tools for effective AI assisted development, like [AI instructions](https://encore.dev/docs/ts/ai-integration) and an [MCP server](https://encore.dev/docs/ts/cli/mcp). - **DevOps Automation Platform (Optional)**: [Encore Cloud](https://encore.cloud/) is an optional platform for automating infrastructure provisioning and DevOps processes in your cloud on AWS and GCP. [**Watch an intro video**![](https://encore.dev/assets/icons/features/preview-envs.png)\\ \\ Get to know the core concepts of Encore in this short video.](https://www.youtube.com/watch?v=vvqTGfoXVsw) [**Example apps**![](https://encore.dev/assets/icons/features/flow.png)\\ \\ Ready-made starter apps to inspire your development.](https://github.com/encoredev/examples) [**Join Discord**\\ \\ Find answers, ask questions, and chat with other Encore developers.](https://encore.dev/discord) [**Star on GitHub**\\ \\ Get involved and star Encore on GitHub.](https://github.com/encoredev/encore) [Installation](https://encore.dev/docs/ts/install) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/overview.md) ## Encore Application Model Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore works by using static analysis to understand your application. This is a fancy term for parsing and analyzing the code you write and creating a graph of how your application works. This graph closely represents your own mental model of the system: boxes and arrows that represent systems and services that communicate with other systems, pass data and connect to infrastructure. We call it the Encore Application Model. Because the Open Source framework, parser, and compiler, are all designed together, Encore can ensure 100% accuracy when creating the application model. Any deviation is caught as a compilation error. Using this model, Encore can provide tools to solve problems that normally would be up to the developer to do manually. From creating architecture diagrams and API documentation to provisioning cloud infrastructure. We're continuously expanding on Encore's capabilities and are building a new generation of developer tools that are enabled by Encore's understanding of your application. The framework, parser, and compiler that enable this are all [Open Source](https://github.com/encoredev/encore). ![](https://encore.dev/assets/docs/flow-diagram.png) ## [Standardization brings clarity](https://encore.dev/docs/go/concepts/application-model\#standardization-brings-clarity) Developers make dozens of decisions when creating a backend application. Deciding how to structure the codebase, defining API schemas, picking underlying infrastructure, etc. The decisions often come down to personal preferences, not technical rationale. This creates a huge problem in the form of fragmentation! When every stack looks different, all tools have to be general purpose. When you adopt Encore, many of these stylistic decisions are already made for you. The Encore framework ensures your application follows modern best practices. And when you run your application, Encore's Open Source parser and compiler check that you're sticking to the standard. This means you're free to focus your energy on what matters: writing your application's business logic. [Benefits](https://encore.dev/docs/go/concepts/benefits) [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/concepts/application-model.md) ## Encore Preview Environments [Sign up](https://encore.dev/signup) [Book a demo](https://encore.dev/book) Concepts - [Introduction](https://encore.dev/docs/platform/introduction) - [AI Integration](https://encore.dev/docs/platform/ai-integration) Deployment - [Deploying & CI/CD](https://encore.dev/docs/platform/deploy/deploying) - [Connect your cloud account](https://encore.dev/docs/platform/deploy/own-cloud) - [Environments](https://encore.dev/docs/platform/deploy/environments) - [Preview Environments](https://encore.dev/docs/platform/deploy/preview-environments) - [Application Security](https://encore.dev/docs/platform/deploy/security) Infrastructure - [Provisioning & Environments](https://encore.dev/docs/platform/infrastructure/infra) - [Infrastructure Configuration](https://encore.dev/docs/platform/infrastructure/configuration) - GCP Infrastructure - AWS Infrastructure - Kubernetes deployment - [Neon Postgres](https://encore.dev/docs/platform/infrastructure/neon) - [Cloudflare R2](https://encore.dev/docs/platform/infrastructure/cloudflare) - [Managing database users](https://encore.dev/docs/platform/infrastructure/manage-db-users) Observability - [Metrics](https://encore.dev/docs/platform/observability/metrics) - [Distributed Tracing](https://encore.dev/docs/platform/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/platform/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/platform/observability/service-catalog) Integrations - [GitHub](https://encore.dev/docs/platform/integrations/github) - [Custom Domains](https://encore.dev/docs/platform/integrations/custom-domains) - [Webhooks](https://encore.dev/docs/platform/integrations/webhooks) - [OAuth Clients](https://encore.dev/docs/platform/integrations/oauth-clients) - [Auth Keys](https://encore.dev/docs/platform/integrations/auth-keys) - [API Reference](https://encore.dev/docs/platform/integrations/api-reference) - [Terraform](https://encore.dev/docs/platform/integrations/terraform) Migration guides - [Migrate to Encore](https://encore.dev/docs/platform/migration/migrate-to-encore) - [Migrate away from Encore](https://encore.dev/docs/platform/migration/migrate-away) Management & Billing - [Compliance & Security](https://encore.dev/docs/platform/management/compliance) - [Plans & billing](https://encore.dev/docs/platform/management/billing) - [Telemetry](https://encore.dev/docs/platform/management/telemetry) - [Roles & Permissions](https://encore.dev/docs/platform/management/permissions) - [Usage limits](https://encore.dev/docs/platform/management/usage) Other - Product comparisons When using [Encore Cloud Pro](https://encore.cloud/pricing), you automatically get ephemeral Preview Environments for each Pull Request. Preview Environments are free, fully-managed development environments that run on Encore Cloud. They let you test changes without managing infrastructure or incurring cost. See the [infra docs](https://encore.dev/docs/platform/infrastructure/infra#preview-environments) if you're curious about exactly how Preview Environments are provisioned. ## [Using Preview Environments](https://encore.dev/docs/platform/deploy/preview-environments\#using-preview-environments) To use Preview Environments, you first need to [connect your application to GitHub](https://encore.dev/docs/platform/integrations/github). Preview Environments are named after the pull request, for example PR #72 creates a Preview Environment named `pr:72` with the API base url `https://pr72-$APP_ID.encr.app`. You can also view the environment in the Encore Cloud dashboard, where the url will be `https://app.encore.cloud/$APP_ID/envs/pr:72`. ![Preview environment linked in GitHub](https://encore.dev/assets/docs/ghpreviewenv.png) ## [Populate databases with test data automatically](https://encore.dev/docs/platform/deploy/preview-environments\#populate-databases-with-test-data-automatically) Preview Environments can automatically come with pre-populated test data thanks to Neon's database branching feature. Here's how it works: 1. Your main database (typically in staging) contains your test data 2. When a Preview Environment is created, it gets a fresh database that's an exact copy of your main database 3. This happens automatically - no manual data copying needed! #### [Setup instructions](https://encore.dev/docs/platform/deploy/preview-environments\#setup-instructions) 1. Go to [Encore Cloud dashboard](https://app.encore.cloud/) 2. Select your app > App Settings > Preview Environments 3. Choose which environment's database to copy from (e.g., staging) 4. Save your changes **Note:** This feature requires using Neon as your database provider, which is: - Default for Encore Cloud environments - Optional for AWS and GCP environments ![](https://encore.dev/assets/docs/pr-neon.png) ## [Frontend Collaboration](https://encore.dev/docs/platform/deploy/preview-environments\#frontend-collaboration) Preview Environments make it really easy to collaborate and test changes with your frontend. Just update your frontend API client to point to the `pr:#` environment. This is a one-line change since your API client always specifies the environment name, e.g. `https://-.encr.app/`. If your pull request makes changes to the API, you can [generate a new API client](https://encore.dev/docs/ts/cli/client-generation) for the new backend API using `encore gen client --env=pr:72 --lang=typescript my-app` [Environments](https://encore.dev/docs/platform/deploy/environments) [Application Security](https://encore.dev/docs/platform/deploy/security) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/platform/deploy/preview-environments.md) ## Encore FAQ Page Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) ## [About the project](https://encore.dev/docs/go/faq\#about-the-project) **Is Encore Open Source?** Yes, check out the project on [GitHub](https://github.com/encoredev/encore). **Is there a community?** Yes, you're welcome to join the developer community on [Discord](https://encore.dev/discord). ## [Can I use X with Encore?](https://encore.dev/docs/go/faq\#can-i-use-x-with-encore) **Can I use Python with Encore?** Encore currently supports Go and TypeScript. Python support in on the [roadmap](https://encore.dev/roadmap) and will be available in 2026. **Can mix TypeScript and Go in one application?** Support for mixing languages in coming. Currently, if you want to use both TypeScript and Go, you need to create a separate application per language and integrate using APIs. **Can I use Azure / Digital Ocean?** Encore Cloud currently supports automating deployments to AWS and GCP. Azure support in on the [roadmap](https://encore.dev/roadmap) and will be available in 2026. If you want to use other cloud providers like Azure or Digital Ocean, you can follow the [self-hosting instructions](https://encore.dev/docs/go/self-host/docker-build). **Can I use MongoDB / MySQL with Encore?** Encore currently has built-in support for PostgreSQL. To use another type of database, like MongoDB and MySQL, you will need to set it up and integrate as you normally would when not using Encore. **Can I use AWS lambda with Encore?** Not right now. Encore currently supports AWS Fargate and EKS (along with CloudRun and GKE on Google Cloud Platform). ## [IDE Integrations](https://encore.dev/docs/go/faq\#ide-integrations) **Is there an Encore plugin for Goland / IntelliJ?** Yes, Encore's official Goland plugin is available in the [JetBrains marketplace](https://plugins.jetbrains.com/plugin/20010-encore). **Is there an Encore plugin for VS Code?** Not yet, it's coming soon. ## [Troubleshooting](https://encore.dev/docs/go/faq\#troubleshooting) **symlink creation error on Windows** Encore currently relies on symbolic links, which may be disabled by default. A common fix for this issue is to enable "developer mode" in the Windows settings (Settings > System > For developers > Developer mode). **`node` errors** You might need to restart the Encore daemon, e.g. if your PATH has changed since installing nvm. Restart the daemon by running `encore daemon`. [AI Integration](https://encore.dev/docs/go/ai-integration) [Benefits](https://encore.dev/docs/go/concepts/benefits) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/faq.md) ## Encore Quick Start Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this short guide, you'll learn key concepts and experience the Encore workflow. It should only take about 5 minutes to complete and by the end you'll have an API running in Encore's free development Cloud (Encore Cloud). To make it easy to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Install the Encore CLI](https://encore.dev/docs/go/quick-start\#1-install-the-encore-cli) To develop with Encore, you need the Encore CLI. It provisions your local environment, and runs your local development dashboard complete with tracing and API documentation. πŸ₯ Install by running the appropriate command for your system: macOS Windows Linux Brew `$ curl -L https://encore.dev/install.sh | bash` ## [2\. Create your app](https://encore.dev/docs/go/quick-start\#2-create-your-app) πŸ₯ Create your app by running: ```bash $ encore app create ``` If this is your first time using Encore, you’ll be prompted to create a free Encore Cloud account. This enables Encore to manage things like secrets and fully automate cloud deployments (which you’ll use later in the tutorial). πŸ₯ Select `Go` as your app’s language. πŸ₯ Choose a starter template. Pick `Hello World` and continue. Optional: Install AI instructions to improve how tools like Cursor and Claude Code work with Encore. After selecting your template, choose the AI instructions for the tool you plan to use. πŸ₯ Pick a name for your app. Encore will now create your app in a folder named after your app. ### [Let's take a look at the code](https://encore.dev/docs/go/quick-start\#lets-take-a-look-at-the-code) Part of what makes Encore different is the simple developer experience when building distributed systems. Let's look at the code to better understand how to build applications with Encore. πŸ₯ Open the `hello.go` file in your code editor. It's located in the folder: `your-app-name/hello/`. You should see this: ```go hello/hello.go // Service hello implements a simple hello world REST API. package hello import ( "context" ) // This is a simple REST API that responds with a personalized greeting. // //encore:api public path=/hello/:name func World(ctx context.Context, name string) (*Response, error) { msg := "Hello, " + name + "!" return &Response{Message: msg}, nil } type Response struct { Message string } ``` As you can see, it's all standard Go code except for a few lines specific to Encore's Backend Framework. One such element is the API annotation: ```bash //encore:api public path=/hello/:name ``` This annotation is all that's needed for Encore to understand that the Go package `hello` is a service, and the `World` function is a public API endpoint. To create more services and endpoints, you simply create new Go packages and define endpoints using the `//encore:api` annotation. _If you're curious, you can read more about [defining APIs](https://encore.dev/docs/go/primitives/defining-apis)._ Encore.go provides several other declarative ways of using backend primitives, such as databases, Pub/Sub, and scheduled tasks. All defined in your application code. ## [3\. Start your app & Explore the Local Development Dashboard](https://encore.dev/docs/go/quick-start\#3-start-your-app--explore-the-local-development-dashboard) πŸ₯ Run your app locally: ```bash $ cd your-app-name # replace with the app name you picked $ encore run ``` You should see this: That means your local development environment is up and running! Encore takes care of setting up all the necessary infrastructure for your applications, even including databases and Pub/Sub. ### [Open the Local Development Dashboard](https://encore.dev/docs/go/quick-start\#open-the-local-development-dashboard) You can now start using your [Local Development Dashboard](https://encore.dev/docs/go/observability/dev-dash). πŸ₯ Open [http://localhost:9400](http://localhost:9400/) in your browser to access it. The Local Development Dashboard is a powerful tool to help you move faster when you're developing new features. It comes with an API explorer, a Service Catalog with automatically generated documentation, and powerful observability features like [distributed tracing](https://encore.dev/docs/go/observability/tracing). Through the Local Development Dashboard you also have access to [Encore Flow](https://encore.dev/docs/go/observability/encore-flow), a visual representation of your microservice architecture that updates in real-time as you develop your application. ### [Call your API](https://encore.dev/docs/go/quick-start\#call-your-api) πŸ₯ While you keep the app running, call your API from the API Explorer: ![](https://encore.dev/assets/docs/qs_call.png) You can also open a separate terminal to call your API endpoint: ```bash $ curl http://localhost:4000/hello/world {"Message": "Hello, world!"} ``` If you see this JSON response, you've successfully made an API call to your very first Encore application. Well done, you're on your way! ### [Review a trace of the request](https://encore.dev/docs/go/quick-start\#review-a-trace-of-the-request) You can now take a look at the trace for the request you just made by clicking on it in the right column in the local dashboard. ![](https://encore.dev/assets/docs/qs_trace.png) With such a simple API, there's not much to it, just a simple request and response. However, just imagine how powerful it is to have tracing when you're developing a more complex system with multiple services, Pub/Sub, and databases. (Learn more about Encore's tracing capabilities in the [tracing docs](https://encore.dev/docs/go/observability/tracing).) ## [4\. Make a code change](https://encore.dev/docs/go/quick-start\#4-make-a-code-change) Let's put our mark on this API and make our first code change. πŸ₯ Head back to your code editor and look at the `hello.go` file again. If you can't come up a creative change yourself, why not simply change the "Hello" message to a more sassy "Howdy"? πŸ₯ Once you've made your change, save the file. When you save, the daemon run by the Encore CLI instantly detects the change and automatically recompiles your application and reloads your local development environment. The output where you're running your app will look something like this: ```output Changes detected, recompiling... Reloaded successfully. TRC registered endpoint endpoint=World path=/hello/:name service=hello TRC listening for incoming HTTP requests ``` πŸ₯ Test your change by calling your API again. ```bash $ curl http://localhost:4000/hello/world {"Message": "Howdy, world!"} ``` Great job, you made a change and your app was reloaded automatically. Now you're ready to head to the cloud! ## [5\. Deploy your app](https://encore.dev/docs/go/quick-start\#5-deploy-your-app) ### [Generating Docker image](https://encore.dev/docs/go/quick-start\#generating-docker-image) You can either deploy by generating a Docker image for you app using: ```bash $ encore build docker MY-IMAGE:TAG ``` This will compile your application using the host machine and then produce a Docker image containing the compiled application. You can now deploy this anywhere you like. Learn more in the [self-host docs](https://encore.dev/docs/go/self-host/docker-build). ### [Deploy using Encore Cloud](https://encore.dev/docs/go/quick-start\#deploy-using-encore-cloud) Optionally, you can use [Encore Cloud](https://encore.dev/use-cases/devops-automation) to automatically deploy your application. It comes with built-in free development hosting, and for production offers fully automated deployment to your own cloud on AWS or GCP. πŸ₯ To deploy, simply push your changes to Encore: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore Cloud will now build and test your app, provision the needed infrastructure, and deploy your application to a staging environment. After triggering the deployment, you will see a URL where you can view its progress in the Encore Cloud dashboard. It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` πŸ₯ Open the URL to access the Encore Cloud dashboard and check the progress of your deployment. You can now use the Cloud Dashboard to view production [traces](https://encore.dev/docs/go/observability/tracing), [connect your cloud account](https://encore.dev/docs/platform/deploy/own-cloud), [integrate with GitHub](https://encore.dev/docs/platform/integrations/github), and much more. ## [What's next?](https://encore.dev/docs/go/quick-start\#whats-next) - Check out the [REST API tutorial](https://encore.dev/docs/go/tutorials/rest-api) to learn how to create endpoints, use databases, and more. - Join the friendly community on [Discord](https://encore.dev/discord) to ask questions and meet other Encore developers. [Installation](https://encore.dev/docs/go/install) [AI Integration](https://encore.dev/docs/go/ai-integration) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/quick-start.mdx) ## Encore TypeScript Framework Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) ![](https://encore.dev/assets/img/dithered-clouds.png) ## Quick Start Guide Build your first Encore.ts application in minutes [Get started](https://encore.dev/docs/ts/quick-start) Encore.ts is an open source backend framework for building type-safe distributed system. It provides a declarative approach to working with essential backend primitives like APIs, microservices, databases, queues, caches, cron jobs, and storage buckets. The framework comes with a lot of built-in tooling for a productive end-to-end developer experience: - **Local Environment Management**: Encore automatically sets up and runs your local development environment and all local infrastructure. - **Enhanced Observability**: Encore comes with tools like a [Local Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash), [tracing](https://encore.dev/docs/ts/observability/tracing), and a database explorer for monitoring application behavior. - **Automatic Documentation**: Generates and maintains [up-to-date documentation](https://encore.dev/docs/ts/observability/service-catalog) for APIs and services, and created [architecture diagrams](https://encore.dev/docs/ts/observability/flow) for your system. - **AI Integration:** Encore comes with built-in tools for effective AI assisted development, like [AI instructions](https://encore.dev/docs/ts/ai-integration) and an [MCP server](https://encore.dev/docs/ts/cli/mcp). - **DevOps Automation Platform (Optional)**: [Encore Cloud](https://encore.cloud/) is an optional platform for automating infrastructure provisioning and DevOps processes in your cloud on AWS and GCP. [**Watch an intro video**![](https://encore.dev/assets/icons/features/preview-envs.png)\\ \\ Get to know the core concepts of Encore in this short video.](https://www.youtube.com/watch?v=vvqTGfoXVsw) [**Example apps**![](https://encore.dev/assets/icons/features/flow.png)\\ \\ Ready-made starter apps to inspire your development.](https://github.com/encoredev/examples) [**Join Discord**\\ \\ Find answers, ask questions, and chat with other Encore developers.](https://encore.dev/discord) [**Star on GitHub**\\ \\ Get involved and star Encore on GitHub.](https://github.com/encoredev/encore) [Installation](https://encore.dev/docs/ts/install) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/overview.md) ## Encore Development Benefits Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Using Encore.go to declare infrastructure in application code helps unlock several benefits: - **Local development with instant infrastructure**: Encore.go automatically sets up necessary infrastructure as you develop. - **Rapid feedback**: Catch issues early with type-safe infrastructure, avoiding slow deployment cycles. - **No manual configuration required**: No need for Infrastructure-as-Code. Your code is the single source of truth. - **Unified codebase**: One codebase for all environments; local, preview, and cloud. - **Cloud-agnostic by default**: Encore.go provides an abstraction layer on top of the cloud provider's APIs, so you avoid becoming locked in to a single cloud. - **Evolve infrastructure without code changes**: As requirements evolve, you can change the provisioned infrastructure without making code changes, you only need to change the infrastructure configuration which is separate from the application code. - **AI-assisted development**: Encore is built for AI coding assistants. With [Encore-specific rules and MCP integration](https://encore.dev/docs/go/ai-integration), AI understands your architecture and can generate type-safe, pattern-consistent code and introspect your appβ€”services, APIs, databases, and traces. ## [No DevOps experience required](https://encore.dev/docs/go/concepts/benefits\#no-devops-experience-required) Encore provides open source tools to help you integrate with your cloud infrastructure, enabling you to self-host your application anywhere that supports Docker containers. Learn more in the [self-host documentation](https://encore.dev/docs/go/self-host/docker-build). You can also use [Encore Cloud](https://encore.dev/use-cases/devops-automation), which fully automates provisioning and managing infrastructure in your own cloud on AWS and GCP. This approach dramatically reduces the level of DevOps expertise required to use scalable, production-ready, cloud services like Kubernetes and Pub/Sub. And because your application code is the source of truth for infrastructure requirements, it ensures the infrastructure in all your environments are always in sync with the application's requirements. ## [Simplicity without giving up flexibility](https://encore.dev/docs/go/concepts/benefits\#simplicity-without-giving-up-flexibility) Encore.go provides integrations for common infrastructure primitives, but also allows for flexibility. You can always use any cloud infrastructure, even if it's not built into Encore.go. If you use Encore's [Cloud Platform](https://encore.dev/use-cases/devops-automation), it [automates infrastructure](https://encore.dev/docs/platform/infrastructure/infra) using your own cloud account, so you always have full access to your services from the cloud provider's console. [FAQ](https://encore.dev/docs/go/faq) [Application Model](https://encore.dev/docs/go/concepts/application-model) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/concepts/benefits.md) ## Encore CLI Installation Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) If you are new to Encore, we recommend following the [quick start guide](https://encore.dev/docs/go/quick-start). ## [Install the Encore CLI](https://encore.dev/docs/go/install\#install-the-encore-cli) To develop locally with Encore, you first need to install the Encore CLI. This is what provisions your local development environment, and runs your Local Development Dashboard complete with logs, tracing, and API documentation. macOS Windows Linux Brew `$ curl -L https://encore.dev/install.sh | bash` ##### Please note To locally run Encore apps with databases, you also need to have [Docker](https://www.docker.com/) installed and running. ### [Optional: Add AI/LLM instructions](https://encore.dev/docs/go/install\#optional-add-aillm-instructions) To help AI coding assistants (Cursor, Claude Code, GitHub Copilot, etc.) understand how to use Encore, run this from your app directory: ```bash encore llm-rules init ``` This prompts you to select your tool and generates the appropriate config (e.g. `.cursorrules`, `CLAUDE.md`) and MCP setup where supported. For full details and other options, see [AI Tools Integration](https://encore.dev/docs/go/ai-integration). ### [Build from source](https://encore.dev/docs/go/install\#build-from-source) If you prefer to build from source, [follow these instructions](https://github.com/encoredev/encore/blob/main/CONTRIBUTING.md). ## [Update to the latest version](https://encore.dev/docs/go/install\#update-to-the-latest-version) Check which version of Encore you have installed by running `encore version` in your terminal. It should print something like: ```bash encore version v1.28.0 ``` If you think you're on an older version of Encore, you can easily update to the latest version by running `encore version update` from your terminal. [Quick Start](https://encore.dev/docs/go/quick-start) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/install.md) ## Encore Go Framework Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) ![](https://encore.dev/assets/img/dithered-clouds.png) ## Quick Start Guide Build your first Encore.go application in minutes [Get started](https://encore.dev/docs/go/quick-start) Encore.go is an open source backend framework for building distributed system. It provides a declarative approach to working with essential backend primitives like APIs, microservices, databases, queues, caches, cron jobs, and storage buckets. The framework comes with a lot of built-in tooling for a productive end-to-end developer experience: - **Local Environment Management**: Encore automatically sets up and runs your local development environment and all local infrastructure. - **Enhanced Observability**: Encore comes with tools like a [Local Development Dashboard](https://encore.dev/docs/go/observability/dev-dash), [tracing](https://encore.dev/docs/go/observability/tracing), and a database explorer for monitoring application behavior. - **Automatic Documentation**: Generates and maintains [up-to-date documentation](https://encore.dev/docs/go/observability/service-catalog) for APIs and services, and created [architecture diagrams](https://encore.dev/docs/go/observability/encore-flow) for your system. - **AI Integration:** Encore comes with built-in tools for effective AI assisted development, like [AI instructions](https://encore.dev/docs/go/ai-integration) and an [MCP server](https://encore.dev/docs/go/cli/mcp). - **DevOps Automation Platform (Optional)**: [Encore Cloud](https://encore.cloud/) is an optional platform for automating infrastructure provisioning and DevOps processes in your cloud on AWS and GCP. [**Watch a demo video**![](https://encore.dev/assets/icons/features/preview-envs.png)\\ \\ See how to build an event-driven app with Encore.](https://www.youtube.com/watch?v=ipj1HdG4dWA) [**Example apps**![](https://encore.dev/assets/icons/features/flow.png)\\ \\ Ready-made starter apps to inspire your development.](https://github.com/encoredev/examples) [**Join Discord**\\ \\ Find answers, ask questions, and chat with other Encore developers.](https://encore.dev/discord) [**Star on GitHub**\\ \\ Get involved and star Encore on GitHub.](https://github.com/encoredev/encore) [Installation](https://encore.dev/docs/go/install) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/overview.md) ## AI Integration Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore is built for AI-assisted development. Encore-specific rules and [MCP](https://encore.dev/docs/go/ai-integration#mcp-server) integration let AI understand your architecture and generate type-safe code that follows your patterns. Run `encore run` to start your app; Encore provisions local infrastructure automatically. For production, [self-host](https://encore.dev/docs/go/self-host/build) or use [Encore Cloud](https://encore.cloud/) to provision infrastructure in your own AWS or GCP account. ## [What AI Enables](https://encore.dev/docs/go/ai-integration\#what-ai-enables) Encore's declarative APIs and infrastructure primitives give AI a clear model to work with. AI can add databases, pub/sub topics, and other resources with built-in guardrails, and use MCP to introspect your appβ€”services, APIs, databases, and tracesβ€”so it can suggest accurate, pattern-consistent code. ## [Enabling AI for Your Project](https://encore.dev/docs/go/ai-integration\#enabling-ai-for-your-project) There are two ways to set up AI support: - [Method 1: Using the CLI](https://encore.dev/docs/go/ai-integration#method-1-using-the-cli) (recommended) - [Method 2: Using Encore Skills](https://encore.dev/docs/go/ai-integration#method-2-using-encore-skills) ### [Method 1: Using the CLI](https://encore.dev/docs/go/ai-integration\#method-1-using-the-cli) **New projects:** When you run `encore app create`, you'll be prompted to select an AI tool. Encore generates the appropriate configuration files for your chosen tool. ![](https://encore.dev/assets/docs/initllm.png) **Existing projects:** Run `encore llm-rules init` to add AI support: ```bash encore llm-rules init ``` This prompts you to select a tool and generates the appropriate configuration file (`.cursorrules`, `CLAUDE.md`, etc.). Both commands also set up MCP server configuration for tools that support it (Cursor, Claude Code). If you want to set up MCP manually, see [MCP Server](https://encore.dev/docs/go/ai-integration#mcp-server) below. Supported tools: Cursor, Claude Code, VS Code, AGENTS.md, and Zed. ### [Method 2: Using Encore Skills](https://encore.dev/docs/go/ai-integration\#method-2-using-encore-skills) Use the [Encore skills package](https://github.com/encoredev/skills) which works with Cursor, Claude Code, GitHub Copilot, and 10+ other AI agents: ```bash npx add-skill encoredev/skills ``` You can also install specific skills or target specific agents: ```bash # List available skills npx add-skill encoredev/skills --list # Install to specific agents npx add-skill encoredev/skills -a cursor -a claude-code ``` ## [MCP Server](https://encore.dev/docs/go/ai-integration\#mcp-server) Encore's [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server gives AI agents deep introspection into your application: querying databases, calling APIs, inspecting services, and analyzing traces. ### [Start the Server](https://encore.dev/docs/go/ai-integration\#start-the-server) From your Encore app directory: ```bash encore mcp start ``` This displays connection information. Keep it running while using your AI tools. ### [Connect Cursor](https://encore.dev/docs/go/ai-integration\#connect-cursor) **Quick setup:** Use this button (update `your-app-id` to your actual app ID): [![Add encore-mcp MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=encore-mcp&config=eyJjb21tYW5kIjoiZW5jb3JlIG1jcCBydW4gLS1hcHA9eW91ci1hcHAtaWQifQ%3D%3D) **Manual setup:** Create `.cursor/mcp.json`: ```json { "mcpServers": { "encore-mcp": { "command": "encore", "args": ["mcp", "run", "--app=your-app-id"] } } } ``` Find your app ID with `encore app info` or in the [Encore dashboard](https://app.encore.dev/). ### [Connect Claude Code](https://encore.dev/docs/go/ai-integration\#connect-claude-code) From your Encore app directory: ```bash claude mcp add --transport stdio encore-mcp -- encore mcp run --app=your-app-id ``` Verify with `claude mcp list`. You should see `encore-mcp` in the list. ## [What AI Can Do](https://encore.dev/docs/go/ai-integration\#what-ai-can-do) With Encore skills and MCP connected, AI can: - **Define infrastructure in code** \- AI declares databases, pub/sub, cron jobs, buckets, and other [primitives](https://encore.dev/docs/go/primitives) - **Generate type-safe APIs** \- code that follows your patterns and passes validation - **Understand architecture** \- inspect services and how they connect via MCP - **Query databases** \- introspect schema and data to generate accurate queries - **Debug with tracing** \- view request traces, timing, and span details to pinpoint issues - **Test instantly** \- run `encore run` to test with real infrastructure, not mocks ### [In Practice](https://encore.dev/docs/go/ai-integration\#in-practice) #### [Smarter Debugging with Tracing](https://encore.dev/docs/go/ai-integration\#smarter-debugging-with-tracing) AI can access Encore's distributed tracing via MCP to debug issues intelligently. Instead of guessing, AI can view actual request traces, analyze timing across services, and inspect span details to pinpoint exactly where things went wrong. This creates a powerful feedback loop: generate code, test it, analyze the traces, and iterate. #### [Database Introspection](https://encore.dev/docs/go/ai-integration\#database-introspection) AI can query your actual database schema and data via MCP. This means AI understands your real data model and can generate accurate queries, suggest schema changes, and debug data issues by inspecting actual records. #### [Instant Validation with Real Infrastructure](https://encore.dev/docs/go/ai-integration\#instant-validation-with-real-infrastructure) When you run `encore run`, Encore provisions real local infrastructure (databases, pub/sub, etc.). AI can generate code and immediately test it against real services, catching issues early and ensuring the code works before you deploy. Example prompts: - "Add an endpoint that publishes to a pub/sub topic, call it and verify in traces" - "Query the users database and show accounts created in the last week" - "Create a new service with CRUD endpoints connected to PostgreSQL" ## [Learn More](https://encore.dev/docs/go/ai-integration\#learn-more) - [MCP Server Documentation](https://encore.dev/docs/go/cli/mcp) \- Complete MCP reference - [Encore Skills Repository](https://github.com/encoredev/skills) \- Available skills and installation - [Quick Start Guide](https://encore.dev/docs/go/quick-start) \- Build your first Encore app [Quick Start](https://encore.dev/docs/go/quick-start) [FAQ](https://encore.dev/docs/go/faq) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/ai-integration.md) ## REST API Tutorial Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this tutorial you will create a REST API for a URL Shortener service. In a few short minutes, you'll learn how to: - Create REST APIs with Encore - Use PostgreSQL databases - Use the local development dashboard to test your app - Create and run tests This is the end result: ```go 1 // Service url takes URLs, generates random short IDs, and stores the URLs in a database. 2 package url 3 4 import ( 5 "context" 6 "crypto/rand" 7 "encoding/base64" 8 9 "encore.dev/storage/sqldb" 10 ) 11 12 type URL struct { 13 ID string // short-form URL id 14 URL string // complete URL, in long form 15 } 16 17 type ShortenParams struct { 18 URL string // the URL to shorten 19 } 20 21 // Shorten shortens a URL. 22 // 23 //encore:api public method=POST path=/url 24 func Shorten(ctx context.Context, p *ShortenParams) (*URL, error) { 25 id, err := generateID() 26 if err != nil { 27 return nil, err 28 } else if err := insert(ctx, id, p.URL); err != nil { 29 return nil, err 30 } 31 return &URL{ID: id, URL: p.URL}, nil 32 } 33 34 // generateID generates a random short ID. 35 func generateID() (string, error) { 36 var data [6]byte // 6 bytes of entropy 37 if _, err := rand.Read(data[:]); err != nil { 38 return "", err 39 } 40 return base64.RawURLEncoding.EncodeToString(data[:]), nil 41 } 42 43 // insert inserts a URL into the database. 44 func insert(ctx context.Context, id, url string) error { 45 _, err := db.Exec(ctx, ` 46 INSERT INTO url (id, original_url) 47 VALUES ($1, $2) 48 `, id, url) 49 return err 50 } 51 52 // Get retrieves the original URL for the id. 53 // 54 //encore:api public method=GET path=/url/:id 55 func Get(ctx context.Context, id string) (*URL, error) { 56 u := &URL{ID: id} 57 err := db.QueryRow(ctx, ` 58 SELECT original_url FROM url 59 WHERE id = $1 60 `, id).Scan(&u.URL) 61 return u, err 62 } 63 64 // Below we define a database named 'url', using the database 65 // migrations in the "./migrations" folder. 66 // Encore provisions, migrates, and connects to the database. 67 // Learn more: https://encore.dev/docs/go/primitives/databases 68 69 // 'url' database is used to store the URLs that are being shortened. 70 var db = sqldb.NewDatabase("url", sqldb.DatabaseConfig{ 71 Migrations: "./migrations", 72 }) 73 ``` Project url migrations 1\_create\_tables.up.sql url.go url\_test.go Encore encore.app go.mod Open on ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Create a service and endpoint](https://encore.dev/docs/go/tutorials/rest-api\#1-create-a-service-and-endpoint) If you haven't already, create a new application by running `encore app create` and select `Empty app` as the template. If this is the first time you're using Encore, you'll be asked if you wish to create a free account. This is needed when you want Encore to manage functionality like secrets and handle cloud deployments (which we'll use later on in the tutorial). Now let's create a new `url` service. πŸ₯ In your application's root folder, create a new folder `url` and create a new file `url.go` that looks like this: ```go url/url.go // Service url takes URLs, generates random short IDs, and stores the URLs in a database. package url import ( "context" "crypto/rand" "encoding/base64" ) type URL struct { ID string // short-form URL id URL string // complete URL, in long form } type ShortenParams struct { URL string // the URL to shorten } // Shorten shortens a URL. //encore:api public method=POST path=/url func Shorten(ctx context.Context, p *ShortenParams) (*URL, error) { id, err := generateID() if err != nil { return nil, err } return &URL{ID: id, URL: p.URL}, nil } // generateID generates a random short ID. func generateID() (string, error) { var data [6]byte // 6 bytes of entropy if _, err := rand.Read(data[:]); err != nil { return "", err } return base64.RawURLEncoding.EncodeToString(data[:]), nil } ``` This sets up the `POST /url` endpoint (see the `//encore:api` annotation on the `Shorten` function). πŸ₯ Let’s see if it works! Start your app by running `encore run`. You should see this: ```output Your API is running at: http://localhost:4000 Development Dashboard URL: http://localhost:9400 4:19PM TRC registered endpoint path=/url service=url endpoint=Shorten ``` πŸ₯ Next, call your endpoint from the Local Development Dashboard at [http://localhost:9400](http://localhost:9400/) and view a trace of the response. It should look like this: You can also call it from the terminal: ```bash $ curl http://localhost:4000/url -d '{"URL": "https://encore.dev"}' ``` And you should see this: ```json { "ID": "5cJpBVRp", "URL": "https://encore.dev" } ``` It works! There’s just one problem... Right now, we’re not actually storing the URL anywhere. That means we can generate shortened IDs but there’s no way to get back to the original URL! We need to store a mapping from the short ID to the complete URL. ## [2\. Save URLs in a database](https://encore.dev/docs/go/tutorials/rest-api\#2-save-urls-in-a-database) Fortunately, Encore makes it really easy to set up a PostgreSQL database to store our data. To do so, we first define a **database schema**, in the form of a migration file. πŸ₯ Create a new folder named `migrations` inside the `url` folder. Then, inside the `migrations` folder, create an initial database migration file named `1_create_tables.up.sql`. The file name format is important (it must start with `1_` and end in `.up.sql`). πŸ₯ Add the following contents to the file: ```sql url/migrations/1_create_tables.up.sql CREATE TABLE url ( id TEXT PRIMARY KEY, original_url TEXT NOT NULL ); ``` πŸ₯ Next, go back to the `url/url.go` file and import the `encore.dev/storage/sqldb` package by modifying the import statement to become: ```go url/url.go import ( "context" "crypto/rand" "encoding/base64" "encore.dev/storage/sqldb" ) ``` πŸ₯ Then let's define our database object by adding the following to `url/url.go`: ```go url/url.go // Define a database named 'url', using the database // migrations in the "./migrations" folder. var db = sqldb.NewDatabase("url", sqldb.DatabaseConfig{ Migrations: "./migrations", }) ``` πŸ₯ Now, to insert data into our database, let’s create a helper function `insert`: ```go url/url.go // insert inserts a URL into the database. func insert(ctx context.Context, id, url string) error { _, err := db.Exec(ctx, ` INSERT INTO url (id, original_url) VALUES ($1, $2) `, id, url) return err } ``` πŸ₯ Lastly, we can update our `Shorten` function to insert into the database: ```go url/url.go func Shorten(ctx context.Context, p *ShortenParams) (*URL, error) { id, err := generateID() if err != nil { return nil, err } else if err := insert(ctx, id, p.URL); err != nil { return nil, err } return &URL{ID: id, URL: p.URL}, nil } ``` ##### Please note Before running your application, make sure you have [Docker](https://www.docker.com/) installed and running. It's required to locally run Encore applications with databases. πŸ₯ Next, start the application again with `encore run` and Encore automatically sets up your database. (In case your application won't run, check the [databases troubleshooting guide](https://encore.dev/docs/develop/databases#troubleshooting).) You can verify that the database was created by opening the **Infra** tab in the local development dashboard at [localhost:9400](http://localhost:9400/), which should something like this: ![Infra tab in local development dashboard](https://encore.dev/assets/docs/infra_tab.png) πŸ₯ Now let's call the API again from the local development dashboard, or from the terminal: ```bash $ curl http://localhost:4000/url -d '{"URL": "https://encore.dev"}' ``` πŸ₯ Finally, let's verify that it was saved in the database. You can do this by checking the trace in the local development dashboard, or you can run `encore db shell url` from the app root directory and inputting `select * from url;`: ```bash $ encore db shell url psql (13.1, server 11.12) Type "help" for help. url=# select * from url; id | original_url ----------+-------------------- zr6RmZc4 | https://encore.dev (1 row) ``` That was easy! ## [3\. Add endpoint to retrieve URLs](https://encore.dev/docs/go/tutorials/rest-api\#3-add-endpoint-to-retrieve-urls) To complete our URL shortener API, let’s add the endpoint to retrieve a URL given its short id. πŸ₯ Add this endpoint to `url/url.go`: ```go url/url.go // Get retrieves the original URL for the id. //encore:api public method=GET path=/url/:id func Get(ctx context.Context, id string) (*URL, error) { u := &URL{ID: id} err := db.QueryRow(ctx, ` SELECT original_url FROM url WHERE id = $1 `, id).Scan(&u.URL) return u, err } ``` Encore uses the `path=/url/:id` syntax to represent a path with a parameter. The `id` name corresponds to the parameter name in the function signature. In this case it is of type `string`, but you can also use other built-in types like `int` or `bool` if you want to restrict the values. πŸ₯ We can make sure it works by reviewing the endpoint in the Service Catalog in the local development dashboard, where we can call it using the `id` you got in the previous step: You can also call it directly from the terminal: ```bash $ curl http://localhost:4000/url/zr6RmZc4 ``` You should now see this: ```json { "ID": "zr6RmZc4", "URL": "https://encore.dev" } ``` It works! That's how you build REST APIs and use PostgreSQL databases in Encore. ## [4\. Add a test](https://encore.dev/docs/go/tutorials/rest-api\#4-add-a-test) Before deployment, it is good practice to have tests to assure that the service works properly. Such tests including database access are easy to write. We've prepared a test to check that the whole cycle of shortening the URL, storing and then retrieving the original URL works. πŸ₯ Save this in a separate file `url/url_test.go`. ```go url/url_test.go package url import ( "context" "testing" ) // TestShortenAndRetrieve - test that the shortened URL is stored and retrieved from database. func TestShortenAndRetrieve(t *testing.T) { testURL := "https://github.com/encoredev/encore" sp := ShortenParams{URL: testURL} resp, err := Shorten(context.Background(), &sp) if err != nil { t.Fatal(err) } wantURL := testURL if resp.URL != wantURL { t.Errorf("got %q, want %q", resp.URL, wantURL) } firstURL := resp gotURL, err := Get(context.Background(), firstURL.ID) if err != nil { t.Fatal(err) } if *gotURL != *firstURL { t.Errorf("got %v, want %v", *gotURL, *firstURL) } } ``` πŸ₯ Now run `encore test ./...` to verify that it's working. If you use the local development dashboard ( [localhost:9400](http://localhost:9400/)), you can even see traces for tests. ## [5\. Deploy](https://encore.dev/docs/go/tutorials/rest-api\#5-deploy) ### [Self-hosting](https://encore.dev/docs/go/tutorials/rest-api\#self-hosting) Encore supports building Docker images directly from the CLI, which can then be self-hosted on your own infrastructure of choice. If your app is using infrastructure resources, such as SQL databases, Pub/Sub, or metrics, you will need to configure your Docker image with the necessary configuration. Our URL shortener makes use of a PostgreSQL database, so we'll need to supply a [runtime configuration](https://encore.dev/docs/go/self-host/configure-infra) so that our app knows how to connect to the database in the cloud. πŸ₯ Create a new file `infra-config.json` in the root of your project with the following contents: ```json { "$schema": "https://encore.dev/schemas/infra.schema.json", "sql_servers": [\ {\ "host": "my-db-host:5432",\ "databases": {\ "url": {\ "username": "my-db-owner",\ "password": {"$env": "DB_PASSWORD"}\ }\ }\ }\ ] } ``` The values in this configuration are just examples, you will need to replace them with the correct values for your database. πŸ₯ Build a Docker image by running `encore build docker url-shortener:v1.0`. This will compile your application using the host machine and then produce a Docker image containing the compiled application. πŸ₯ Upload the Docker image to the cloud provider of your choice and run it. ### [Encore Cloud (free)](https://encore.dev/docs/go/tutorials/rest-api\#encore-cloud-free) Encore Cloud provides automated infrastructure and DevOps. Deploy to a free development environment or to your own cloud account on AWS or GCP. ### [Create account](https://encore.dev/docs/go/tutorials/rest-api\#create-account) Before deploying with Encore Cloud, you need to have a free Encore Cloud account and link your app to the platform. If you already have an account, you can move on to the next step. If you don’t have an account, the simplest way to get set up is by running `encore app create` and selecting **Y** when prompted to create a new account. Once your account is set up, continue creating a new app, selecting the `empty app` template. After creating the app, copy your project files into the new app directory, ensuring that you do not replace the `encore.app` file (this file holds a unique id which links your app to the platform). ### [Commit changes](https://encore.dev/docs/go/tutorials/rest-api\#commit-changes) The final step before you deploy is to commit all changes to the project repo. πŸ₯ 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 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From there you can also see metrics, traces, and connect your own AWS or GCP account to use for production deployment. _Now you have a fully fledged backend running in the cloud, well done!_ ### [Celebrate with fireworks](https://encore.dev/docs/go/tutorials/rest-api\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) πŸ₯ A great next step is to [integrate with GitHub](https://encore.dev/docs/platform/integrations/github). Once you've linked with GitHub, Encore will automatically start building and running tests against your Pull Requests. ## [What's next](https://encore.dev/docs/go/tutorials/rest-api\#whats-next) Now that you know how to build a backend with a database, you're ready to let your creativity flow and begin building your next great idea! We're excited to hear what you're going to build with Encore, join the pioneering developer community on [Discord](https://encore.dev/discord) and share your story. [Application Model](https://encore.dev/docs/go/concepts/application-model) [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/tutorials/rest-api.mdx) ## Uptime Monitor Tutorial Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Want to be notified when your website goes down so you can fix it before your users notice? You need an uptime monitoring system. Sounds daunting? Don't worry, we'll build it with Encore in 30 minutes! The app will use an event-driven architecture and the final result will look like this: ![](https://encore.dev/assets/tutorials/uptime/frontend.png) ```go 1 // Service monitor checks if a website is up or down and publishes UP/DOWN notifications via Pub/Sub. 2 package monitor 3 4 import ( 5 "context" 6 "errors" 7 8 "encore.app/site" 9 "golang.org/x/sync/errgroup" 10 11 "encore.dev/cron" 12 "encore.dev/pubsub" 13 "encore.dev/storage/sqldb" 14 ) 15 16 // Check checks a single site. 17 // 18 //encore:api public method=POST path=/check/:siteID 19 func Check(ctx context.Context, siteID int) error { 20 site, err := site.Get(ctx, siteID) 21 if err != nil { 22 return err 23 } 24 return check(ctx, site) 25 } 26 27 // CheckAll checks all sites. 28 // 29 //encore:api public method=POST path=/check-all 30 func CheckAll(ctx context.Context) error { 31 // Get all the tracked sites. 32 resp, err := site.List(ctx) 33 if err != nil { 34 return err 35 } 36 37 // Check up to 8 sites concurrently. 38 g, ctx := errgroup.WithContext(ctx) 39 g.SetLimit(8) 40 for _, site := range resp.Sites { 41 site := site // capture for closure 42 g.Go(func() error { 43 return check(ctx, site) 44 }) 45 } 46 return g.Wait() 47 } 48 49 func check(ctx context.Context, site *site.Site) error { 50 result, err := Ping(ctx, site.URL) 51 if err != nil { 52 return err 53 } 54 55 if err := publishOnTransition(ctx, site, result.Up); err != nil { 56 return err 57 } 58 59 _, err = db.Exec(ctx, ` 60 INSERT INTO checks (site_id, up, checked_at) 61 VALUES ($1, $2, NOW()) 62 `, site.ID, result.Up) 63 return err 64 } 65 66 func publishOnTransition(ctx context.Context, site *site.Site, isUp bool) error { 67 wasUp, err := getPreviousMeasurement(ctx, site.ID) 68 if err != nil { 69 return err 70 } 71 if isUp == wasUp { 72 // Nothing to do 73 return nil 74 } 75 _, err = TransitionTopic.Publish(ctx, &TransitionEvent{ 76 Site: site, 77 Up: isUp, 78 }) 79 return err 80 } 81 82 // getPreviousMeasurement reports whether the given site was 83 // up or down in the previous measurement. 84 func getPreviousMeasurement(ctx context.Context, siteID int) (up bool, err error) { 85 err = db.QueryRow(ctx, ` 86 SELECT up FROM checks 87 WHERE site_id = $1 88 ORDER BY checked_at DESC 89 LIMIT 1 90 `, siteID).Scan(&up) 91 92 if errors.Is(err, sqldb.ErrNoRows) { 93 // There was no previous ping; treat this as if the site was up before 94 return true, nil 95 } else if err != nil { 96 return false, err 97 } 98 return up, nil 99 } 100 101 // Defines a Cron Job to check all tracked sites every hour. 102 // Learn more: https://encore.dev/docs/go/primitives/cron-jobs 103 104 // 'check-all' is used to check all tracked sites every hour. 105 var _ = cron.NewJob("check-all", cron.JobConfig{ 106 Title: "Check all sites", 107 Endpoint: CheckAll, 108 Every: 1 * cron.Hour, 109 }) 110 111 // TransitionEvent describes a transition of a monitored site 112 // from up->down or from down->up. 113 type TransitionEvent struct { 114 // Site is the monitored site in question. 115 Site *site.Site `json:"site"` 116 // Up specifies whether the site is now up or down (the new value). 117 Up bool `json:"up"` 118 } 119 120 // 'uptime-transition' is a pubsub topic for events when a monitored site transitions from up->down or from down->up. 121 var TransitionTopic = pubsub.NewTopic[*TransitionEvent]("uptime-transition", pubsub.TopicConfig{ 122 DeliveryGuarantee: pubsub.AtLeastOnce, 123 }) 124 125 var _ = pubsub.NewSubscription(site.SiteAddedTopic, "check-site", pubsub.SubscriptionConfig[*site.Site]{ 126 Handler: func(ctx context.Context, s *site.Site) error { 127 return Check(ctx, s.ID) 128 }, 129 }) 130 131 // Below we define a database named 'monitor', using the database migrations 132 // in the "./migrations" folder. Encore automatically provisions, 133 // migrates, and connects to the database. 134 // Learn more: https://encore.dev/docs/go/primitives/databases 135 136 // 'monitor' database is used to store the uptime checks 137 var db = sqldb.NewDatabase("monitor", sqldb.DatabaseConfig{ 138 Migrations: "./migrations", 139 }) 140 ``` Project frontend monitor migrations 1\_create\_tables.up.sql check.go ping.go ping\_test.go status.go site migrations 1\_create\_tables.up.sql 2\_url\_unique.up.sql site.go site\_test.go slack slack.go slack\_test.go Encore encore.app go.mod Open on ## [1\. Create your Encore application](https://encore.dev/docs/go/tutorials/uptime\#1-create-your-encore-application) ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. πŸ₯ Create a new Encore application, using this tutorial project's starting-point branch. This gives you a ready-to-go frontend to use. ```bash $ encore app create uptime --example=github.com/encoredev/example-app-uptime/tree/starting-point ``` If this is the first time you're using Encore, you'll be asked if you wish to create a free account. This is needed when you want Encore to manage functionality like secrets and handle cloud deployments (which we'll use later on in the tutorial). When we're done we'll have a backend with an event-driven architecture, as seen below in the [automatically generated diagram](https://encore.dev/docs/go/observability/encore-flow) where white boxes are services and black boxes are Pub/Sub topics: ![](https://encore.dev/assets/tutorials/uptime/encore-flow.png) ## [2\. Create monitor service](https://encore.dev/docs/go/tutorials/uptime\#2-create-monitor-service) Let's start by creating the functionality to check if a website is currently up or down. Later we'll store this result in a database so we can detect when the status changes and send alerts. πŸ₯ Create an Encore service named `monitor` containing a file named `ping.go`. ```bash $ mkdir monitor $ touch monitor/ping.go ``` πŸ₯ Add an Encore API endpoint named `Ping` that takes a URL as input and returns a response indicating whether the site is up or down. ```go monitor/ping.go // Service monitor checks if a website is up or down. package monitor import ( "context" "net/http" "strings" ) // PingResponse is the response from the Ping endpoint. type PingResponse struct { Up bool `json:"up"` } // Ping pings a specific site and determines whether it's up or down right now. // //encore:api public path=/ping/*url func Ping(ctx context.Context, url string) (*PingResponse, error) { // If the url does not start with "http:" or "https:", default to "https:". if !strings.HasPrefix(url, "http:") && !strings.HasPrefix(url, "https:") { url = "https://" + url } // Make an HTTP request to check if it's up. req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } resp, err := http.DefaultClient.Do(req) if err != nil { return &PingResponse{Up: false}, nil } resp.Body.Close() // 2xx and 3xx status codes are considered up up := resp.StatusCode < 400 return &PingResponse{Up: up}, nil } ``` πŸ₯ Let's try it! Run `encore run` in your terminal and you should see the service start up. Then open up the Local Development Dashboard at [http://localhost:9400](http://localhost:9400/) and try calling the `monitor.ping` endpoint from the API Explorer, passing in `google.com` as the URL. You can then see the response, logs, and view a trace of the request. It will look something like this: If you prefer to use the terminal instead run `curl http://localhost:4000/ping/google.com` in a new terminal instead. Either way you should see the response: ```json {"up": true} ``` You can also try with `httpstat.us/400` and `some-non-existing-url.com` and it should respond with `{"up": false}`. (It's always a good idea to test the negative case as well.) ### [Add a test](https://encore.dev/docs/go/tutorials/uptime\#add-a-test) πŸ₯ Let's write an automated test so we don't break this endpoint over time. Create the file `monitor/ping_test.go` with the content: ```go monitor/ping_test.go package monitor import ( "context" "testing" ) func TestPing(t *testing.T) { ctx := context.Background() tests := []struct { URL string Up bool }{ {"encore.dev", true}, {"google.com", true}, // Test both with and without "https://" {"httpbin.org/status/200", true}, {"https://httpbin.org/status/200", true}, // 4xx and 5xx should considered down. {"httpbin.org/status/400", false}, {"https://httpbin.org/status/500", false}, // Invalid URLs should be considered down. {"invalid://scheme", false}, } for _, test := range tests { resp, err := Ping(ctx, test.URL) if err != nil { t.Errorf("url %s: unexpected error: %v", test.URL, err) } else if resp.Up != test.Up { t.Errorf("url %s: got up=%v, want %v", test.URL, resp.Up, test.Up) } } } ``` πŸ₯ Run `encore test ./...` to check that it all works as expected. You should see something like: ```bash $ encore test ./... 9:38AM INF starting request endpoint=Ping service=monitor test=TestPing 9:38AM INF request completed code=ok duration=71.861792 endpoint=Ping http_code=200 service=monitor test=TestPing [... lots more lines ...] PASS ok encore.app/monitor 1.660 ``` And if you open the local development dashboard at [localhost:9400](http://localhost:9400/), you can also see traces for the tests. ## [3\. Create site service](https://encore.dev/docs/go/tutorials/uptime\#3-create-site-service) Next, we want to keep track of a list of websites to monitor. Since most of these APIs will be simple "CRUD" (Create/Read/Update/Delete) endpoints, let's build this service using [GORM](https://gorm.io/), an ORM library that makes building CRUD endpoints really simple. πŸ₯ Let's create a new service named `site` with a SQL database. To do so, create a new directory `site` in the application root with `migrations` folder inside that folder: ```bash $ mkdir site $ mkdir site/migrations ``` πŸ₯ Add a database migration file inside that folder, named `1_create_tables.up.sql`. The file name is important (it must look something like `1_.up.sql`). Add the following contents: ```sql site/migrations/1_create_tables.up.sql CREATE TABLE sites ( id BIGSERIAL PRIMARY KEY, url TEXT NOT NULL ); ``` πŸ₯ Next, install the GORM library and PostgreSQL driver: ```bash $ go get -u gorm.io/gorm gorm.io/driver/postgres ``` Now let's create the `site` service itself. To do this we'll use Encore's support for [dependency injection](https://encore.dev/docs/go/how-to/dependency-injection) to inject the GORM database connection. πŸ₯ Create `site/service.go` with the contents: ```go site/service.go // Service site keeps track of which sites to monitor. package site import ( "encore.dev/storage/sqldb" "gorm.io/driver/postgres" "gorm.io/gorm" ) //encore:service type Service struct { db *gorm.DB } // Define a database named 'site', using the database migrations // in the "./migrations" folder. Encore automatically provisions, // migrates, and connects to the database. var db = sqldb.NewDatabase("site", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // initService initializes the site service. // It is automatically called by Encore on service startup. func initService() (*Service, error) { db, err := gorm.Open(postgres.New(postgres.Config{ Conn: db.Stdlib(), })) if err != nil { return nil, err } return &Service{db: db}, nil } ``` πŸ₯ With that, we're now ready to create our CRUD endpoints. Create the following files: ```go site/get.go site/add.go site/list.go site/delete.go package site import "context" // Site describes a monitored site. type Site struct { // ID is a unique ID for the site. ID int `json:"id"` // URL is the site's URL. URL string `json:"url"` } // Get gets a site by id. // //encore:api public method=GET path=/site/:siteID func (s *Service) Get(ctx context.Context, siteID int) (*Site, error) { var site Site if err := s.db.Where("id = $1", siteID).First(&site).Error; err != nil { return nil, err } return &site, nil } ``` πŸ₯ Now make sure you have [Docker](https://docker.com/) installed and running, and then restart `encore run` to cause the `site` database to be created by Encore. You can verify that the database was created by looking at your application's Flow architecture diagram in the local development dashboard at [localhost:9400](http://localhost:9400/), and then use the Service Catalog to call the `site.Add` endpoint. Or you can call `site.Add` from the terminal: ```bash $ curl -X POST 'http://localhost:4000/site' -d '{"url": "https://encore.dev"}' { "id": 1, "url": "https://encore.dev" } ``` ## [4\. Record uptime checks](https://encore.dev/docs/go/tutorials/uptime\#4-record-uptime-checks) In order to notify when a website goes down or comes back up, we need to track the previous state it was in. πŸ₯ To do so, let's add a database to the `monitor` service as well. Create the directory `monitor/migrations` and the file `monitor/migrations/1_create_tables.up.sql`: ```sql monitor/migrations/1_create_tables.up.sql CREATE TABLE checks ( id BIGSERIAL PRIMARY KEY, site_id BIGINT NOT NULL, up BOOLEAN NOT NULL, checked_at TIMESTAMP WITH TIME ZONE NOT NULL ); ``` We'll insert a database row every time we check if a site is up. πŸ₯ Add a new endpoint `Check` to the `monitor` service, that takes in a Site ID, pings the site, and inserts a database row in the `checks` table. For this service we'll use Encore's [`sqldb` package](https://encore.dev/docs/go/primitives/databases#querying-databases) instead of GORM (in order to showcase both approaches). ```go monitor/check.go package monitor import ( "context" "encore.app/site" "encore.dev/storage/sqldb" ) // Check checks a single site. // //encore:api public method=POST path=/check/:siteID func Check(ctx context.Context, siteID int) error { site, err := site.Get(ctx, siteID) if err != nil { return err } result, err := Ping(ctx, site.URL) if err != nil { return err } _, err = db.Exec(ctx, ` INSERT INTO checks (site_id, up, checked_at) VALUES ($1, $2, NOW()) `, site.ID, result.Up) return err } // Define a database named 'monitor', using the database migrations // in the "./migrations" folder. Encore automatically provisions, // migrates, and connects to the database. var db = sqldb.NewDatabase("monitor", sqldb.DatabaseConfig{ Migrations: "./migrations", }) ``` πŸ₯ Restart `encore run` to cause the `monitor` database to be created. We can again verify that the database was created in the Flow diagram, and also see the dependency between the `monitor` service and the `site` service that we just added. We can then call the `monitor.Check` endpoint using the id `1` that we got in the last step, and view the trace where we see the database interactions. It will look something like this: πŸ₯ You can also inspect the database using `encore db shell ` to make sure everything worked: ```bash $ encore db shell monitor psql (14.4, server 14.2) Type "help" for help. monitor=> SELECT * FROM checks; id | site_id | up | checked_at ----+---------+----+------------------------------- 1 | 1 | t | 2022-10-21 09:58:30.674265+00 ``` If that's what you see, everything's working great! ### [Add a cron job to check all sites](https://encore.dev/docs/go/tutorials/uptime\#add-a-cron-job-to-check-all-sites) We now want to regularly check all the tracked sites so we can respond in case any of them go down. We'll create a new `CheckAll` API endpoint in the `monitor` service that will list all the tracked sites and check all of them. πŸ₯ Let's extract some of the functionality we wrote for the `Check` endpoint into a separate function, like so: ```go monitor/check.go // Check checks a single site. // //encore:api public method=POST path=/check/:siteID func Check(ctx context.Context, siteID int) error { site, err := site.Get(ctx, siteID) if err != nil { return err } return check(ctx, site) } func check(ctx context.Context, site *site.Site) error { result, err := Ping(ctx, site.URL) if err != nil { return err } _, err = db.Exec(ctx, ` INSERT INTO checks (site_id, up, checked_at) VALUES ($1, $2, NOW()) `, site.ID, result.Up) return err } ``` Now we're ready to create our new `CheckAll` endpoint. πŸ₯ Create the new `CheckAll` endpoint inside `monitor/check.go`: ```go monitor/check.go import "golang.org/x/sync/errgroup" // CheckAll checks all sites. // //encore:api public method=POST path=/checkall func CheckAll(ctx context.Context) error { // Get all the tracked sites. resp, err := site.List(ctx) if err != nil { return err } // Check up to 8 sites concurrently. g, ctx := errgroup.WithContext(ctx) g.SetLimit(8) for _, site := range resp.Sites { site := site // capture for closure g.Go(func() error { return check(ctx, site) }) } return g.Wait() } ``` This uses [an errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) to check up to 8 sites concurrently, aborting early if we encounter any error. (Note that a website being down is not treated as an error.) πŸ₯ Run `go get golang.org/x/sync/errgroup` to install that dependency. πŸ₯ Now that we have a `CheckAll` endpoint, define a [cron job](https://encore.dev/docs/go/primitives/cron-jobs) to automatically call it every 1 hour (since this is an example, we don't need to go too crazy and check every minute): ```go monitor/check.go import "encore.dev/cron" // Check all tracked sites every 1 hour. var _ = cron.NewJob("check-all", cron.JobConfig{ Title: "Check all sites", Endpoint: CheckAll, Every: 1 * cron.Hour, }) ``` ##### Please note Cron jobs are not triggered when running the application locally but work when deploying the application to a cloud environment. The frontend needs a way to list all sites and display if they are up or down. πŸ₯ Add a file in the `monitor` service and name it `status.go`. Add the following code: ```go monitor/status.go package monitor import ( "context" "time" ) // SiteStatus describes the current status of a site // and when it was last checked. type SiteStatus struct { Up bool `json:"up"` CheckedAt time.Time `json:"checked_at"` } // StatusResponse is the response type from the Status endpoint. type StatusResponse struct { // Sites contains the current status of all sites, // keyed by the site ID. Sites map[int]SiteStatus `json:"sites"` } // Status checks the current up/down status of all monitored sites. // //encore:api public method=GET path=/status func Status(ctx context.Context) (*StatusResponse, error) { rows, err := db.Query(ctx, ` SELECT DISTINCT ON (site_id) site_id, up, checked_at FROM checks ORDER BY site_id, checked_at DESC `) if err != nil { return nil, err } defer rows.Close() result := make(map[int]SiteStatus) for rows.Next() { var siteID int var status SiteStatus if err := rows.Scan(&siteID, &status.Up, &status.CheckedAt); err != nil { return nil, err } result[siteID] = status } if err := rows.Err(); err != nil { return nil, err } return &StatusResponse{Sites: result}, nil } ``` Now try visiting http://localhost:4000/frontend in your browser again. This time you should see a working frontend that lists all sites and their current status. ## [5\. Deploy](https://encore.dev/docs/go/tutorials/uptime\#5-deploy) To try out your uptime monitor for real, let's deploy it to the cloud. ### [Self-hosting](https://encore.dev/docs/go/tutorials/uptime\#self-hosting) Encore supports building Docker images directly from the CLI, which can then be self-hosted on your own infrastructure of choice. If your app is using infrastructure resources, such as SQL databases, Pub/Sub, or metrics, you will need to supply a [runtime configuration](https://encore.dev/docs/go/self-host/configure-infra) your Docker image. πŸ₯ Create a new file `infra-config.json` in the root of your project with the following contents: ```json { "$schema": "https://encore.dev/schemas/infra.schema.json", "sql_servers": [\ {\ "host": "my-db-host:5432",\ "databases": {\ "monitor": {\ "username": "my-db-owner",\ "password": {"$env": "DB_PASSWORD"}\ },\ "site": {\ "username": "my-db-owner",\ "password": {"$env": "DB_PASSWORD"}\ }\ }\ }\ ] } ``` The values in this configuration are just examples, you will need to replace them with the correct values for your database. πŸ₯ Build a Docker image by running `encore build docker uptime:v1.0`. This will compile your application using the host machine and then produce a Docker image containing the compiled application. πŸ₯ Upload the Docker image to the cloud provider of your choice and run it. ### [Encore Cloud (free)](https://encore.dev/docs/go/tutorials/uptime\#encore-cloud-free) Encore Cloud provides automated infrastructure and DevOps. Deploy to a free development environment or to your own cloud account on AWS or GCP. ### [Create account](https://encore.dev/docs/go/tutorials/uptime\#create-account) Before deploying with Encore Cloud, you need to have a free Encore Cloud account and link your app to the platform. If you already have an account, you can move on to the next step. If you don’t have an account, the simplest way to get set up is by running `encore app create` and selecting **Y** when prompted to create a new account. Once your account is set up, continue creating a new app, selecting the `empty app` template. After creating the app, copy your project files into the new app directory, ensuring that you do not replace the `encore.app` file (this file holds a unique id which links your app to the platform). ### [Commit changes](https://encore.dev/docs/go/tutorials/uptime\#commit-changes) Encore comes with built-in CI/CD, and the deployment process is as simple as a `git push`. (You can also integrate with GitHub to activate per Pull Request Preview Environments, learn more in the [CI/CD docs](https://encore.dev/docs/platform/deploy/deploying).) πŸ₯ Now, let's deploy your app to Encore's free development cloud by running: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From the Cloud Dashboard you can also see metrics, trigger Cron Jobs, see traces, and later connect your own AWS or GCP account to use for deployment. πŸ₯ When the deploy has finished, you can try out your uptime monitor by going to `https://staging-$APP_ID.encr.app/frontend`. _You now have an Uptime Monitor running in the cloud, well done!_ ## [6\. Publish Pub/Sub events when a site goes down](https://encore.dev/docs/go/tutorials/uptime\#6-publish-pubsub-events-when-a-site-goes-down) Hold on, an uptime monitoring system isn't very useful if it doesn't actually notify you when a site goes down. To do so let's add a [Pub/Sub topic](https://encore.dev/docs/go/primitives/pubsub) on which we'll publish a message every time a site transitions from being up to being down, or vice versa. πŸ₯ Define the topic using Encore's Pub/Sub package in a new file, `monitor/alerts.go`: ```go monitor/alerts.go package monitor import "encore.dev/pubsub" // TransitionEvent describes a transition of a monitored site // from up->down or from down->up. type TransitionEvent struct { // Site is the monitored site in question. Site *site.Site `json:"site"` // Up specifies whether the site is now up or down (the new value). Up bool `json:"up"` } // TransitionTopic is a pubsub topic with transition events for when a monitored site // transitions from up->down or from down->up. var TransitionTopic = pubsub.NewTopic[*TransitionEvent]("uptime-transition", pubsub.TopicConfig{ DeliveryGuarantee: pubsub.AtLeastOnce, }) ``` Now let's publish a message on the `TransitionTopic` if a site's up/down state differs from the previous measurement. πŸ₯ Create a `getPreviousMeasurement` function to report the last up/down state: ```go monitor/alerts.go import ( "encore.dev/storage/sqldb" "errors" "context" ) // getPreviousMeasurement reports whether the given site was // up or down in the previous measurement. func getPreviousMeasurement(ctx context.Context, siteID int) (up bool, err error) { err = db.QueryRow(ctx, ` SELECT up FROM checks WHERE site_id = $1 ORDER BY checked_at DESC LIMIT 1 `, siteID).Scan(&up) if errors.Is(err, sqldb.ErrNoRows) { // There was no previous ping; treat this as if the site was up before return true, nil } else if err != nil { return false, err } return up, nil } ``` πŸ₯ Now add a function to conditionally publish a message if the up/down state differs: ```go monitor/alerts.go import "encore.app/site" func publishOnTransition(ctx context.Context, site *site.Site, isUp bool) error { wasUp, err := getPreviousMeasurement(ctx, site.ID) if err != nil { return err } if isUp == wasUp { // Nothing to do return nil } _, err = TransitionTopic.Publish(ctx, &TransitionEvent{ Site: site, Up: isUp, }) return err } ``` πŸ₯ Finally modify the `check` function to call this function: ```go monitor/check.go func check(ctx context.Context, site *site.Site) error { result, err := Ping(ctx, site.URL) if err != nil { return err } // Publish a Pub/Sub message if the site transitions // from up->down or from down->up. if err := publishOnTransition(ctx, site, result.Up); err != nil { return err } _, err = db.Exec(ctx, ` INSERT INTO checks (site_id, up, checked_at) VALUES ($1, $2, NOW()) `, site.ID, result.Up) return err } ``` Now the monitoring system will publish messages on the `TransitionTopic` whenever a monitored site transitions from up->down or from down->up. It doesn't know or care who actually listens to these messages. The truth is right now nobody does. So let's fix that by adding a Pub/Sub subscriber that posts these events to Slack. ## [7\. Send Slack notifications when a site goes down](https://encore.dev/docs/go/tutorials/uptime\#7-send-slack-notifications-when-a-site-goes-down) πŸ₯ Start by creating a Slack service containing the following: ```go slack/slack.go package slack import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" ) type NotifyParams struct { // Text is the Slack message text to send. Text string `json:"text"` } // Notify sends a Slack message to a pre-configured channel using a // Slack Incoming Webhook (see https://api.slack.com/messaging/webhooks). // //encore:api private func Notify(ctx context.Context, p *NotifyParams) error { reqBody, err := json.Marshal(p) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, "POST", secrets.SlackWebhookURL, bytes.NewReader(reqBody)) if err != nil { return err } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("notify slack: %s: %s", resp.Status, body) } return nil } var secrets struct { // SlackWebhookURL defines the Slack webhook URL to send // uptime notifications to. SlackWebhookURL string } ``` πŸ₯ Now go to a Slack community of your choice where you have the permission to create a new Incoming Webhook. πŸ₯ Once you have the Webhook URL, set it as an Encore secret: ```bash $ encore secret set --type dev,local,pr SlackWebhookURL Enter secret value: ***** Successfully updated development secret SlackWebhookURL. ``` πŸ₯ Test the `slack.Notify` endpoint by calling it via cURL: ```bash $ curl 'http://localhost:4000/slack.Notify' -d '{"Text": "Testing Slack webhook"}' ``` You should see the _Testing Slack webhook_ message appear in the Slack channel you designated for the webhook. πŸ₯ When it works it's time to add a Pub/Sub subscriber to automatically notify Slack when a monitored site goes up or down. Add the following: ```go slack/slack.go import ( "encore.dev/pubsub" "encore.app/monitor" ) var _ = pubsub.NewSubscription(monitor.TransitionTopic, "slack-notification", pubsub.SubscriptionConfig[*monitor.TransitionEvent]{ Handler: func(ctx context.Context, event *monitor.TransitionEvent) error { // Compose our message. msg := fmt.Sprintf("*%s is down!*", event.Site.URL) if event.Up { msg = fmt.Sprintf("*%s is back up.*", event.Site.URL) } // Send the Slack notification. return Notify(ctx, &NotifyParams{Text: msg}) }, }) ``` ## [8\. Deploy your finished Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime\#8-deploy-your-finished-uptime-monitor) Now you're ready to deploy your finished Uptime Monitor, complete with a Slack integration. ### [Self-hosting](https://encore.dev/docs/go/tutorials/uptime\#self-hosting-1) Because we have added more infrastructure to our app, we need to [update the configuration](https://encore.dev/docs/go/self-host/configure-infra) in our `infra-config.json` to include the new Pub/Sub topic and subscription as well as how we should set the `SlackWebhookURL` secret. πŸ₯ Update your `Γ¬nfra-config.json` to reflect the new infrastructure. πŸ₯ Build a Docker image by running `encore build docker uptime:v2.0`. πŸ₯ Upload the Docker image to the cloud provider and run it. ### [Encore Cloud (free)](https://encore.dev/docs/go/tutorials/uptime\#encore-cloud-free-1) πŸ₯ As before, deploying your app to the cloud is as simple as running: ```bash $ git add -A . $ git commit -m 'Add slack integration' $ git push encore ``` ### [Celebrate with fireworks](https://encore.dev/docs/go/tutorials/uptime\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) ## [Conclusion](https://encore.dev/docs/go/tutorials/uptime\#conclusion) We've now built a fully functioning uptime monitoring system. If we may say so ourselves (and we may; it's our documentation after all) it's pretty remarkable how much we've accomplished in such little code: - We've built three different services (`site`, `monitor`, and `slack`) - We've added two databases (to the `site` and `monitor` services) for tracking monitored sites and the monitoring results - We've added a cron job for automatically checking the sites every hour - We've set up a Pub/Sub topic to decouple the monitoring system from the Slack notifications - We've added a Slack integration, using secrets to securely store the webhook URL, listening to a Pub/Sub subscription for up/down transition events All of this in just a bit over 300 lines of code. It's time to lean back and take a sip of your favorite beverage, safe in the knowledge you'll never be caught unaware of a website going down suddenly. [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/tutorials/uptime.md) ## GraphQL API Tutorial Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore has great support for GraphQL with its type-safe approach to building APIs. Encore's automatic tracing also makes it easy to find and fix performance issues that often arise in GraphQL APIs (like the [N+1 problem](https://hygraph.com/blog/graphql-n-1-problem)). The best way to use GraphQL with Encore is using [gqlgen](https://gqlgen.com/), which has similar goals as Encore (type-safe APIs, minimal boilerplate, code generation, etc). The final code will look like this: ```yml 1 # Where are all the schema files located? globs are supported eg src/**/*.graphqls 2 schema: 3 - graphql/*.graphqls 4 5 # Where should the generated server code go? 6 exec: 7 filename: graphql/generated/generated.go 8 package: generated 9 10 # Uncomment to enable federation 11 # federation: 12 # filename: graph/generated/federation.go 13 # package: generated 14 15 # Where should any generated models go? 16 model: 17 filename: graphql/model/models_gen.go 18 package: model 19 20 # Where should the resolver implementations go? 21 resolver: 22 layout: follow-schema 23 dir: graphql 24 package: graphql 25 26 # Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models 27 # struct_tag: json 28 29 # Optional: turn on to use []Thing instead of []*Thing 30 # omit_slice_element_pointers: false 31 32 # Optional: turn off to make struct-type struct fields not use pointers 33 # e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing } 34 # struct_fields_always_pointers: true 35 36 # Optional: turn off to make resolvers return values instead of pointers for structs 37 # resolvers_always_return_pointers: true 38 39 # Optional: set to speed up generation time by not performing a final validation pass. 40 # skip_validation: true 41 42 # gqlgen will search for any type names in the schema in these go packages 43 # if they match it will use them, otherwise it will generate them. 44 autobind: 45 - "encore.app/url" 46 47 # This section declares type mapping between the GraphQL and go type systems 48 # 49 # The first line in each type will be used as defaults for resolver arguments and 50 # modelgen, the others will be allowed when binding to fields. Configure them to 51 # your liking 52 models: 53 ID: 54 model: 55 - github.com/99designs/gqlgen/graphql.ID 56 - github.com/99designs/gqlgen/graphql.Int 57 - github.com/99designs/gqlgen/graphql.Int64 58 - github.com/99designs/gqlgen/graphql.Int32 59 Int: 60 model: 61 - github.com/99designs/gqlgen/graphql.Int 62 - github.com/99designs/gqlgen/graphql.Int64 63 - github.com/99designs/gqlgen/graphql.Int32 64 ``` Project graphql generated generated.go model model.go graphql.go resolver.go tools.go url.graphqls url.resolvers.go url migrations 1\_create\_tables.up.sql url.go url\_test.go Encore encore.app go.mod gqlgen.yml Open on ## [1\. Create your Encore application](https://encore.dev/docs/go/tutorials/graphql\#1-create-your-encore-application) This tutorial uses the [REST API](https://encore.dev/docs/go/tutorials/rest-api) tutorial as a starting point. You can either follow that tutorial first, or you can create a new Encore application using the `url-shortener` template by running: ```bash $ encore app create --example=url-shortener ``` ## [2\. Initialize gqlgen](https://encore.dev/docs/go/tutorials/graphql\#2-initialize-gqlgen) To get started, initialize gqlgen by creating a `tools.go` file in the application root: ```go tools.go //go:build tools package tools import ( _ "github.com/99designs/gqlgen" _ "github.com/99designs/gqlgen/graphql/introspection" ) ``` Then run `go mod tidy` to download the dependencies. Next, create a `gqlgen.yml` file in the application root containing: ```yaml gqlgen.yml # Where are all the schema files located? globs are supported eg src/**/*.graphqls schema: - graphql/*.graphqls # Where should the generated server code go? exec: filename: graphql/generated/generated.go package: generated # Where should any generated models go? model: filename: graphql/model/models_gen.go package: model # Where should the resolver implementations go? resolver: layout: follow-schema dir: graphql package: graphql # gqlgen will search for any type names in the schema in these go packages # if they match it will use them, otherwise it will generate them. autobind: - "encore.app/url" # This section declares type mapping between the GraphQL and go type systems # # The first line in each type will be used as defaults for resolver arguments and # modelgen, the others will be allowed when binding to fields. Configure them to # your liking models: ID: model: - github.com/99designs/gqlgen/graphql.ID - github.com/99designs/gqlgen/graphql.Int - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 Int: model: - github.com/99designs/gqlgen/graphql.Int - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 ``` ## [3\. Create Encore service](https://encore.dev/docs/go/tutorials/graphql\#3-create-encore-service) Now it's time to create our Encore service that will provide the GraphQL API. First generate the gqlgen boilerplate: ```bash $ mkdir -p graphql/generated graphql/model $ echo "package model" > graphql/model/model.go $ go run github.com/99designs/gqlgen generate ``` This will create a bunch of files in the `graphql` directory. Next, create a `graphql/service.go` file containing: ```go graphql/service.go // Service graphql exposes a GraphQL API. package graphql import ( "net/http" "encore.app/graphql/generated" "encore.dev" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" ) //go:generate go run github.com/99designs/gqlgen generate //encore:service type Service struct { srv *handler.Server playground http.Handler } func initService() (*Service, error) { srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &Resolver{}})) pg := playground.Handler("GraphQL Playground", "/graphql") return &Service{srv: srv, playground: pg}, nil } //encore:api public raw path=/graphql func (s *Service) Query(w http.ResponseWriter, req *http.Request) { s.srv.ServeHTTP(w, req) } //encore:api public raw path=/graphql/playground func (s *Service) Playground(w http.ResponseWriter, req *http.Request) { // Disable playground in production if encore.Meta().Environment.Type == encore.EnvProduction { http.Error(w, "Playground disabled", http.StatusNotFound) return } s.playground.ServeHTTP(w, req) } ``` This creates an Encore service that exposes the `/graphql` and `/graphql/playground` endpoints. It also adds a `//go:generate` directive that lets you re-run the gqlgen code generation by running `go generate ./graphql`. ## [4\. Add GraphQL schema](https://encore.dev/docs/go/tutorials/graphql\#4-add-graphql-schema) Now it's time to define the GraphQL schema. Create a `graphql/schema.graphqls` file containing: ```graphql graphql/url.graphqls type Query { urls: [URL!]! get(id: ID!): URL! } type Mutation { shorten(input: String!): URL! } type URL { id: ID! # shortened id url: String! # full URL } ``` Then, re-run the code generation to generate the resolver stubs: ```bash $ go generate ./graphql ``` The stubs will be written to `graphql/url.resolvers.go` and will contain a bunch of unimplemented resolver methods that look something like this: ```go // Shorten is the resolver for the shorten field. func (r *mutationResolver) Shorten(ctx context.Context, input string) (*url.URL, error) { panic(fmt.Errorf("not implemented: Shorten - shorten")) } ``` ## [5\. Implement resolvers](https://encore.dev/docs/go/tutorials/graphql\#5-implement-resolvers) Now, modify the resolvers to call the `url` service. Since the GraphQL API uses the same types (thanks to the `autobind` directive in `gqlgen.yml`) as the Encore API exposes, we can just call the endpoints directly. Implement the resolvers in `graphql/url.resolvers.go` like this: ```go graphql/url.resolvers.go // Shorten is the resolver for the shorten field. func (r *mutationResolver) Shorten(ctx context.Context, input string) (*url.URL, error) { return url.Shorten(ctx, &url.ShortenParams{URL: input}) } // Urls is the resolver for the urls field. func (r *queryResolver) Urls(ctx context.Context) ([]*url.URL, error) { resp, err := url.List(ctx) if err != nil { return nil, err } return resp.URLs, nil } // Get is the resolver for the get field. func (r *queryResolver) Get(ctx context.Context, id string) (*url.URL, error) { return url.Get(ctx, id) } ``` As you can see, the resolvers are just thin wrappers around the Encore API endpoints themselves. ## [6\. Trying it out](https://encore.dev/docs/go/tutorials/graphql\#6-trying-it-out) With that, the GraphQL API is done! Try it out by running `encore run` and opening up [the playground](http://localhost:4000/graphql/playground). Enter the query: ```graphql mutation { shorten(input: "https://encore.dev") { id } } ``` You should get back an id like `MnTWA8Jo`. Pass the id you got (it will be something different) to a `get` query: ```graphql query { get(id: "") { url } } ``` And you should get back `https://encore.dev`. ## [7\. Deploy](https://encore.dev/docs/go/tutorials/graphql\#7-deploy) ### [Self-hosting](https://encore.dev/docs/go/tutorials/graphql\#self-hosting) Encore supports building Docker images directly from the CLI, which can then be self-hosted on your own infrastructure of choice. If your app is using infrastructure resources, such as SQL databases, Pub/Sub, or metrics, you will need to supply a [runtime configuration](https://encore.dev/docs/go/self-host/configure-infra) your Docker image. πŸ₯ Build a Docker image by running `encore build docker graphql:v1.0`. This will compile your application using the host machine and then produce a Docker image containing the compiled application. πŸ₯ Upload the Docker image to the cloud provider of your choice and run it. ### [Deploy to Encore Cloud](https://encore.dev/docs/go/tutorials/graphql\#deploy-to-encore-cloud) Encore Cloud provides automated infrastructure and DevOps. Deploy to a free development environment or to your own cloud account on AWS or GCP. ### [Create account](https://encore.dev/docs/go/tutorials/graphql\#create-account) Before deploying with Encore Cloud, you need to have a free Encore Cloud account and link your app to the platform. If you already have an account, you can move on to the next step. If you don’t have an account, the simplest way to get set up is by running `encore app create` and selecting **Y** when prompted to create a new account. Once your account is set up, continue creating a new app, selecting the `empty app` template. After creating the app, copy your project files into the new app directory, ensuring that you do not replace the `encore.app` file (this file holds a unique id which links your app to the platform). ### [Commit changes](https://encore.dev/docs/go/tutorials/graphql\#commit-changes) The final step before you deploy is to commit all changes to the project repo. Push your changes and deploy your application to Encore's free development cloud by running: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From there you can also see metrics, traces, link your app to a GitHub repo to get automatic deploys on new commits, and connect your own AWS or GCP account to use for production deployment. ### [Celebrate with fireworks](https://encore.dev/docs/go/tutorials/graphql\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) ## [Conclusion](https://encore.dev/docs/go/tutorials/graphql\#conclusion) We've now built a GraphQL API gateway that forwards requests to the application's underlying Encore services in a type-safe way with minimal boilerplate. Note that the concepts discussed here are general and can be easily adapted to any GraphQL schema. Whenever you make a change to the schema or configuration, re-run `go generate ./graphql` to regenerate the GraphQL boilerplate. And for more information on how to use `gqlgen`, see the [gqlgen documentation](https://gqlgen.com/). [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/tutorials/graphql.mdx) ## Slack Bot Tutorial Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this tutorial you will create a Slack bot that brings the greatness of the `cowsay` utility to Slack! ![Slack Cowsay](https://encore.dev/assets/docs/cowsay.png) This is the end result: ```go 1 // Service slack implements a cowsaw Slack bot. 2 package slack 3 4 import ( 5 "crypto/hmac" 6 "crypto/sha256" 7 "encoding/hex" 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "net/url" 13 "strconv" 14 "strings" 15 "time" 16 17 "encore.dev/beta/errs" 18 ) 19 20 // This uses Encore's built-in secrets manager, learn more: https://encore.dev/docs/go/primitives/secrets 21 var secrets struct { 22 SlackSigningSecret string 23 } 24 25 // cowart is the formatting string for printing the cow art. 26 const cowart = ` 27 ________________________________________ 28 < %- 38s > 29 ---------------------------------------- 30 \ ^__^ 31 \ (oo)\_______ 32 (__)\ )\/\ 33 ||----w | 34 || || 35 ` 36 37 //encore:api public raw path=/cowsay 38 func Cowsay(w http.ResponseWriter, req *http.Request) { 39 body, err := verifyRequest(req) 40 if err != nil { 41 errs.HTTPError(w, err) 42 return 43 } 44 q, _ := url.ParseQuery(string(body)) 45 text := q.Get("text") 46 data, _ := json.Marshal(map[string]string{ 47 "response_type": "in_channel", 48 "text": fmt.Sprintf(cowart, text), 49 }) 50 w.Header().Set("Content-Type", "application/json") 51 w.WriteHeader(200) 52 w.Write(data) 53 } 54 55 // verifyRequest verifies that a request is coming from Slack. 56 func verifyRequest(req *http.Request) (body []byte, err error) { 57 eb := errs.B().Code(errs.InvalidArgument) 58 body, err = ioutil.ReadAll(req.Body) 59 if err != nil { 60 return nil, eb.Cause(err).Err() 61 } 62 63 // Compare timestamps to prevent replay attack 64 ts := req.Header.Get("X-Slack-Request-Timestamp") 65 threshold := int64(5 * 60) 66 n, _ := strconv.ParseInt(ts, 10, 64) 67 if diff := time.Now().Unix() - n; diff > threshold || diff < -threshold { 68 return body, eb.Msg("message not recent").Err() 69 } 70 71 // Compare HMAC signature 72 sig := req.Header.Get("X-Slack-Signature") 73 prefix := "v0=" 74 if !strings.HasPrefix(sig, prefix) { 75 return body, eb.Msg("invalid signature").Err() 76 } 77 gotMac, _ := hex.DecodeString(sig[len(prefix):]) 78 79 mac := hmac.New(sha256.New, []byte(secrets.SlackSigningSecret)) 80 fmt.Fprintf(mac, "v0:%s:", ts) 81 mac.Write(body) 82 expectedMac := mac.Sum(nil) 83 if !hmac.Equal(gotMac, expectedMac) { 84 return body, eb.Msg("bad mac").Err() 85 } 86 return body, nil 87 } 88 ``` Project slack slack.go Encore encore.app go.mod Open on ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Create your Encore application](https://encore.dev/docs/go/tutorials/slack-bot\#1-create-your-encore-application) πŸ₯ Create a new Encore application by running `encore app create` and select `Empty app` as the template. **Take a note of your app id, we'll need it in the next step.** ## [2\. Create a Slack app](https://encore.dev/docs/go/tutorials/slack-bot\#2-create-a-slack-app) πŸ₯ The first step is to create a new Slack app: 1. Head over to [Slack's API site](https://api.slack.com/apps) and create a new app. 2. When prompted, choose to create the app **from an app manifest**. 3. Choose a workspace to install the app in. πŸ₯ Enter the following manifest (replace `$APP_ID` in the URL below with your app id from above): ```yaml _metadata: major_version: 1 display_information: name: Encore Bot description: Cowsay for the cloud age. features: slash_commands: - command: /cowsay # Replace $APP_ID below url: https://staging-$APP_ID.encr.app/cowsay description: Say things with a flair! usage_hint: your message here should_escape: false bot_user: display_name: encore-bot always_online: true oauth_config: scopes: bot: - commands - chat:write - chat:write.public settings: org_deploy_enabled: false socket_mode_enabled: false token_rotation_enabled: false ``` Once created, we're ready to move on with implementing our Encore endpoint! ## [3\. Implement the Slack endpoint](https://encore.dev/docs/go/tutorials/slack-bot\#3-implement-the-slack-endpoint) Since Slack sends custom HTTP headers that we need to pay attention to, we're going to use a raw endpoint in Encore. For more information on this check out Slack's documentation on [Enabling interactivity with Slash Commands](https://api.slack.com/interactivity/slash-commands). πŸ₯ In your Encore app, create a new directory named `slack` and create a file `slack/slack.go` with the following contents: ```go slack/slack.go // Service slack implements a cowsaw Slack bot. package slack import ( "encoding/json" "fmt" "net/http" ) // cowart is the formatting string for printing the cow art. const cowart = "Moo! %s" //encore:api public raw path=/cowsay func Cowsay(w http.ResponseWriter, req *http.Request) { text := req.FormValue("text") data, _ := json.Marshal(map[string]string{ "response_type": "in_channel", "text": fmt.Sprintf(cowart, text), }) w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) w.Write(data) } ``` Let's try it out locally. πŸ₯ Start your app with `encore run` and then call it in another terminal: ```bash $ curl http://localhost:4000/cowsay -d 'text=Eat your greens!' {"response_type":"in_channel","text":"Moo! Eat your greens!"} ``` Looks great! πŸ₯ Next, let's deploy it to the cloud: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Once deployed, we're ready to try our Slack command! πŸ₯ Head over to the workspace you installed the app in and run `/cowsay Hello there`. You should see something like this: ![Cowsay](https://encore.dev/assets/docs/cowsay-wip.png) And just like that we have a fully working Slack integration. ## [4\. Secure the webhook endpoint](https://encore.dev/docs/go/tutorials/slack-bot\#4-secure-the-webhook-endpoint) In order to get up and running quickly we ignored one important aspect for a production-ready Slack app: verifying that the webhook requests are actually coming from Slack. Let's do that now! The Slack documentation covers this really well on the [Verifying requests from Slack](https://api.slack.com/authentication/verifying-requests-from-slack) page. In short, what we need to do is: 1. Save a shared secret that Slack provides us 2. Use the secret to verify that the request comes from Slack, using HMAC (Hash-based Message Authentication Code). ### [Save the shared secret](https://encore.dev/docs/go/tutorials/slack-bot\#save-the-shared-secret) Let's define a secret using Encore's secrets management functionality. πŸ₯ Add this to your `slack.go` file: ```go slack/slack.go var secrets struct { SlackSigningSecret string } ``` πŸ₯ Head over to the configuration section for your Slack app (go to [Your Apps](https://api.slack.com/apps) β†’ select your app β†’ Basic Information). πŸ₯ Copy the **Signing Secret** and then run `encore secret set --type prod SlackSigningSecret` and paste the secret. πŸ₯ For development you will also want to set `encore secret set --type dev,local,pr SlackSigningSecret`. You can use the same secret value or a placeholder value. ### [Compute the HMAC](https://encore.dev/docs/go/tutorials/slack-bot\#compute-the-hmac) Go makes computing HMAC very straightforward, but it's still a fair amount of code. πŸ₯ Add a few more imports to your file, so that it reads: ```go slack/slack.go import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strconv" "strings" "time" "encore.dev/beta/errs" "encore.dev/rlog" ) ``` πŸ₯ Next, we'll add the `verifyRequest` function: ```go slack/slack.go // verifyRequest verifies that a request is coming from Slack. func verifyRequest(req *http.Request) (body []byte, err error) { eb := errs.B().Code(errs.InvalidArgument) body, err = ioutil.ReadAll(req.Body) if err != nil { return nil, eb.Cause(err).Err() } // Compare timestamps to prevent replay attack ts := req.Header.Get("X-Slack-Request-Timestamp") threshold := int64(5 * 60) n, _ := strconv.ParseInt(ts, 10, 64) if diff := time.Now().Unix() - n; diff > threshold || diff < -threshold { return body, eb.Msg("message not recent").Err() } // Compare HMAC signature sig := req.Header.Get("X-Slack-Signature") prefix := "v0=" if !strings.HasPrefix(sig, prefix) { return body, eb.Msg("invalid signature").Err() } gotMac, _ := hex.DecodeString(sig[len(prefix):]) mac := hmac.New(sha256.New, []byte(secrets.SlackSigningSecret)) fmt.Fprintf(mac, "v0:%s:", ts) mac.Write(body) expectedMac := mac.Sum(nil) if !hmac.Equal(gotMac, expectedMac) { return body, eb.Msg("bad mac").Err() } return body, nil } ``` ##### Please note As you can see, this function needs to consume the whole HTTP body in order to compute the HMAC. This breaks the use of `req.FormValue("text")` that we used earlier, since it relies on reading the HTTP body. That's the reason we're returning the body from `verifyRequest`, so that we can parse the form values from that directly instead. We're now ready to verify the signature. πŸ₯ Update the `Cowsay` function to look like this: ```go slack/slack.go //encore:api public raw path=/cowsay func Cowsay(w http.ResponseWriter, req *http.Request) { body, err := verifyRequest(req) if err != nil { errs.HTTPError(w, err) return } q, _ := url.ParseQuery(string(body)) text := q.Get("text") data, _ := json.Marshal(map[string]string{ "response_type": "in_channel", "text": fmt.Sprintf(cowart, text), }) w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) w.Write(data) } ``` ## [5\. Put it all together and deploy](https://encore.dev/docs/go/tutorials/slack-bot\#5-put-it-all-together-and-deploy) Finally we're ready to put it all together. πŸ₯ Add the `cowart` like so: ```go slack/slack.go const cowart = ` ________________________________________ < %- 38s > ---------------------------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ` ``` πŸ₯ Finally, let's commit our changes and deploy it: ```bash $ git add -A . $ git commit -m 'Verify webhook requests and improve art' $ git push encore ``` πŸ₯ Once deployed, head back to Slack and run `/cowsay Hello there`. If everything is set up correctly, you should see: ![Slack Cowsay](https://encore.dev/assets/docs/cowsay.png) And there we go, a production-ready Slack bot in less than 100 lines of code. Well done! ### [Celebrate with fireworks](https://encore.dev/docs/go/tutorials/slack-bot\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/tutorials/slack-bot.md) ## Meeting Notes App Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this tutorial, we will create a backend in less than 100 lines of code. The backend will: - Store data in a cloud SQL database - Make API calls to a third-party service - Deploy to the cloud and be publicly available The example app we will build is a markdown meeting notes app BUT it’s trivial to replace the specifics if you have another idea in mind (again, less than 100 lines of code). **[Demo version of the app](https://encoredev.github.io/meeting-notes)** This is the end result: ```go 1 package note 2 3 import ( 4 "context" 5 "encore.dev/storage/sqldb" 6 ) 7 8 // Type that represents a note. 9 type Note struct { 10 ID string `json:"id"` 11 Text string `json:"text"` 12 CoverURL string `json:"cover_url"` 13 } 14 15 //encore:api public method=POST path=/note 16 func SaveNote(ctx context.Context, note *Note) (*Note, error) { 17 // Save the note to the database. 18 // If the note already exists (i.e. CONFLICT), we update the notes text and the cover URL. 19 _, err := sqldb.Exec(ctx, ` 20 INSERT INTO note (id, text, cover_url) VALUES ($1, $2, $3) 21 ON CONFLICT (id) DO UPDATE SET text=$2, cover_url=$3 22 `, note.ID, note.Text, note.CoverURL) 23 24 // If there was an error saving to the database, then we return that error. 25 if err != nil { 26 return nil, err 27 } 28 29 // Otherwise, we return the note to indicate that the save was successful. 30 return note, nil 31 } 32 33 //encore:api public method=GET path=/note/:id 34 func GetNote(ctx context.Context, id string) (*Note, error) { 35 note := &Note{ID: id} 36 37 // We use the note ID to query the database for the note's text and cover URL. 38 err := sqldb.QueryRow(ctx, ` 39 SELECT text, cover_url FROM note 40 WHERE id = $1 41 `, id).Scan(¬e.Text, ¬e.CoverURL) 42 43 // If the note doesn't exist, we return an error. 44 if err != nil { 45 return nil, err 46 } 47 48 // Otherwise, we return the note. 49 return note, nil 50 } 51 ``` Project .github frontend note migrations 1\_create\_tables.up.sql note.go pexels pexels.go Encore encore.app go.mod Open on ## [Create your Encore application](https://encore.dev/docs/go/tutorials/meeting-notes\#create-your-encore-application) Create a new app from the meeting-notes example. This will start you off with everything described in this tutorial: ```bash $ encore app create my-app --example=meeting-notes ``` ##### Please note Before running the project locally, make sure you have [Docker](https://www.docker.com/products/docker-desktop/) installed and running. Docker is needed for Encore to create databases for locally running projects. Also, if you want to try the photo search functionality then you will need an API key from [pexels.com/api/](https://www.pexels.com/api/) (more on that below) To run the backend locally: ```bash $ cd you-app-name # replace with the app name you picked $ encore run ``` You should see the following: That means your local development backend is up and running! Encore takes care of setting up all the necessary infrastructure for your application, including databases. Encore also starts the local development dashboard which is a tool to help you move faster when you're developing new features. To start the front-end, run the following commands in another terminal window: ```bash $ cd you-app-name/frontend $ npm install $ npm run dev ``` You can now open http://localhost:5173/example-meeting-notes/ in your browser πŸ”₯ ## [Storing and retrieving from an SQL database](https://encore.dev/docs/go/tutorials/meeting-notes\#storing-and-retrieving-from-an-sql-database) Let's take a look at the backend code. There are essentially only three files of interest, let's start by looking at `note.go`. This file contains two endpoints and one interface, all standard Go code except for a few lines specific to Encore. The `Note` type represents our data structure: ```go type Note struct { ID string `json:"id"` Text string `json:"text"` CoverURL string `json:"cover_url"` } ``` Every note will have an `ID` (uuid that is created on the frontend), `Text` (Markdown text content), and `CoverURL` (background image URL). The `SaveNote` function handles storing a meeting note: ```go //encore:api public method=POST path=/note func SaveNote(ctx context.Context, note *Note) (*Note, error) { // Save the note to the database. // If the note already exists (i.e. CONFLICT), we update the notes text and the cover URL. _, err := sqldb.Exec(ctx, ` INSERT INTO note (id, text, cover_url) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET text=$2, cover_url=$3 `, note.ID, note.Text, note.CoverURL) // If there was an error saving to the database, then we return that error. if err != nil { return nil, err } // Otherwise, we return the note to indicate that the save was successful. return note, nil } ``` The comment above the function tells Encore that this is a public endpoint that should be reachable by POST on `/note`. The second argument to the function (`Note`) is the POST body and the function returns a `Note` and an `error` (a `nil` error means a 200 response). The `GetNote` function takes care of fetching a meeting note from our database given an `id`: ```go //encore:api public method=GET path=/note/:id func GetNote(ctx context.Context, id string) (*Note, error) { note := &Note{ID: id} // We use the note ID to query the database for the note's text and cover URL. err := sqldb.QueryRow(ctx, ` SELECT text, cover_url FROM note WHERE id = $1 `, id).Scan(¬e.Text, ¬e.CoverURL) // If the note doesn't exist, we return an error. if err != nil { return nil, err } // Otherwise, we return the note. return note, nil } ``` Here we have a public GET endpoint with a dynamic path parameter which is the `id` of the meeting note to fetch. The second argument, in this case, is the dynamic path parameter, a request to this endpoint will look like `/note/123-abc` where `id` will be set to `123-abc`. Both `SaveNote` and `GetNote` makes use of a SQL database table named `note`, let's look at how that table is defined. ## [Defining a SQL database](https://encore.dev/docs/go/tutorials/meeting-notes\#defining-a-sql-database) To create a SQL database using Encore we first create a folder named `migrations` and inside that folder a migration file named `1_create_tables.up.sql`. The file name is important (it must look something like `1_name.up.sql`). Our migration file is only five lines long and looks like this: ```sql CREATE TABLE note ( id TEXT PRIMARY KEY, text TEXT, cover_url TEXT ); ``` When recognizing this file, Encore will create a `note` table with three columns `id`, `text` and `cover_url`. The `id` is the primary key, used to identify specific meeting notes. ## [Making requests to a third-party API](https://encore.dev/docs/go/tutorials/meeting-notes\#making-requests-to-a-third-party-api) Let's look at how we can use an Encore endpoint to proxy requests to a third-party service (in this example photo service [pexels.com](http://www.pexels.com/) but the idea would be the same for any other third-party API). The file `pexels.go` only has one endpoint, `SearchPhoto`: ```go //encore:api public method=GET path=/images/:query func SearchPhoto(ctx context.Context, query string) (*SearchResponse, error) { // Create a new http client to proxy the request to the Pexels API. URL := "https://api.pexels.com/v1/search?query=" + query client := &http.Client{} req, _ := http.NewRequest("GET", URL, nil) // Add authorization header to the req with the API key. req.Header.Set("Authorization", secrets.PexelsApiKey) // Make the request, and close the response body when we're done. res, err := client.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode >= 400 { return nil, fmt.Errorf("Pexels API error: %s", res.Status) } // Decode the data into the searchResponse struct. var searchResponse *SearchResponse err = json.NewDecoder(res.Body).Decode(&searchResponse) if err != nil { return nil, err } return searchResponse, nil } ``` Again a GET endpoint with a dynamic path parameter which this time represents the query text we want to send to the Pexels API. The type we use to decode the response from the Pexels API looks like this: ```go type SearchResponse struct { Photos []struct { Id int `json:"id"` Src struct { Medium string `json:"medium"` Landscape string `json:"landscape"` } `json:"src"` Alt string `json:"alt"` } `json:"photos"` } ``` We get a lot more data from Pexels but here we only pick the fields that we want to propagate to our frontend. [Pexels API](https://www.pexels.com/api/) requires an API key, as most open APIs do. The API key is added as a header to the requests (from the `SearchPhoto` function above): ```go req.Header.Set("Authorization", secrets.PexelsApiKey) ``` Here we could have hardcoded the API key but that would have made it readable for everyone with access to our repo. Instead, we made use of Encore's built-in [secrets management](https://encore.dev/docs/go/primitives/secrets). To set this secret, run the following command in your project folder and follow the prompt: ```bash encore secret set --type dev,prod,local,pr PexelsApiKey ``` ## [Creating a request client](https://encore.dev/docs/go/tutorials/meeting-notes\#creating-a-request-client) Encore is able to generate frontend [request clients](https://encore.dev/docs/go/cli/client-generation) (TypeScript or JavaScript). This means that you do not need to manually keep the request/response objects in sync on the frontend, huge time saver. To generate a client run: ```bash $ encore gen client --output=./src/client.ts --env= ``` You are going to want to run this command quite often (whenever you make a change to your endpoints) so having it as an `npm` script is a good idea: ```json { ... "scripts": { ... "generate-client:staging": "encore gen client --output=./src/client.ts --env=staging", "generate-client:local": "encore gen client --output=./src/client.ts --env=local" }, } ``` After that you are ready to use the request client in your code. Here is an example of calling the `GetNote` endpoint: ```tsx import Client, { Environment, Local } from "src/client.ts"; // Making request to locally running backend... const client = new Client(Local); // or to a specific deployed environment const client = new Client(Environment("staging")); // Calling APIs as typesafe functions 🌟 const response = await client.note.GetNote("note-uuid"); console.log(response.id); console.log(response.cover_url); console.log(response.text); ``` ## [Deploying the backend to the cloud](https://encore.dev/docs/go/tutorials/meeting-notes\#deploying-the-backend-to-the-cloud) It’s deploy time! To get your backend deployed in the cloud all you need to do is to commit your code and push it to the `encore` remote: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` When running `git push encore` you will get a link to the Encore Cloud dashboard where you can view the deploy for your app and after about a minute you have a backend running in the cloud ☁️ ## [Hosting the frontend](https://encore.dev/docs/go/tutorials/meeting-notes\#hosting-the-frontend) The frontend can be deployed to any static site hosting platform. The example project is pre-configured to deploy the frontend to [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site). Take a look at `.github/workflows/node.yml` to see the GitHub actions workflow being triggered on new commits to the repo: ```yaml name: Build and Deploy on: [push] permissions: contents: write jobs: build-and-deploy: concurrency: ci-${{ github.ref }} runs-on: ubuntu-latest defaults: run: working-directory: frontend steps: - name: Checkout πŸ›ŽοΈ uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v3 with: node-version: "16.15.1" - name: Install and Build πŸ”§ run: | npm install npm run build - name: Deploy πŸš€ uses: JamesIves/github-pages-deploy-action@v4.3.3 with: branch: gh-pages folder: frontend/dist ``` The interesting part is towards the bottom where we build the frontend code and make use of the [github-pages-deploy-action](https://github.com/JamesIves/github-pages-deploy-action) step to automatically make a new commit with the compiled frontend code to a `gh-pages` branch. **Steps to deploy to GitHub pages:** 1. Create a repo on GitHub 2. In the `vite.config.js` file, set the `base` property to the name of your repo: ```yaml base: "/my-repo-name/", ``` 1. Push your code to GitHub and wait for the GitHub actions workflow to finish. 2. Go to _Settings_ β†’ _Pages_ for your repo on GitHub and set _Branch_ to `gh-pages`. ## [Celebrate with fireworks](https://encore.dev/docs/go/tutorials/meeting-notes\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) ## [Wrapping up](https://encore.dev/docs/go/tutorials/meeting-notes\#wrapping-up) You’ve learned how to build and deploy a Go backend using Encore, store data in an SQL database, and make API calls to an external service. All of this in under 100 lines of code. [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/tutorials/meeting-notes.mdx) ## Booking System Tutorial Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this tutorial we'll build a booking system with a user facing UI (see available slots and book appointments) and an admin dashboard (manage scheduled appointments and set availability). You will learn how to: - Create API endpoints using Encore (both public and authenticated). - Working with PostgreSQL databases using [sqlc](https://sqlc.dev/) and [pgx](https://github.com/jackc/pgx). - Scrub sensitive user data from traces. - Work with dates and times in Go. - Authenticate requests using an auth handler. - Send emails using a SendGrid integration. [Demo version of the app](https://prod-booking-system-teti.encr.app/frontend) The final result will look like this: ![](https://encore.dev/assets/tutorials/booking-system/user-calendar.png)![](https://encore.dev/assets/tutorials/booking-system/admin-dashboard.png) If you want to skip ahead you can view the final project here: [https://github.com/encoredev/examples/tree/main/booking-system](https://github.com/encoredev/examples/tree/main/booking-system) ## [1\. Create your Encore application](https://encore.dev/docs/go/tutorials/booking-system\#1-create-your-encore-application) ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. Make sure you have [Docker](https://docker.com/) installed and running, it is used by Encore to run PostgreSQL databases locally. πŸ₯ Create a new Encore application, using this tutorial project's starting-point branch. This gives you a ready-to-go frontend to use. ```bash $ encore app create booking-system --example=github.com/encoredev/example-booking-system/tree/starting-point ``` πŸ₯ Check that your frontend works: ```bash $ cd booking-system $ encore run ``` Then visit [http://localhost:4000/frontend/](http://localhost:4000/frontend/) to see the frontend. It won't function yet, since we haven't yet built the backend, so let's do just that! When we're done we'll have a backend with this [architecture](https://encore.dev/docs/go/observability/encore-flow): ![](https://encore.dev/assets/tutorials/booking-system/booking-system-flow.png) ## [2\. Create booking service](https://encore.dev/docs/go/tutorials/booking-system\#2-create-booking-service) Let's start by creating the functionality to view bookable slots. With Encore you define a service by [defining one or more APIs](https://encore.dev/docs/go/primitives/defining-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/platform/infrastructure/infra) for each service. We already have a Go package named `booking`, let's turn that into an Encore service. πŸ₯ Inside the `booking` folder, create a file named `slots.go`. ```bash $ touch booking/slots.go ``` πŸ₯ Add an Encore API endpoint named `GetBookableSlots` that takes a date as input. The endpoint will return a list of bookable slots from the supplied date and six days forward (so that we can show a week view calendar in the UI). ```go booking/slots.go // Service booking keeps track of bookable slots in the calendar. package booking import ( "context" "github.com/jackc/pgx/v5/pgtype" "time" ) const DefaultBookingDuration = 1 * time.Hour type BookableSlot struct { Start time.Time `json:"start"` End time.Time `json:"end"` } type SlotsParams struct{} type SlotsResponse struct{ Slots []BookableSlot } //encore:api public method=GET path=/slots/:from func GetBookableSlots(ctx context.Context, from string) (*SlotsResponse, error) { fromDate, err := time.Parse("2006-01-02", from) if err != nil { return nil, err } const numDays = 7 var slots []BookableSlot for i := 0; i < numDays; i++ { date := fromDate.AddDate(0, 0, i) daySlots, err := bookableSlotsForDay(date) if err != nil { return nil, err } slots = append(slots, daySlots...) } return &SlotsResponse{Slots: slots}, nil } func bookableSlotsForDay(date time.Time) ([]BookableSlot, error) { // 09:00 availStartTime := pgtype.Time{ Valid: true, Microseconds: int64(9*3600) * 1e6, } // 17:00 availEndTime := pgtype.Time{ Valid: true, Microseconds: int64(17*3600) * 1e6, } availStart := date.Add(time.Duration(availStartTime.Microseconds) * time.Microsecond) availEnd := date.Add(time.Duration(availEndTime.Microseconds) * time.Microsecond) // Compute the bookable slots in this day, based on availability. var slots []BookableSlot start := availStart for { end := start.Add(DefaultBookingDuration) if end.After(availEnd) { break } slots = append(slots, BookableSlot{ Start: start, End: end, }) start = end } return slots, nil } ``` The availability is currently hardcoded to be 09:00 - 17:00 for each day. Later we'll add the functionality to set it for each day of the week. We are also returning time slots that have already passed. Don't worry, we'll come back and fix it later on. πŸ₯ Let's try it! Open up the Local Development Dashboard running at [http://localhost:9400](http://localhost:9400/) and try calling the `booking.GetBookableSlots` endpoint, passing in `2024-12-01`. If you prefer to use the terminal instead run `curl http://localhost:4000/slots/2024-12-01` in a new terminal instead. Either way you should see the response: ```json { "Slots": [\ {\ "start": "2024-12-01T09:00:00Z",\ "end": "2024-12-01T10:00:00Z"\ },\ {\ "start": "2024-12-01T10:00:00Z",\ "end": "2024-12-01T11:00:00Z"\ },\ {\ "start": "2024-12-01T11:00:00Z",\ "end": "2024-12-01T12:00:00Z"\ },\ ...\ ] } ``` ## [3\. Book an appointment](https://encore.dev/docs/go/tutorials/booking-system\#3-book-an-appointment) Next, we want to make it possible to book an appointment. We'll need a database to store the bookings in. Encore makes it really simple to [create and use databases](https://encore.dev/docs/go/primitives/databases) (both for local and cloud environments), but for this example we will also make use of [sqlc](https://sqlc.dev/) that will compile our SQL queries into type-safe Go code that we can use in our application. πŸ₯ Let's create a SQL database for our booking service and the required sqlc scaffolding. Create the following file structure: ```go /my-app └── booking // booking service (a Go package) β”œβ”€β”€ db // (New) db related files (directory) β”‚ β”œβ”€β”€ migrations // (New) db migrations (directory) β”‚ β”‚ └── 1_create_tables.up.sql // (New) db migration schema β”‚ └── query.sql // (New) SQL queries β”œβ”€β”€ sqlc.yaml // (New) sqlc config file β”œβ”€β”€ slots.go // booking service code └── helpers.go // booking service code ``` πŸ₯ Naming of the database migration file is important, it must look something like: `1_.up.sql`. Add the following contents to the migration file: ```sql booking/db/migrations/1_create_tables.up.sql CREATE TABLE booking ( id BIGSERIAL PRIMARY KEY, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP NOT NULL, email TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW() ); ``` πŸ₯ Next, install the sqlc library: ```bash $ go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest ``` πŸ₯ Next, we need to configure sqlc. Add the following contents to `sqlc.yaml`: ```yaml booking/sqlc.yaml version: "2" sql: - engine: "postgresql" queries: "db/query.sql" schema: "./db/migrations" gen: go: package: "db" out: "db" sql_package: "pgx/v5" ``` This instructs sqlc to generate Go code from the queries in `db/query.sql` and models from the schemas in the `db/migrations` folder. πŸ₯ Let's create our first SQL queries. Add the following contents to `db/query.sql`: ```sql -- name: InsertBooking :one INSERT INTO booking (start_time, end_time, email) VALUES ($1, $2, $3) RETURNING *; -- name: ListBookingsBetween :many SELECT * FROM booking WHERE start_time >= $1 AND end_time <= $2; -- name: ListBookings :many SELECT * FROM booking; -- name: DeleteBooking :exec DELETE FROM booking WHERE id = $1; ``` πŸ₯ It's time for sqlc to shine! Run the following command in your terminal: ```bash $ cd booking $ sqlc generate ``` Three files should now have been generated inside the `db` folder: `query.sql.go`, `db.go` and `models.go`. These files contain generated Go code and should not be manually edited. We will be adding more queries to `db/query.sql` later and then re-run `sqlc generate` to update the generated Go code. Now let's create an endpoint that makes use of one of these queries. πŸ₯ Create `booking/booking.go` with the contents: ```go booking/booking.go package booking import ( "context" "time" "encore.app/booking/db" "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" "encore.dev/beta/errs" "encore.dev/storage/sqldb" ) var ( bookingDB = sqldb.NewDatabase("booking", sqldb.DatabaseConfig{ Migrations: "./db/migrations", }) pgxdb = sqldb.Driver[*pgxpool.Pool](bookingDB) query = db.New(pgxdb) ) type Booking struct { ID int64 `json:"id"` Start time.Time `json:"start"` End time.Time `json:"end"` Email string `encore:"sensitive"` } type BookParams struct { Start time.Time `json:"start"` Email string `encore:"sensitive"` } //encore:api public method=POST path=/booking func Book(ctx context.Context, p *BookParams) error { eb := errs.B() now := time.Now() if p.Start.Before(now) { return eb.Code(errs.InvalidArgument).Msg("start time must be in the future").Err() } tx, err := pgxdb.Begin(ctx) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to start transaction").Err() } defer tx.Rollback(context.Background()) // committed explicitly on success _, err = query.InsertBooking(ctx, db.InsertBookingParams{ StartTime: pgtype.Timestamp{Time: p.Start, Valid: true}, EndTime: pgtype.Timestamp{Time: p.Start.Add(DefaultBookingDuration), Valid: true}, Email: p.Email, }) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to insert booking").Err() } if err := tx.Commit(ctx); err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to commit transaction").Err() } return nil } ``` We are now using the generated type-safe `query.InsertBooking` function to make the database operation. Notice the `encore:"sensitive"` tag on the `Email` field. This tells Encore to scrub this field so that the data is not viewable in the traces for deployed environments. This is useful for fields that contain [sensitive data](https://encore.dev/docs/go/primitives/defining-apis#sensitive-data) such as email addresses, passwords, etc. πŸ₯ Restart `encore run` to cause the database to be created, and then call the `booking.Book` endpoint: ```bash $ curl -X POST 'http://localhost:4000/booking' -d '{"start": "2024-12-11T09:00:00Z", "email": "test@example.com"}' ``` Congratulations, you have now booked your first appointment! ## [4\. Authentication](https://encore.dev/docs/go/tutorials/booking-system\#4-authentication) To provide an admin dashboard for our booking system, we need to add authentication to our application so that we can have protected endpoints. Keep in mind, in this tutorial we'll only include a very basic implementation. πŸ₯ Let's start by creating a new service named `user`: ```bash $ mkdir user $ touch user/auth.go ``` πŸ₯ Add the following contents to `user/auth.go`: ```go user/auth.go // Service user authenticates users. package user import ( "context" "encore.dev/beta/auth" "encore.dev/beta/errs" ) type Data struct { Email string } type AuthParams struct { Authorization string `header:"Authorization"` } //encore:authhandler func AuthHandler(ctx context.Context, p *AuthParams) (auth.UID, *Data, error) { if p.Authorization != "" { return "test", &Data{}, nil } return "", nil, errs.B().Code(errs.Unauthenticated).Msg("no auth header").Err() } ``` This function is our [auth handler](https://encore.dev/docs/go/develop/auth#the-auth-handler). An Encore applications can designate a special function to handle authentication, by defining a function and annotating it with `//encore:authhandler`. This annotation tells Encore to run the function whenever an incoming API call contains authentication data. The auth handler is responsible for validating the incoming authentication data and returning an `auth.UID` (a string type representing a user id). The `auth.UID` can be whatever you wish, but in practice it usually maps directly to the primary key stored in a user table (either defined in the Encore service or in an external service like Firebase or Okta). In order to keep this example simple, we'll just approve any request containing a token that is not empty. Next we will implement some of our auth endpoints and make use of our newly created auth handler. ## [5\. Setting availability](https://encore.dev/docs/go/tutorials/booking-system\#5-setting-availability) Right now the availability is hardcoded to 9:00 - 17:00. Let's add the functionality to let our admin users customize this. Let's start by adding another migration file, this time to create an `availability` table. πŸ₯ Create a file called `2_add_availability.up.sql` inside the `booking/db/migrations` folder. Add the following contents to that file: ```sql booking/db/migrations/2_add_availability.up.sql CREATE TABLE availability ( weekday SMALLINT NOT NULL PRIMARY KEY, -- Sunday=0, Monday=1, etc. start_time TIME NULL, -- null indicates not available end_time TIME NULL -- null indicates not available ); -- Add some placeholder availability to get started INSERT INTO availability (weekday, start_time, end_time) VALUES (0, '09:30', '17:00'), (1, '09:00', '17:00'), (2, '09:00', '18:00'), (3, '08:30', '18:00'), (4, '09:00', '17:00'), (5, '09:00', '17:00'), (6, '09:30', '16:30'); ``` πŸ₯ We can now add two queries to `booking/db/query.sql` so that we can store and retrieve availability: ```less booking/db/query.sql -- name: GetAvailability :many SELECT * FROM availability ORDER BY weekday; -- name: UpdateAvailability :exec INSERT INTO availability (weekday, start_time, end_time) VALUES (@weekday, @start_time, @end_time) ON CONFLICT (weekday) DO UPDATE SET start_time = @start_time, end_time = @end_time; ``` πŸ₯ Run `sqlc generate` to update the generated Go code. πŸ₯ Create a new file in the `booking` service named `availability.go`: ```bash $ touch booking/availability.go ``` πŸ₯ Add the following to that file: ```go booking/availability.go package booking import ( "context" "errors" "fmt" "encore.app/booking/db" "github.com/jackc/pgx/v5/pgtype" "encore.dev/beta/errs" "encore.dev/rlog" ) type Availability struct { Start *string `json:"start" encore:"optional"` End *string `json:"end" encore:"optional"` } type GetAvailabilityResponse struct { Availability []Availability } //encore:api public method=GET path=/availability func GetAvailability(ctx context.Context) (*GetAvailabilityResponse, error) { rows, err := query.GetAvailability(ctx) if err != nil { return nil, err } availability := make([]Availability, 7) for _, row := range rows { day := row.Weekday if day < 0 || day > 6 { rlog.Error("invalid week day in availability table", "row", row) continue } // These never fail start, _ := row.StartTime.TimeValue() end, _ := row.EndTime.TimeValue() availability[day] = Availability{ Start: timeToStr(start), End: timeToStr(end), } } return &GetAvailabilityResponse{Availability: availability}, nil } type SetAvailabilityParams struct { Availability []Availability } //encore:api auth method=POST path=/availability func SetAvailability(ctx context.Context, params SetAvailabilityParams) error { eb := errs.B() tx, err := pgxdb.Begin(ctx) if err != nil { return err } defer tx.Rollback(context.Background()) // committed explicitly on success qry := query.WithTx(tx) for weekday, a := range params.Availability { if weekday > 6 { return eb.Code(errs.InvalidArgument).Msgf("invalid weekday %d", weekday).Err() } start, err1 := strToTime(a.Start) end, err2 := strToTime(a.End) if err := errors.Join(err1, err2); err != nil { return eb.Cause(err).Code(errs.InvalidArgument).Msg("invalid start/end time").Err() } else if start.Valid != end.Valid { return eb.Code(errs.InvalidArgument).Msg("both start/stop must be set, or both null").Err() } else if start.Valid && start.Microseconds > end.Microseconds { return eb.Code(errs.InvalidArgument).Msg("start must be before end").Err() } err = qry.UpdateAvailability(ctx, db.UpdateAvailabilityParams{ Weekday: int16(weekday), StartTime: start, EndTime: end, }) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to update availability").Err() } } err = tx.Commit(ctx) return errs.WrapCode(err, errs.Unavailable, "failed to commit transaction") } ``` This file contains two endpoints, a setter and a getter. The `SetAvailability` endpoint is protected by the `auth` middleware which means that the user must be authenticated in order to call it. The `GetAvailability` endpoint is public and can be called without authentication. πŸ₯ Let's set the availability for each day of the week. Open the Development Dashboard at [http://localhost:9400](http://localhost:9400/) and select the `booking.SetAvailability` endpoint in the API Explorer. For the request body, paste the following: ```json { "Availability": [{\ "start": "09:30",\ "end": "17:00"\ },{\ "start": "09:00",\ "end": "17:00"\ },{\ "start": "09:00",\ "end": "18:00"\ },{\ "start": "08:30",\ "end": "18:00"\ },{\ "start": "09:00",\ "end": "17:00"\ },{\ "start": "09:00",\ "end": "17:00"\ },{\ "start": "09:30",\ "end": "16:30"\ }] } ``` ##### Please note Don't leave the auth token empty, it will cause the auth handler to reject the request. You can use any value for the auth token. Now try retrieving the availability by calling the `booking.GetAvailability` endpoint through the API Explorer in the Development Dashboard. πŸ₯ Add the following functions inside the `booking` package, and import the `slices` package: ```go func listBookingsBetween( ctx context.Context, start, end time.Time, ) ([]*Booking, error) { rows, err := query.ListBookingsBetween(ctx, db.ListBookingsBetweenParams{ StartTime: pgtype.Timestamp{Time: start, Valid: true}, EndTime: pgtype.Timestamp{Time: end, Valid: true}, }) if err != nil { return nil, err } var bookings []*Booking for _, row := range rows { bookings = append(bookings, &Booking{ ID: row.ID, Start: row.StartTime.Time, End: row.EndTime.Time, Email: row.Email, }) } return bookings, nil } func filterBookableSlots( slots []BookableSlot, now time.Time, bookings []*Booking, ) []BookableSlot { // Remove slots for which the start time has already passed. slots = slices.DeleteFunc(slots, func(s BookableSlot) bool { // Has the slot already passed? if s.Start.Before(now) { return true } // Is there a booking that overlaps with this slot? for _, b := range bookings { if b.Start.Before(s.End) && b.End.After(s.Start) { return true } } return false }) return slots } ``` We'll use these functions to figure out which slots are bookable, and which are not, to avoid double bookings. πŸ₯ Now we can update the `Book` endpoint inside `booking.go` and make use of these new functions: ```go booking/booking.go //encore:api public method=POST path=/booking func Book(ctx context.Context, p *BookParams) error { eb := errs.B() now := time.Now() if p.Start.Before(now) { return eb.Code(errs.InvalidArgument).Msg("start time must be in the future").Err() } tx, err := pgxdb.Begin(ctx) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to start transaction").Err() } defer tx.Rollback(context.Background()) // committed explicitly on success // Get the bookings for this day. startOfDay := time.Date(p.Start.Year(), p.Start.Month(), p.Start.Day(), 0, 0, 0, 0, p.Start.Location()) bookings, err := listBookingsBetween(ctx, startOfDay, startOfDay.AddDate(0, 0, 1)) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to list bookings").Err() } // Is this slot bookable? slot := BookableSlot{Start: p.Start, End: p.Start.Add(DefaultBookingDuration)} if len(filterBookableSlots([]BookableSlot{slot}, now, bookings)) == 0 { return eb.Code(errs.InvalidArgument).Msg("slot is unavailable").Err() } _, err = query.InsertBooking(ctx, db.InsertBookingParams{ StartTime: pgtype.Timestamp{Time: p.Start, Valid: true}, EndTime: pgtype.Timestamp{Time: p.Start.Add(DefaultBookingDuration), Valid: true}, Email: p.Email, }) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to insert booking").Err() } if err := tx.Commit(ctx); err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to commit transaction").Err() } return nil } ``` πŸ₯ Inside `slots.go`, update the `GetBookableSlots` endpoint and the `bookableSlotsForDay` functions to look like this: ```go booking/slots.go //encore:api public method=GET path=/slots/:from func GetBookableSlots(ctx context.Context, from string) (*SlotsResponse, error) { fromDate, err := time.Parse("2006-01-02", from) if err != nil { return nil, err } availabilityResp, err := GetAvailability(ctx) if err != nil { return nil, err } availability := availabilityResp.Availability const numDays = 7 var slots []BookableSlot for i := 0; i < numDays; i++ { date := fromDate.AddDate(0, 0, i) weekday := int(date.Weekday()) if len(availability) <= weekday { break } daySlots, err := bookableSlotsForDay(date, &availability[weekday]) if err != nil { return nil, err } slots = append(slots, daySlots...) } // Get bookings for the next 7 days. activeBookings, err := listBookingsBetween(ctx, fromDate, fromDate.AddDate(0, 0, numDays)) if err != nil { return nil, err } slots = filterBookableSlots(slots, time.Now(), activeBookings) return &SlotsResponse{Slots: slots}, nil } func bookableSlotsForDay(date time.Time, avail *Availability) ([]BookableSlot, error) { if avail.Start == nil || avail.End == nil { return nil, nil } availStartTime, err1 := strToTime(avail.Start) availEndTime, err2 := strToTime(avail.End) if err := errors.Join(err1, err2); err != nil { return nil, err } availStart := date.Add(time.Duration(availStartTime.Microseconds) * time.Microsecond) availEnd := date.Add(time.Duration(availEndTime.Microseconds) * time.Microsecond) // Compute the bookable slots in this day, based on availability. var slots []BookableSlot start := availStart for { end := start.Add(DefaultBookingDuration) if end.After(availEnd) { break } slots = append(slots, BookableSlot{ Start: start, End: end, }) start = end } return slots, nil } ``` ## [6\. Managing scheduled bookings](https://encore.dev/docs/go/tutorials/booking-system\#6-managing-scheduled-bookings) To display the scheduled bookings in the admin dashboard, we need to add the functionality to list all bookings. While we're at it, we'll also make it possible to delete bookings. πŸ₯ Add two new endpoints to `booking/booking.go`: ```go booking/booking.go type ListBookingsResponse struct { Booking []*Booking `json:"bookings"` } //encore:api auth method=GET path=/booking func ListBookings(ctx context.Context) (*ListBookingsResponse, error) { rows, err := query.ListBookings(ctx) if err != nil { return nil, err } var bookings []*Booking for _, row := range rows { bookings = append(bookings, &Booking{ ID: row.ID, Start: row.StartTime.Time, End: row.EndTime.Time, Email: row.Email, }) } return &ListBookingsResponse{Booking: bookings}, nil } //encore:api auth method=DELETE path=/booking/:id func DeleteBooking(ctx context.Context, id int64) error { return query.DeleteBooking(ctx, id) } ``` That's it! We now have all the backend endpoints in place to be able to supply the frontend with data. πŸŽ‰ ## [7\. Running the React frontend](https://encore.dev/docs/go/tutorials/booking-system\#7-running-the-react-frontend) The frontend should now be working as expected. πŸ₯ Go to [http://localhost:4000/frontend/](http://localhost:4000/frontend/) and try out your new booking system. The frontend is built using [React](https://react.dev/) and [Tailwind CSS](https://tailwindcss.com/). It uses Encore's ability to generate type-safe [request clients](https://encore.dev/docs/go/cli/client-generation). This means you don't need to manually keep the request/response objects in sync on the frontend. To generate a client: ```bash $ encore gen client --output=./src/client.ts --env= ``` While you're developing, you are going to want to run this command quite often (whenever you make a change to your endpoints) so having it as an `npm` script is a good idea. Take a look at the scripts in the `package.json` file: ```json { ... "scripts": { ... "gen": "encore gen client --output=./src/lib/client.ts --env=staging", "gen:local": "encore gen client --output=./src/lib/client.ts --env=local" }, } ``` For this frontend we use the request client together with [TanStack Query](https://tanstack.com/query/latest). When building something a bit more complex, you will likely need to deal with caching, refetching, and data going stale. [TanStack Query](https://tanstack.com/query/latest) is a popular library that was built to solve exactly these problems and works great with the Encore request client. See our the docs page about [integrating with a web frontend](https://encore.dev/docs/how-to/integrate-frontend) to learn more. ## [8\. Deploy to Encore's development cloud](https://encore.dev/docs/go/tutorials/booking-system\#8-deploy-to-encores-development-cloud) Let's deploy the project to Encore's free development cloud. Encore comes with built-in CI/CD, and the deployment process is as simple as a `git push`. (You can also integrate with GitHub to activate per Pull Request Preview Environments, learn more in the [CI/CD docs](https://encore.dev/docs/platform/deploy/deploying).) πŸ₯ Now, let's deploy your app to Encore's free development cloud by running: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From there you can also see metrics, traces, link your app to a GitHub repo to get automatic deploys on new commits, and connect your own AWS or GCP account to use for production deployment. πŸ₯ When the deploy has finished, you can try out your booking system by going to `https://staging-$APP_ID.encr.app/frontend/`. _You now have an Appointment Booking System running in the cloud, well done!_ ## [8\. Sending confirmation emails using SendGrid](https://encore.dev/docs/go/tutorials/booking-system\#8-sending-confirmation-emails-using-sendgrid) In order for the users to get a confirmation email when they book an appointment we need to add an email integration. Conveniently for us, there is a ready to use SendGrid integration as an [Encore Bit](https://github.com/encoredev/examples?tab=readme-ov-file#bits). πŸ₯ [Follow the instructions](https://github.com/encoredev/examples/tree/main/bits/sendgrid) to add the SendGrid integration to your project. Next, we need to call our new `sendgrid` service when an appointment is booked. πŸ₯ Add a call to `sendgrid.Send` in the `Book` endpoint: ```go booking/booking.go //encore:api public method=POST path=/booking func Book(ctx context.Context, p *BookParams) error { eb := errs.B() now := time.Now() if p.Start.Before(now) { return eb.Code(errs.InvalidArgument).Msg("start time must be in the future").Err() } tx, err := pgxdb.Begin(ctx) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to start transaction").Err() } defer tx.Rollback(context.Background()) // committed explicitly on success // Get the bookings for this day. startOfDay := time.Date(p.Start.Year(), p.Start.Month(), p.Start.Day(), 0, 0, 0, 0, p.Start.Location()) bookings, err := listBookingsBetween(ctx, startOfDay, startOfDay.AddDate(0, 0, 1)) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to list bookings").Err() } // Is this slot bookable? slot := BookableSlot{Start: p.Start, End: p.Start.Add(DefaultBookingDuration)} if len(filterBookableSlots([]BookableSlot{slot}, now, bookings)) == 0 { return eb.Code(errs.InvalidArgument).Msg("slot is unavailable").Err() } _, err = query.InsertBooking(ctx, db.InsertBookingParams{ StartTime: pgtype.Timestamp{Time: p.Start, Valid: true}, EndTime: pgtype.Timestamp{Time: p.Start.Add(DefaultBookingDuration), Valid: true}, Email: p.Email, }) if err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to insert booking").Err() } if err := tx.Commit(ctx); err != nil { return eb.Cause(err).Code(errs.Unavailable).Msg("failed to commit transaction").Err() } // Send confirmation email using SendGrid formattedTime := pgtype.Timestamp{Time: p.Start, Valid: true}.Time.Format("2006-01-02 15:04") _, err = sendgrid.Send(ctx, &sendgrid.SendParams{ From: sendgrid.Address{ Name: "", Email: "", }, To: sendgrid.Address{ Email: p.Email, }, Subject: "Booking Confirmation", Text: "Thank you for your booking!\nWe look forward to seeing you soon at " + formattedTime, Html: "", }) if err != nil { return err } return nil } ``` ##### Please note The `From` email used when sending emails needs to go through the SendGrid verification process before it can be used. You can read more about it here: [https://sendgrid.com/docs/ui/sending-email/sender-verification/](https://sendgrid.com/docs/ui/sending-email/sender-verification/) The default behaviour of the SendGrid integration is to only send emails on production environments. You can create production environments through the Encore Cloud Dashboard. ## [9\. Deploy your finished Booking System](https://encore.dev/docs/go/tutorials/booking-system\#9-deploy-your-finished-booking-system) Now you're ready to deploy your finished Booking System, complete with a SendGrid integration. πŸ₯ As before, deploying your app to the cloud is as simple as running: ```bash $ git add -A . $ git commit -m 'Add sendgrid integration' $ git push encore ``` ### [Celebrate with fireworks](https://encore.dev/docs/go/tutorials/booking-system\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) [Building an Incident Management tool](https://encore.dev/docs/go/tutorials/incident-management-tool) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/tutorials/booking-system.mdx) ## Incident Management Tool Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this tutorial, we're going to walk through together how to build our very own Incident Management Tool like [Incident.io](https://incident.io/) or [PagerDuty](https://pagerduty.com/). We can then have our own on call schedule that can be rotated between many users, and have incidents come and be assigned according to the schedule! ![Slack Incident Management Tool](https://encore.dev/assets/docs/incident-slack-example.png) In about 30 minutes, your application will be able to support: - Creating users, as well as schedules for when users will be on call - Creating incidents, and reminders for unacknowledged incidents on Slack every 10 minutes - Auto-assign incidents which are unassigned (when the next user is on call) _Sounds good? Let's dig in!_ Or if you'd rather watch a video of this tutorial, you can do that below. Building an Incident Management System in Go - YouTube [Photo image of Encore](https://www.youtube.com/channel/UCvqeAqMPotfuA6SPXa4VhNQ?embeds_referring_euri=https%3A%2F%2Fencore.dev%2Fdocs%2Fgo%2Ftutorials%2Fincident-management-tool) Encore 5.16K subscribers [Building an Incident Management System in Go](https://www.youtube.com/watch?v=BR_ys_qR2kI) Encore Search Info Shopping Tap to unmute If playback doesn't begin shortly, try restarting your device. Share Include playlist An error occurred while retrieving sharing information. Please try again later. Watch later Share Copy link [Watch on www.youtube.com](https://www.youtube.com/watch?v=BR_ys_qR2kI) Watch on View full project on [GitHub](https://github.com/encoredev/example-app-oncall) ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Create your Encore application](https://encore.dev/docs/go/tutorials/incident-management-tool\#1-create-your-encore-application) πŸ₯ Create a new Encore application by running `encore app create`, select `Empty app` as the template and name it `oncall-tutorial`. ## [2\. Integrate with Slack](https://encore.dev/docs/go/tutorials/incident-management-tool\#2-integrate-with-slack) πŸ₯ Follow [this guide to create your own Incoming Webhook](https://api.slack.com/messaging/webhooks) for your Slack workspace. Incoming webhooks cannot read messages, and can only post to a specific channel of your choice. ![Creating a Slack app](https://encore.dev/assets/docs/incident-slack-app-creation.png) πŸ₯ Once you have your Webhook URL which starts with `https://hooks.slack.com/services/...` then copy and paste that and run the following commands to save these as secrets. We recommend having a different webhook/channel for development and production. ```bash $ encore secret set --type dev,local,pr SlackWebhookURL $ encore secret set --type prod SlackWebhookURL ``` πŸ₯ Next, let's create our `slack` service that contains the logic for calling the Webhook URL in order to post notifications to our Slack. To do this we need to implement our code in `slack/slack.go`: ```go // Service slack calls a webhook to post notifications to Slack. package slack import ( "bytes" "context" "encoding/json" "encore.dev/beta/errs" "io" "net/http" ) type NotifyParams struct { Text string `json:"text"` } //encore:api private func Notify(ctx context.Context, p *NotifyParams) error { eb := errs.B() reqBody, err := json.Marshal(p) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, "POST", secrets.SlackWebhookURL, bytes.NewReader(reqBody)) if err != nil { return err } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { body, _ := io.ReadAll(resp.Body) return eb.Code(errs.Unavailable).Msgf("notify slack: %s: %s", resp.Status, body).Err() } return nil } var secrets struct { SlackWebhookURL string } ``` ##### Please note The `slack` service can be reused across any of your Encore apps. All you need is the `slack/slack.go` code and the `SlackWebhookURL` secret to be defined. Then you can call the following method signature anywhere in your app: ```go slack.Notify(context, &slack.NotifyParams{ Text: "Send a Slack notification" }) ``` ## [3\. Create a service to manage users](https://encore.dev/docs/go/tutorials/incident-management-tool\#3-create-a-service-to-manage-users) With an Incident Management Tool (or usually any tool, for that matter) we need a service for users. This will allow us to figure out who we should assign incoming incidents to! To get started, we need to create a `users` service with the following resources: | # | Type | Description / Filename | | --- | --- | --- | | #1 | SQL Migration | Our PostgreSQL schema for scheduling data
`users/migrations/1_create_users.up.sql` | | #2 | HTTP Endpoint
`POST /users` | Create a new User
`users/users.go` | | #3 | HTTP Endpoint
`GET /users/:id` | Get an existing User
`users/users.go` | With #1, let's design our database schema for a User in our system. For now let's store a first and last name as well as a Slack handle in case we need to notify them about any incidents which may have been assigned to them or acknowledged by them. πŸ₯ Let's create our migration file in `users/migrations/1_create_users.up.sql`: ```sql CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, slack_handle VARCHAR(255) NOT NULL ); ``` πŸ₯ Then, we need to write our code to implement the HTTP endpoints listed in #2 (for creating a user) and #3 (for listing a user) belonging in `users/users.go`. Let's split them out into three sections: our structs (i.e. data models) and methods. ```go // Service users manages users and assigns incidents. package users import ( "context" "encore.dev/storage/sqldb" ) // This is a Go struct representing our PostgreSQL schema for `users` type User struct { Id int32 FirstName string LastName string SlackHandle string } // Define a database named 'users', using the database migrations // in the "./migrations" folder. Encore automatically provisions, // migrates, and connects to the database. var db = sqldb.NewDatabase("users", sqldb.DatabaseConfig{ Migrations: "./migrations", }) //encore:api public method=POST path=/users func Create(ctx context.Context, params CreateParams) (*User, error) { user := User{} err := db.QueryRow(ctx, ` INSERT INTO users (first_name, last_name, slack_handle) VALUES ($1, $2, $3) RETURNING id, first_name, last_name, slack_handle `, params.FirstName, params.LastName, params.SlackHandle).Scan(&user.Id, &user.FirstName, &user.LastName, &user.SlackHandle) if err != nil { return nil, err } return &user, nil } // This is what JSON params our POST /users endpoint will accept type CreateParams struct { FirstName string LastName string SlackHandle string } //encore:api public method=GET path=/users/:id func Get(ctx context.Context, id int32) (*User, error) { user := User{} err := db.QueryRow(ctx, ` SELECT id, first_name, last_name, slack_handle FROM users WHERE id = $1 `, id).Scan(&user.Id, &user.FirstName, &user.LastName, &user.SlackHandle) if err != nil { return nil, err } return &user, nil } ``` πŸ₯ Next, type `encore run` in your Terminal and in a separate window run the command under **cURL Request** (feel free to edit the values!) to create our first user: ```bash curl -d '{ "FirstName":"Katy", "LastName":"Smith", "SlackHandle":"katy" }' http://localhost:4000/users # Example JSON response # { # "Id":1, # "FirstName":"Katy", # "LastName":"Smith", # "SlackHandle":"katy" # } ``` Fantastic, we now have a user system in our app! Next we need a list of start and end times of each scheduled rotation so we know who to assign incoming incidents to (as well as notify them on Slack!) ## [4\. Add scheduling](https://encore.dev/docs/go/tutorials/incident-management-tool\#4-add-scheduling) A good incident management tool should be able to spread the workload of diagnosing and fixing incidents across multiple users in a team. Being able to know who the correct person to assign an incident to is very important; our incidents might not get resolved quickly otherwise! In order to achieve this, let's create a new service called `schedules`: | # | Type | Description / Filename | | --- | --- | --- | | #1 | SQL Migration | Our PostgreSQL schema for user data
`schedules/migrations/1_create_schedules.up.sql` | | #2 | HTTP Endpoint
`GET /schedules` | Get list of schedules between time range
`schedules/schedules.go` | | #3 | HTTP Endpoint
`POST /users/:id/schedules` | Create a new Schedule
`schedules/schedules.go` | | #4 | HTTP Endpoint
`GET /scheduled/:timestamp` | Get Schedule at specific time
`schedules/schedules.go` | For the SQL migration in #1, we need to create both a table and an index. For every rotation let's need a new entry containing the user who it is for as well as the start and end times of the scheduled rotation. πŸ₯ Let's create our migration file in `schedules/migrations/1_create_schedules.up.sql`: ```sql CREATE TABLE schedules ( id BIGSERIAL PRIMARY KEY, user_id INTEGER NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP NOT NULL ); CREATE INDEX schedules_range_index ON schedules (start_time, end_time); ``` ##### Please note Table indexes are used to optimize lookups without having to search every row in the table. In this case, looking up rows against both `start_time` and `end_time` will be faster _with the index_ as the dataset grows. [Learn more about PostgreSQL indexes here](https://www.tutorialspoint.com/postgresql/postgresql_indexes.htm). πŸ₯ Next, let's implement the HTTP endpoints for #2 (listing schedules), #3 (creating a schedule) and #4 (getting the schedule/user at a specific time) in `schedules/schedules.go`: ```go // Service schedules implements schedules to answer who should be assigned to an incident. package schedules import ( "context" "errors" "time" "encore.app/users" "encore.dev/beta/errs" "encore.dev/storage/sqldb" ) // Define a database named 'schedules', using the database migrations // in the "./migrations" folder. Encore automatically provisions, // migrates, and connects to the database. var db = sqldb.NewDatabase("schedules", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // This struct holds multiple Schedule structs type Schedules struct { Items []Schedule } // This is a Go struct representing our PostgreSQL schema for `schedules` type Schedule struct { Id int32 User users.User Time TimeRange } // As we use time ranges in our schedule, we created a generic TimeRange struct type TimeRange struct { Start time.Time End time.Time } //encore:api public method=POST path=/users/:userId/schedules func Create(ctx context.Context, userId int32, timeRange TimeRange) (*Schedule, error) { eb := errs.B().Meta("userId", userId, "timeRange", timeRange) // check for existing overlapping schedules if schedule, err := ScheduledAt(ctx, timeRange.Start.String()); schedule != nil && err == nil { return nil, eb.Code(errs.InvalidArgument).Cause(err).Msg("schedule already exists within this start timestamp").Err() } if schedule, err := ScheduledAt(ctx, timeRange.End.String()); schedule != nil && err == nil { return nil, eb.Code(errs.InvalidArgument).Cause(err).Msg("schedule already exists within this end timestamp").Err() } // check user exists user, err := users.Get(ctx, userId) if err != nil { return nil, eb.Code(errs.Unavailable).Cause(err).Msg("failed to get user").Err() } schedule := Schedule{User: *user, Time: TimeRange{}} err = db.QueryRow( ctx, `INSERT INTO schedules (user_id, start_time, end_time) VALUES ($1, $2, $3) RETURNING id, start_time, end_time`, userId, timeRange.Start, timeRange.End, ).Scan(&schedule.Id, &schedule.Time.Start, &schedule.Time.End) if err != nil { return nil, eb.Code(errs.Unavailable).Cause(err).Msg("failed to insert schedule").Err() } return &schedule, nil } //encore:api public method=GET path=/scheduled func ScheduledNow(ctx context.Context) (*Schedule, error) { return scheduled(ctx, time.Now()) } //encore:api public method=GET path=/scheduled/:timestamp func ScheduledAt(ctx context.Context, timestamp string) (*Schedule, error) { eb := errs.B().Meta("timestamp", timestamp) parsedtime, err := time.Parse(time.RFC3339, timestamp) if err != nil { return nil, eb.Code(errs.InvalidArgument).Msg("timestamp is not in a valid format").Err() } return scheduled(ctx, parsedtime) } func scheduled(ctx context.Context, timestamp time.Time) (*Schedule, error) { eb := errs.B().Meta("timestamp", timestamp) schedule, err := RowToSchedule(ctx, db.QueryRow(ctx, ` SELECT id, user_id, start_time, end_time FROM schedules WHERE start_time <= $1 AND end_time >= $1 `, timestamp.UTC())) if errors.Is(err, db.ErrNoRows) { return nil, eb.Code(errs.NotFound).Msg("no schedule found").Err() } if err != nil { return nil, err } return schedule, nil } //encore:api public method=GET path=/schedules func ListByTimeRange(ctx context.Context, timeRange TimeRange) (*Schedules, error) { rows, err := db.Query(ctx, ` SELECT id, user_id, start_time, end_time FROM schedules WHERE start_time >= $1 AND end_time <= $2 ORDER BY start_time ASC `, timeRange.Start, timeRange.End) if err != nil { return nil, err } defer rows.Close() var schedules []Schedule for rows.Next() { schedule, err := RowToSchedule(ctx, rows) if err != nil { return nil, err } schedules = append(schedules, *schedule) } return &Schedules{Items: schedules}, nil } //encore:api public method=DELETE path=/schedules func DeleteByTimeRange(ctx context.Context, timeRange TimeRange) (*Schedules, error) { schedules, err := ListByTimeRange(ctx, timeRange) if err != nil { return nil, err } _, err = db.Exec(ctx, `DELETE FROM schedules WHERE start_time >= $1 AND end_time <= $2`, timeRange.Start, timeRange.End) if err != nil { return nil, err } return schedules, err } // Helper function to convert a Row object to to Schedule func RowToSchedule(ctx context.Context, row interface { Scan(dest ...interface{}) error }) (*Schedule, error) { var schedule = &Schedule{Time: TimeRange{}} var userId int32 err := row.Scan(&schedule.Id, &userId, &schedule.Time.Start, &schedule.Time.End) if err != nil { return nil, err } user, err := users.Get(ctx, userId) if err != nil { return nil, err } schedule.User = *user return schedule, nil } ``` πŸ₯ Next, type `encore run` in your Terminal and in a separate window run the command under **cURL Request** (also feel free to edit the values!) to create our first schedule against the user we created earlier: ```bash curl -d '{ "Start":"2023-11-28T10:00:00Z", "End":"2023-11-30T10:00:00Z" }' "http://localhost:4000/users/1/schedules" # Example JSON response # { # "Id":1, # "User":{ # "Id":1, # "FirstName":"Katy", # "LastName":"Smith", # "SlackHandle":"katy" # }, # "Time":{ # "Start":"2023-11-28T10:00:00Z", # "End":"2023-11-30T10:00:00Z" # } # } ``` ## [5\. Create a service to manage incidents](https://encore.dev/docs/go/tutorials/incident-management-tool\#5-create-a-service-to-manage-incidents) So we have users, and we know who is available to be notified (or if nobody should be notified) at any given time with the introduction of the `schedules` service. The only thing we're missing is the ability to report, assign and acknowledge incidents! The flow we're going to implement is: an incoming incident will arrive, let's either unassign or auto-assign it based on the `schedules` service, and incidents have to be acknowledged. If they are not acknowledged, they will continue to be notified on Slack every 10 minutes until it has. To start with, we need to create a new `incidents` service with the following resources: | # | Type | Description / Filename | | --- | --- | --- | | #1 | SQL Migration | Our PostgreSQL schema for storing incidents
`incidents/migrations/1_create_incidents.up.sql` | | #2 | HTTP Endpoint
`GET /incidents` | Get list of all unacknowledged incidents
`incidents/incidents.go` | | #3 | HTTP Endpoint
`PUT /incidents/:id/acknowledge` | Acknowledge an incident
`incidents/incidents.go` | | #4 | HTTP Endpoint
`GET /scheduled/:timestamp` | Get
`incidents/incidents.go` | For the SQL migration in #1, we need to create the table for our incidents. We need to have a one-to-many relationship between an user and an incident. That is, an incident can only be assigned to a single user but a single user can be assigned to many incidents. πŸ₯ Let's create our migration file in `incidents/migrations/1_create_incidents.up.sql`: ```sql CREATE TABLE incidents ( id BIGSERIAL PRIMARY KEY, assigned_user_id INTEGER, body TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), acknowledged_at TIMESTAMP ); ``` πŸ₯ Next, our code belonging in `incidents/incidents.go` for being able to support incidents is below: ```go // Service incidents reports, assigns and acknowledges incidents. package incidents import ( "context" "encore.app/schedules" "encore.app/slack" "encore.app/users" "encore.dev/beta/errs" "encore.dev/storage/sqldb" "fmt" "time" ) // Define a database named 'incidents', using the database migrations // in the "./migrations" folder. Encore automatically provisions, // migrates, and connects to the database. var db = sqldb.NewDatabase("incidents", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // This struct holds multiple Incidents structs type Incidents struct { Items []Incident } // This is a Go struct representing our PostgreSQL schema for `incidents` type Incident struct { Id int32 Body string CreatedAt time.Time Acknowledged bool AcknowledgedAt *time.Time Assignee *users.User } //encore:api public method=GET path=/incidents func List(ctx context.Context) (*Incidents, error) { rows, err := db.Query(ctx, ` SELECT id, assigned_user_id, body, created_at, acknowledged_at FROM incidents WHERE acknowledged_at IS NULL `) if err != nil { return nil, err } return RowsToIncidents(ctx, rows) } //encore:api public method=PUT path=/incidents/:id/acknowledge func Acknowledge(ctx context.Context, id int32) (*Incident, error) { eb := errs.B().Meta("incidentId", id) rows, err := db.Query(ctx, ` UPDATE incidents SET acknowledged_at = NOW() WHERE acknowledged_at IS NULL AND id = $1 RETURNING id, assigned_user_id, body, created_at, acknowledged_at `, id) if err != nil { return nil, err } incidents, err := RowsToIncidents(ctx, rows) if err != nil { return nil, err } if incidents.Items == nil { return nil, eb.Code(errs.NotFound).Msg("no incident found").Err() } incident := &incidents.Items[0] _ = slack.Notify(ctx, &slack.NotifyParams{ Text: fmt.Sprintf("Incident #%d assigned to %s %s <@%s> has been acknowledged:\n%s", incident.Id, incident.Assignee.FirstName, incident.Assignee.LastName, incident.Assignee.SlackHandle, incident.Body), }) return incident, err } //encore:api public method=POST path=/incidents func Create(ctx context.Context, params *CreateParams) (*Incident, error) { // check who is on-call schedule, err := schedules.ScheduledNow(ctx) incident := Incident{} if schedule != nil { incident.Assignee = &schedule.User } var row *db.Row if schedule != nil { // Someone is on-call row = db.QueryRow(ctx, ` INSERT INTO incidents (assigned_user_id, body) VALUES ($1, $2) RETURNING id, body, created_at `, &schedule.User.Id, params.Body) } else { // Nobody is on-call row = db.QueryRow(ctx, ` INSERT INTO incidents (body) VALUES ($1) RETURNING id, body, created_at `, params.Body) } if err = row.Scan(&incident.Id, &incident.Body, &incident.CreatedAt); err != nil { return nil, err } var text string if incident.Assignee != nil { text = fmt.Sprintf("Incident #%d created and assigned to %s %s <@%s>\n%s", incident.Id, incident.Assignee.FirstName, incident.Assignee.LastName, incident.Assignee.SlackHandle, incident.Body) } else { text = fmt.Sprintf("Incident #%d created and unassigned\n%s", incident.Id, incident.Body) } _ = slack.Notify(ctx, &slack.NotifyParams{Text: text}) return &incident, nil } type CreateParams struct { Body string } // Helper to take a db.Rows instance and convert it into a list of Incidents func RowsToIncidents(ctx context.Context, rows *db.Rows) (*Incidents, error) { eb := errs.B() defer rows.Close() var incidents []Incident for rows.Next() { var incident = Incident{} var assignedUserId *int32 if err := rows.Scan(&incident.Id, &assignedUserId, &incident.Body, &incident.CreatedAt, &incident.AcknowledgedAt); err != nil { return nil, eb.Code(errs.Unknown).Msgf("could not scan: %v", err).Err() } if assignedUserId != nil { user, err := users.Get(ctx, *assignedUserId) if err != nil { return nil, eb.Code(errs.NotFound).Msgf("could not retrieve user for incident %v", assignedUserId).Err() } incident.Assignee = user } incident.Acknowledged = incident.AcknowledgedAt != nil incidents = append(incidents, incident) } return &Incidents{Items: incidents}, nil } ``` Fantastic! We have an _almost_ working application. The main two things we're missing are: 1. For unacknowledged incidents, we need to post a reminder on Slack every 10 minutes until they have been acknolwedged. 2. Whenever a user is currently on call, we should assign all previously unassigned incidents to them. πŸ₯ To achieve this, we'll need to create two [Cron Jobs](http://localhost:3000/docs/develop/cron-jobs) which thankfully Encore makes incredibly simple. So let's go ahead and create the first one for reminding us every 10 minutes of incidents we haven't acknowledged. Go ahead and add the code below to our `incidents/incidents.go` file: ```go // Track unacknowledged incidents var _ = cron.NewJob("unacknowledged-incidents-reminder", cron.JobConfig{ Title: "Notify on Slack about incidents which are not acknowledged", Every: 10 * cron.Minute, Endpoint: RemindUnacknowledgedIncidents, }) //encore:api private func RemindUnacknowledgedIncidents(ctx context.Context) error { incidents, err := List(ctx) // we never query for acknowledged incidents if err != nil { return err } if incidents == nil { return nil } var items = []string{"These incidents have not been acknowledged yet. Please acknowledge them otherwise you will be reminded every 10 minutes:"} for _, incident := range incidents.Items { var assignee string if incident.Assignee != nil { assignee = fmt.Sprintf("%s %s (<@%s>)", incident.Assignee.FirstName, incident.Assignee.LastName, incident.Assignee.SlackHandle) } else { assignee = "Unassigned" } items = append(items, fmt.Sprintf("[%s] [#%d] %s", assignee, incident.Id, incident.Body)) } if len(incidents.Items) > 0 { _ = slack.Notify(ctx, &slack.NotifyParams{Text: strings.Join(items, "\n")}) } return nil } ``` And for our second cronjob, when someone goes on call we need to automatically assign the previously unassigned incidents to them. We don't have a HTTP endpoint for assigning incidents so we need to implement a `PUT /incidents/:id/assign` endpoint. πŸ₯ So let's also add that endpoint as well as the cronjob code to our `incidents/incidents.go` file: ```go //encore:api public method=PUT path=/incidents/:id/assign func Assign(ctx context.Context, id int32, params *AssignParams) (*Incident, error) { eb := errs.B().Meta("params", params) rows, err := db.Query(ctx, ` UPDATE incidents SET assigned_user_id = $1 WHERE acknowledged_at IS NULL AND id = $2 RETURNING id, assigned_user_id, body, created_at, acknowledged_at `, params.UserId, id) if err != nil { return nil, err } incidents, err := RowsToIncidents(ctx, rows) if err != nil { return nil, err } if incidents.Items == nil { return nil, eb.Code(errs.NotFound).Msg("no incident found").Err() } incident := &incidents.Items[0] _ = slack.Notify(ctx, &slack.NotifyParams{ Text: fmt.Sprintf("Incident #%d is re-assigned to %s %s <@%s>\n%s", incident.Id, incident.Assignee.FirstName, incident.Assignee.LastName, incident.Assignee.SlackHandle, incident.Body), }) return incident, err } type AssignParams struct { UserId int32 } var _ = cron.NewJob("assign-unassigned-incidents", cron.JobConfig{ Title: "Assign unassigned incidents to user on-call", Every: 1 * cron.Minute, Endpoint: AssignUnassignedIncidents, }) //encore:api private func AssignUnassignedIncidents(ctx context.Context) error { // if this fails, we don't have anyone on call so let's skip this schedule, err := schedules.ScheduledNow(ctx) if err != nil { return err } incidents, err := List(ctx) // we never query for acknowledged incidents if err != nil { return err } for _, incident := range incidents.Items { if incident.Assignee != nil { continue // this incident has already been assigned } _, err := Assign(ctx, incident.Id, &AssignParams{UserId: schedule.User.Id}) if err == nil { rlog.Info("OK assigned unassigned incident", "incident", incident, "user", schedule.User) } else { rlog.Error("FAIL to assign unassigned incident", "incident", incident, "user", schedule.User, "err", err) return err } } return nil } ``` πŸ₯ Next, call `encore run` in your Terminal and in a separate window run the command under **cURL Request** (also feel free to edit the values!) to trigger our first incident. Most likely we won't have an assigned user unless you have scheduled a time that overlaps with right now in the last cURL request for creating a schedule: ```bash curl -d '{ "Body":"An unexpected error happened on example-website.com on line 38. It needs addressing now!" }' http://localhost:4000/incidents # Example JSON response # { # "Id":1, # "Body":"An unexpected error happened on example-website.com on line 38. It needs addressing now!", # "CreatedAt":"2022-09-28T15:09:00Z", # "Acknowledged":false, # "AcknowledgedAt":null, # "Assignee":null # } ``` ## [6\. Try your app and deploy](https://encore.dev/docs/go/tutorials/incident-management-tool\#6-try-your-app-and-deploy) Congratulations! Our application looks ready for others to try - we have our `users`, `schedules``incidents` and `slack` services along with 3 database tables and 2 cronjobs. Even better that all of the deployment and maintenance is taken care by Encore! πŸ₯ To try out your application, type `encore run` in your Terminal and run the following cURL commands: ```bash # Step 1: Create a User and copy the User ID to your clipboard curl -d '{ "FirstName":"Katy", "LastName":"Smith", "SlackHandle":"katy" }' http://localhost:4000/users # Step 2: Create a schedule for the user we just created curl -d '{ "Start":"2022-09-28T10:00:00Z", "End":"2022-09-29T10:00:00Z" }' "http://localhost:4000/users/1/schedules" # Step 3: Trigger an incident curl -d '{ "Body":"An unexpected error happened on example-website.com on line 38. It needs addressing now!" }' http://localhost:4000/incidents # Step 4: Acknowledge the Incident curl -X PUT "http://localhost:4000/incidents/1/acknowledge" ``` And if you don't acknowledge incoming incidents on Step 4, you will be reminded on Slack every 10 minutes: ![Being reminded on Slack about unacknowledged incidents](https://encore.dev/assets/docs/incident-slack-reminder-example.png) ### [Deploy to the cloud](https://encore.dev/docs/go/tutorials/incident-management-tool\#deploy-to-the-cloud) πŸ₯ Push your changes and deploy your application to Encore's free development cloud by running: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From there you can also see metrics, traces, link your app to a GitHub repo to get automatic deploys on new commits, and connect your own AWS or GCP account to use for production deployment. ### [Celebrate with fireworks](https://encore.dev/docs/go/tutorials/incident-management-tool\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) ### [Architecture Diagram](https://encore.dev/docs/go/tutorials/incident-management-tool\#architecture-diagram) Take a look at the [Encore Flow](https://encore.dev/docs/go/observability/encore-flow) diagram that was automatically generated for our new application too! ![Being reminded on Slack about unacknowledged incidents](https://encore.dev/assets/docs/incident-flow-diagram.png) ### [GitHub Repository](https://encore.dev/docs/go/tutorials/incident-management-tool\#github-repository) πŸ₯ Check out the `example-app-oncall` repository on GitHub for this example which includes additional features and tests: [https://github.com/encoredev/example-app-oncall](https://github.com/encoredev/example-app-oncall) Alternatively, you can clone our example application by running this in your Terminal: ```bash $ encore app create --example https://github.com/encoredev/example-app-oncall ``` ### [Feedback](https://encore.dev/docs/go/tutorials/incident-management-tool\#feedback) πŸ₯ We'd love to hear your thoughts about this tutorial and learn about what you're building next. Let us know by [tweeting your experience](https://twitter.com/encoredotdev), blog about it, or talk to us about it on [Discord](https://encore.dev/discord). [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) [App Structure](https://encore.dev/docs/go/primitives/app-structure) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/tutorials/incident-management-tool.md) ## Encore App Structure Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore uses a monorepo design and it's best to use one Encore app for your entire backend application. This lets Encore build an application model that spans your entire app, necessary to get the most value out of many features like [distributed tracing](https://encore.dev/docs/go/observability/tracing) and [Encore Flow](https://encore.dev/docs/go/observability/encore-flow). If you have a large application, see advice on how to [structure an app with several systems](https://encore.dev/docs/go/primitives/app-structure#large-applications-with-several-systems). It's simple to integrate Encore applications with pre-existing systems you might have, using APIs and built-in tools like [client generation](https://encore.dev/docs/go/cli/client-generation). ## [Monolith or Microservices](https://encore.dev/docs/go/primitives/app-structure\#monolith-or-microservices) Encore is not opinionated about monoliths vs. microservices. It does however let you build microservices applications with a monolith-style developer experience. For example, you automatically get IDE auto-complete when making [API calls between services](https://encore.dev/docs/go/primitives/api-calls), along with cross-service type-safety. When using Encore Cloud to create an environment on AWS/GCP, Encore enables you to configure if you want to combine multiple services into one process or keep them separate. This can be useful for improved efficiency at smaller scales, and for co-locating services for increased performance. Learn more in the [environments documentation](https://encore.dev/docs/platform/deploy/environments#process-allocation). ## [Creating services](https://encore.dev/docs/go/primitives/app-structure\#creating-services) To create an Encore service, you create a Go package and [define an API](https://encore.dev/docs/go/primitives/defining-apis) within it. When using databases, you add database migrations in a subfolder `migrations` to define the structure of the database(s). Learn more in the [SQL databases docs](https://encore.dev/docs/go/primitives/databases). On disk it might look like this: ```go /my-app β”œβ”€β”€ encore.app // ... and other top-level project files β”‚ β”œβ”€β”€ hello // hello service (a Go package) β”‚Β Β  β”œβ”€β”€ migrations // hello service db migration (directory) β”‚Β Β  β”‚ └── 1_create_table.up.sql // hello service db migration β”‚Β Β  β”œβ”€β”€ hello.go // hello service code β”‚Β Β  └── hello_test.go // tests for hello service β”‚ └── world // world service (a Go package) └── world.go // world service code ``` ##### Related docs [Services](https://encore.dev/docs/go/primitives/services) [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) ## [Structure services using sub-packages](https://encore.dev/docs/go/primitives/app-structure\#structure-services-using-sub-packages) Within a service, it's possible to have multiple sub-packages. This is a good way to define components, helper functions, or other code for your functions, should you wish to do that. You can create as many sub-packages, in any kind of nested structure within your service, as you want. To create sub-packages, you create sub-directories within a service package. Sub-packages are internal to services, they are not themselves service packages. This means sub-packages within services cannot themselves define APIs. You can however define an API in a service package that calls a function within a sub-package. For example, rather than define the entire logic for an endpoint in that endpoint's function, you can call functions from sub-packages and divide the logic in any way you want. **`hello/hello.go`** ```go package hello import ( "context" "encore.app/hello/foo" ) //encore:api public path=/hello/:name func World(ctx context.Context, name string) (*Response, error) { msg := foo.GenerateMessage(name) return &Response{Message: msg}, nil } type Response struct { Message string } ``` **`hello/foo/foo.go`** ```go package foo import ( "fmt" ) func GenerateMessage(name string) string { return fmt.Sprintf("Hello %s!", name) } ``` On disk it might look like this: ```go /my-app β”œβ”€β”€ encore.app // ... and other top-level project files β”‚ β”œβ”€β”€ hello // hello service (a Go package) β”‚Β Β  β”œβ”€β”€ migrations // hello service db migrations (directory) β”‚Β Β  β”‚ └── 1_create_table.up.sql // hello service db migration β”‚Β Β  β”œβ”€β”€ foo // sub-package foo (directory) β”‚Β Β  β”‚ └── foo.go // foo code (cannot define APIs) β”‚Β Β  β”œβ”€β”€ hello.go // hello service code β”‚Β Β  └── hello_test.go // tests for hello service β”‚ └── world // world service (a Go package) └── world.go // world service code ``` ## [Large applications with several systems](https://encore.dev/docs/go/primitives/app-structure\#large-applications-with-several-systems) If you have a large application with several logical domains, each consisting of multiple services, it can be practical to separate these into distinct systems. Systems are not a special construct in Encore, they only help you divide your application logically around common concerns and purposes. Encore only handles services, the compiler will read your systems and extract the services of your application. As applications grow, systems help you decompose your application without requiring any complex refactoring. To create systems, create a sub-directory for each system and put the relevant service packages within it. This is all you need to do, since with Encore each service consists of a Go package. As an example, a company building a Trello app might divide their application into three systems: the **Trello** system (for the end-user facing app with boards and cards), the **User** system (for user and organization management), and the **Premium** system (for handling payments and subscriptions). On disk it might look like this: ```go /my-trello-clone β”œβ”€β”€ encore.app // ... and other top-level project files β”‚ β”œβ”€β”€ trello // trello system (a directory) β”‚Β Β  β”œβ”€β”€ board // board service (a Go package) β”‚Β Β  β”‚ └── board.go // board service code β”‚Β Β  └── card // card service (a Go package) β”‚ └── card.go // card service code β”‚ β”œβ”€β”€ premium // premium system (a directory) β”‚Β Β  β”œβ”€β”€ payment // payment service (a Go package) β”‚Β Β  β”‚ └── payment.go // payment service code β”‚Β Β  └── subscription // subscription service (a Go package) β”‚ └── subscription.go // subscription service code β”‚ └── usr // usr system (a directory) β”œβ”€β”€ org // org service (a Go package) β”‚Β Β  └── org.go // org service code └── user // user service (a Go package) Β Β  └── user.go // user service code ``` The only refactoring needed to divide an existing Encore application into systems is to move services into their respective subfolders. This is a simple way to separate the specific concerns of each system. What matters for Encore are the packages containing services, and the division in systems or subsystems will not change the endpoints or architecture of your application. [Building an Incident Management tool](https://encore.dev/docs/go/tutorials/incident-management-tool) [Services](https://encore.dev/docs/go/primitives/services) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/app-structure.md) ## Microservices with Encore.go Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore.go makes it simple to build applications with one or many services, without needing to manually handle the typical complexity of developing microservices. ## [Defining a service](https://encore.dev/docs/go/primitives/services\#defining-a-service) With Encore.go you define a service by [defining at least one API](https://encore.dev/docs/go/primitives/defining-apis) within a regular Go package. Encore recognizes this as a service, and uses the package name as the service name. On disk it might look like this: ```go /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 ``` This means building a microservices architecture is as simple as creating multiple Go packages within your application. See the [app structure documentation](https://encore.dev/docs/go/primitives/app-structure) for more details. ##### Related example [encoredev/examples/trello-clone](https://github.com/encoredev/examples/tree/main/trello-clone) Simple microservices example application. `$ encore app create --example=trello-clone` ## [Service Initialization](https://encore.dev/docs/go/primitives/services\#service-initialization) Under the hood Encore automatically generates a `main` function that initializes all your infrastructure resources when the application starts up. This means you don't write a `main` function for your Encore application. If you want to customize the initialization behavior of your service, you can define a service struct and define custom initialization logic with that. See the [service struct docs](https://encore.dev/docs/go/primitives/service-structs) for more info. [App Structure](https://encore.dev/docs/go/primitives/app-structure) [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/services.md) ## Defining APIs in Encore Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) - [API Calls](https://encore.dev/docs/go/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) - [Service Structs](https://encore.dev/docs/go/primitives/service-structs) - [API Errors](https://encore.dev/docs/go/primitives/api-errors) - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore.go enables you to create type-safe APIs from regular Go functions. To define an API, add the `//encore:api` annotation to a function in your code. This tells Encore that the function is an API endpoint and Encore will automatically generate the necessary boilerplate at compile-time. In the example below, we define the API endpoint `Ping`, in the `hello` service, which gets exposed as `hello.Ping`. ```go package hello // service name //encore:api public func Ping(ctx context.Context, params *PingParams) (*PingResponse, error) { msg := fmt.Sprintf("Hello, %s!", params.Name) return &PingResponse{Message: msg}, nil } ``` ##### Related example [encoredev/examples/hello-world](https://github.com/encoredev/examples/tree/main/hello-world) Hello World REST API example application. `$ encore app create --example=hello-world` ## [Access controls](https://encore.dev/docs/go/primitives/defining-apis\#access-controls) When you define an API, you have three options for how it can be accessed: - `//encore:api public` – defines a public API that anybody on the internet can call. - `//encore:api private` – defines a private API that is never accessible to the outside world. It can only be called from other services in your app and via cron jobs. - `//encore:api auth` – defines a public API that anybody can call, but requires valid authentication. You can optionally send in auth data to `public` and `private` APIs, in which case the auth handler will be used. When used for `private` APIs, they are still not accessible from the outside world. For more on defining APIs that require authentication, see the [authentication guide](https://encore.dev/docs/go/develop/auth). ## [API Schemas](https://encore.dev/docs/go/primitives/defining-apis\#api-schemas) ### [Request and response schemas](https://encore.dev/docs/go/primitives/defining-apis\#request-and-response-schemas) In the example above we defined an API that uses request and response schemas. The request data is of type `PingParams` and the response data of type `PingResponse`. That means we need to define them like so: ```go package hello // service name // PingParams is the request data for the Ping endpoint. type PingParams struct { Name string } // PingResponse is the response data for the Ping endpoint. type PingResponse struct { Message string } // Ping is an API endpoint that responds with a simple response. // This is exposed as "hello.Ping". //encore:api public func Ping(ctx context.Context, params *PingParams) (*PingResponse, error) { msg := fmt.Sprintf("Hello, %s!", params.Name) return &PingResponse{Message: msg}, nil } ``` Request and response schemas are both optional. There are four different ways of defining an API: **Using both request and response data:** `func Foo(ctx context.Context, p *Params) (*Response, error)` **Only returning a response:** `func Foo(ctx context.Context) (*Response, error)` **With only request data:** `func Foo(ctx context.Context, p *Params) error` **Without any request or response data:** `func Foo(ctx context.Context) error` As you can see, two parts are always present: the `ctx context.Context` parameter and the `error` return value. The `ctx` parameter is used for _cancellation_. It lets you detect when the caller is no longer interested in the result, and lets you abort the request processing and save resources that nobody needs. [Learn more about contexts on the Go blog](https://blog.golang.org/context). The `error` return type is always required because APIs can always fail from the caller's perspective. Therefore even though our simple `Ping` API endpoint above never fails in its implementation, from the perspective of the caller perhaps the service is crashing or the network is down and the service cannot be reached. This approach is simple but very powerful. It lets Encore use [static analysis](https://encore.dev/docs/go/concepts/application-model) to understand the request and response schemas of all your APIs, which enables Encore to automatically generate API documentation, type-safe API clients, and much more. ### [Request and response data types](https://encore.dev/docs/go/primitives/defining-apis\#request-and-response-data-types) Request and response data types are structs (or pointers to structs) with optional field tags, which Encore uses to encode API requests to HTTP messages. The same struct can be used for requests and responses, but the `query` tag is ignored when generating responses. All tags except `json` are ignored for nested tags, which means you can only define `header` and `query` parameters for root level fields. For example, this struct: ```go type NestedRequestResponse struct { Header string `header:"X-Header"`// this field will be read from the http header Query string `query:"query"`// this field will be read from the query string Body1 string `json:"body1"` Nested struct { Header2 string `header:"X-Header2"`// this field will be read from the body Query2 string `query:"query2"`// this field will be read from the body Body2 string `json:"body2"` } `json:"nested"` } ``` Would be unmarshalled from this request: ```output POST /example?query=a%20query HTTP/1.1 Content-Type: application/json X-Header: A header { "body1": "a body", "nested": { "Header2": "not a header", "Query2": "not a query", "body2": "a nested body" } } ``` And marshalled to this response: ```output HTTP/1.1 200 OK Content-Type: application/json X-Header: A header { "Query": "not a query", "body1": "a body", "nested": { "Header2": "not a header", "Query2": "not a query", "body2": "a nested body" } } ``` ### [Path parameters](https://encore.dev/docs/go/primitives/defining-apis\#path-parameters) Path parameters are specified by the `path` field in the `//encore:api` annotation. To specify a placeholder variable, use `:name` and add a function parameter with the same name to the function signature. Encore parses the incoming request URL and makes sure it matches the type of the parameter. The last segment of the path can be parsed as a wildcard parameter by using `*name` with a matching function parameter. ```go // GetBlogPost retrieves a blog post by id. //encore:api public method=GET path=/blog/:id/*path func GetBlogPost(ctx context.Context, id int, path string) (*BlogPost, error) { // Use id to query database... } ``` ### [Fallback routes](https://encore.dev/docs/go/primitives/defining-apis\#fallback-routes) Encore supports defining fallback routes that will be called if no other endpoint matches the request, using the syntax `path=/!fallback`. This is often useful when migrating an existing backend service over to Encore, as it allows you to gradually migrate endpoints over to Encore while routing the remaining endpoints to the existing HTTP router using a raw endpoint with a fallback route. For example: ```go //encore:service type Service struct { oldRouter *gin.Engine // existing HTTP router } // Route all requests to the existing HTTP router if no other endpoint matches. //encore:api public raw path=/!fallback func (s *Service) Fallback(w http.ResponseWriter, req *http.Request) { s.oldRouter.ServeHTTP(w, req) } ``` ### [Headers](https://encore.dev/docs/go/primitives/defining-apis\#headers) Headers are defined by the `header` field tag, which can be used in both request and response data types. The tag name is used to translate between the struct field and http headers. In the example below, the `Language` field of `ListBlogPost` will be fetched from the `Accept-Language` HTTP header. ```go type ListBlogPost struct { Language string `header:"Accept-Language"` Author string // Not a header } ``` ### [Cookies](https://encore.dev/docs/go/primitives/defining-apis\#cookies) Cookies can be set in the response by using the `header` tag with the `Set-Cookie` header name. ```go type LoginResponse struct { SessionID string `header:"Set-Cookie"` } //encore:api public method=POST path=/login func Login(ctx context.Context) (*LoginResponse, error) { return &LoginResponse{SessionID: "session=123"}, nil } ``` The cookies can then be read using e.g. [structured auth data](https://encore.dev/docs/go/develop/auth#accepting-structured-auth-information). ### [Query parameters](https://encore.dev/docs/go/primitives/defining-apis\#query-parameters) For `GET`, `HEAD` and `DELETE` requests, parameters are read from the query string by default. The query parameter name defaults to the [snake-case](https://en.wikipedia.org/wiki/Snake_case) encoded name of the corresponding struct field (e.g. BlogPost becomes blog\_post). The `query` field tag can be used to parse a field from the query string for other HTTP methods (e.g. POST) and to override the default parameter name. Query strings are not supported in HTTP responses and therefore `query` tags in response types are ignored. In the example below, the `PageLimit` field will be read from the `limit` query parameter, whereas the `Author` field will be parsed from the query string (as `author`) only if the method of the request is `GET`, `HEAD` or `DELETE`. ```go type ListBlogPost struct { PageLimit int `query:"limit"` // always a query parameter Author string // query if GET, HEAD or DELETE, otherwise body parameter } ``` When fetching data with `GET` endpoints, it's common to receive additional parameters for optional behavior, like filtering a list or changing the sort order. When you use a struct type as the last argument in the function signature, Encore automatically parses these fields from the HTTP query string (for the `GET`, `HEAD`, and `DELETE` methods). For example, if you want to have a `ListBlogPosts` endpoint: ```go type ListParams struct { Limit uint // number of blog posts to return Offset uint // number of blog posts to skip, for pagination } type ListResponse struct { Posts []*BlogPost } //encore:api public method=GET path=/blog func ListBlogPosts(ctx context.Context, opts *ListParams) (*ListResponse, error) { // Use limit and offset to query database... } ``` This could then be queried as `/blog?limit=10&offset=20`. Query parameters are more limited than structured JSON data, and can only consist of basic types (`string`, `bool`, integer and floating point numbers), [Encore's UUID types](https://pkg.go.dev/encore.dev/types/uuid#UUID), and slices of those types. ### [Body parameters](https://encore.dev/docs/go/primitives/defining-apis\#body-parameters) Encore will default to reading request parameters from the body (as JSON) for all HTTP methods except `GET`, `HEAD` or `DELETE`. The name of the body parameter defaults to the field name, but can be overridden by the `json` tag. Response fields will be serialized as JSON in the HTTP body unless the `header` tag is set. There is no tag to force a field to be read from the body, as some infrastructure entities do not support body content in `GET`, `HEAD` or `DELETE` requests. ```go type CreateBlogPost struct { Subject string `json:"limit"` // query if GET, HEAD or DELETE, otherwise body parameter Author string // query if GET, HEAD or DELETE, otherwise body parameter } ``` ### [Optional types](https://encore.dev/docs/go/primitives/defining-apis\#optional-types) Encore supports optional types using the `option.Option[T]` type from the `encore.dev/types/option` package. This can be used in request and response schemas to indicate that the value is not always set. See the [package documentation](https://pkg.go.dev/encore.dev/types/option) for more information on usage. ### [Supported types](https://encore.dev/docs/go/primitives/defining-apis\#supported-types) The table below lists the data types supported by each HTTP message location. | Type | Header | Path | Query | Body | | --- | --- | --- | --- | --- | | bool | X | X | X | X | | numeric | X | X | X | X | | string | X | X | X | X | | time.Time | X | X | X | X | | uuid.UUID | X | X | X | X | | json.RawMessage | X | X | X | X | | option.Option\[T\] | X | | X | X | | pointer | X | | X | X | | list | X | | X | X | | struct | | | | X | | map | | | | X | ## [Sensitive data](https://encore.dev/docs/go/primitives/defining-apis\#sensitive-data) Encore.go comes with built-in tracing functionality that automatically captures request and response payloads to simplify debugging. While helpful, that's not always desirable. For instance when a request or response payload contains sensitive data, such as API keys or personally identifiable information (PII). For those use cases Encore supports marking a field as sensitive using the struct tag `encore:"sensitive"`. Encore's tracing system will automatically redact fields tagged as sensitive. This works for both individual values as well as nested fields. Note that inputs to [auth handlers](https://encore.dev/docs/go/develop/auth) are automatically marked as sensitive and are always redacted. Raw endpoints lack a schema, which means there's no way to add a struct tag to mark certain data as sensitive. For this reason Encore supports tagging the whole API endpoint as sensitive by adding `sensitive` to the `//encore:api` annotation. This will cause the whole request and response payload to be redacted, including all request and response headers. ##### Please note The `encore:"sensitive"` tag is ignored for local development environments to make development and debugging with the Local Development Dashboard easier. ### [Example](https://encore.dev/docs/go/primitives/defining-apis\#example) ```go package blog // service name import ( "time" "encore.dev/types/uuid" ) type Updates struct { Author string `json:"author,omitempty"` PublishTime time.Time `json:"publish_time,omitempty"` } // BatchUpdateParams is the request data for the BatchUpdate endpoint. type BatchUpdateParams struct { Requester string `header:"X-Requester"` RequestTime time.Time `header:"X-Request-Time"` CurrentAuthor string `query:"author"` Updates *Updates `json:"updates"` MySecretKey string `encore:"sensitive"` } // BatchUpdateResponse is the response data for the BatchUpdate endpoint. type BatchUpdateResponse struct { ServedBy string `header:"X-Served-By"` UpdatedIDs []uuid.UUID `json:"updated_ids"` } //encore:api public method=POST path=/section/:sectionID/posts func BatchUpdate(ctx context.Context, sectionID string, params *BatchUpdateParams) (*BatchUpdateResponse, error) { // Update blog posts for section return &BatchUpdateResponse{ServedBy: hostname, UpdatedIDs: ids}, nil } ``` ## [REST APIs](https://encore.dev/docs/go/primitives/defining-apis\#rest-apis) Encore has support for RESTful APIs and lets you easily define resource-oriented API URLs, parse parameters out of them, and more. To create a REST API, start by defining an endpoint and specify the `method` and `path` fields in the `//encore:api` comment. To specify a placeholder variable, use `:name` and add a function parameter with the same name to the function signature. Encore parses the incoming request URL and makes sure it matches the type of the parameter. For example, if you want to have a `GetBlogPost` endpoint that takes a numeric id as a parameter: ```go // GetBlogPost retrieves a blog post by id. //encore:api public method=GET path=/blog/:id func GetBlogPost(ctx context.Context, id int) (*BlogPost, error) { // Use id to query database... } ``` You can also combine path parameters with body payloads. For example, if you want to have an `UpdateBlogPost` endpoint: ```go // UpdateBlogPost updates an existing blog post by id. //encore:api public method=PUT path=/blog/:id func UpdateBlogPost(ctx context.Context, id int, post *BlogPost) error { // Use `post` to update the blog post with the given id. } ``` ##### Take care You cannot define paths that conflict with each other, including paths where the static part can be mistaken for a parameter, e.g both `/blog` and `/blog/:id` would conflict with `/:username`. As a rule of thumb, try to place path parameters at the end of the path and prefix them with the service name, e.g: ```sql GET /blog/posts GET /blog/posts/:id GET /user/profile/:username GET /user/me ``` ## [Custom HTTP status codes](https://encore.dev/docs/go/primitives/defining-apis\#custom-http-status-codes) By default, Encore automatically sets appropriate HTTP status codes for your API responses. We recommend using these default status codes, but there are situations where you might need to set a custom HTTP status code, such as when porting an existing API that clients depend on for specific status codes. To set a custom HTTP status code, use the `encore:"httpstatus"` struct tag on a field in your response type: ```go type Response struct { Message string `json:"message"` Status int `encore:"httpstatus"` } //encore:api public method=GET path=/example func Example(ctx context.Context) (*Response, error) { return &Response{ Message: "Hello", Status: 201, // HTTP 201 Created }, nil } ``` The field with the `encore:"httpstatus"` tag can be an integer type and should contain a valid HTTP status code value. [Services](https://encore.dev/docs/go/primitives/services) [API Calls](https://encore.dev/docs/go/primitives/api-calls) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/defining-apis.md) ## Encore API Calls Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) - [API Calls](https://encore.dev/docs/go/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) - [Service Structs](https://encore.dev/docs/go/primitives/service-structs) - [API Errors](https://encore.dev/docs/go/primitives/api-errors) - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Calling an API endpoint looks like a regular function call with Encore.go. To call an endpoint you first import the other service as a Go package using `import "encore.app/package-name"` and then call the API endpoint like a regular function. Encore will 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 } ``` ##### Related example [encoredev/examples/trello-clone](https://github.com/encoredev/examples/tree/main/trello-clone) Simple microservices example application with service-to-service API calls. `$ encore app create --example=trello-clone` This means your development workflow is as simple as building a monolith, even if you use multiple services. You also 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 building your application, Encore uses [static analysis](https://encore.dev/docs/go/concepts/application-model) to parse all API calls and compiles them to proper API calls. ## [Current Request](https://encore.dev/docs/go/primitives/api-calls\#current-request) By using Encore's [current request API](https://pkg.go.dev/encore.dev/#Request) you can get meta-information about the current request. Including the type of request, the time the request started, the service and endpoint called and the path which was called on the service. For more information, see the [metadata documentation](https://encore.dev/docs/go/develop/metadata). [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) [Raw Endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/api-calls.md) ## Raw Endpoints Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) - [API Calls](https://encore.dev/docs/go/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) - [Service Structs](https://encore.dev/docs/go/primitives/service-structs) - [API Errors](https://encore.dev/docs/go/primitives/api-errors) - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Sometimes you need to operate a lower abstraction than Encore.go normally provides. For example, you might want to access the underlying HTTP request, often useful for things like accepting webhooks. Encore.go has you covered using "raw endpoints". To define a raw endpoint, change the `//encore:api` annotation and function signature like so: ```go package service import "net/http" // Webhook receives incoming webhooks from Some Service That Sends Webhooks. //encore:api public raw func Webhook(w http.ResponseWriter, req *http.Request) { // ... operate on the raw HTTP request ... } ``` Like any other Encore API endpoint, once deployed this will be exposed at the URL: `https://-.encr.app/service.Webhook`. Just like regular endpoints, raw endpoints support the use of `:id` and `*wildcard` segments. Experienced Go developers will have already noted this is just a regular Go HTTP handler. (See the [net/http documentation](https://pkg.go.dev/net/http#Handler) for how Go HTTP handlers work.) Learn more about receiving webhooks and using WebSockets in the [receiving regular HTTP requests guide](https://encore.dev/docs/go/how-to/http-requests). ##### Related example [encoredev/examples/slack-bot](https://github.com/encoredev/examples/tree/main/slack-bot) Slack Bot example application that uses Raw endpoints to accept webhooks. `$ encore app create --example=slack-bot` [API Calls](https://encore.dev/docs/go/primitives/api-calls) [Service Structs](https://encore.dev/docs/go/primitives/service-structs) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/raw-endpoints.md) ## Service Structs Overview Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) - [API Calls](https://encore.dev/docs/go/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) - [Service Structs](https://encore.dev/docs/go/primitives/service-structs) - [API Errors](https://encore.dev/docs/go/primitives/api-errors) - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore lets you define a type, called a service struct, to represent your running service. This lets you define an initialization function (similar to the `main` function in regular Go programs). You can also define API endpoints as methods on the service struct type, enabling you to use [dependency injection](https://encore.dev/docs/go/how-to/dependency-injection) for testing purposes. It works by defining a struct type of your choice (typically called `Service`) and declaring it with `//encore:service`. Then, you can define a special function named `initService` (or `initWhatever` if you named the type `Whatever`) that gets called by Encore to initialize your service when it starts up. It looks like this: ```go //encore:service type Service struct { // Add your dependencies here } func initService() (*Service, error) { // Write your service initialization code here. } //encore:api public func (s *Service) MyAPI(ctx context.Context) error { // ... } ``` ##### Related example [encoredev/examples/uptime](https://github.com/encoredev/examples/tree/main/uptime) Event-driven example application using service structs. `$ encore app create --example=uptime` ## [Calling APIs defined on service structs](https://encore.dev/docs/go/primitives/service-structs\#calling-apis-defined-on-service-structs) When using a service struct like above, Encore will create a file named `encore.gen.go` in your service directory. This file contains package-level functions for the APIs defined as methods on the service struct. In the example above, you would see: ```go // Code generated by encore. DO NOT EDIT. package email import "context" // These functions are automatically generated and maintained by Encore // to simplify calling them from other services, as they were implemented as methods. // They are automatically updated by Encore whenever your API endpoints change. func Send(ctx context.Context, p *SendParams) error { // The implementation is elided here, and generated at compile-time by Encore. return nil } ``` These functions are generated in order to allow other services to keep calling your APIs as package-level functions, in the same way as before: `email.Send(...)`. This means other services do not need to care about whether you're using Dependency Injection internally. You must always use these generated package-level functions for making API calls. ##### Please note Encore will automatically generate these files and keep them up to date whenever your code changes. There is no need to manually invoke anything to regenerate this code. Encore adds all `encore.gen.go` files to your `.gitignore` since you typically don't want to commit them to your repository; doing so ends up creating a lot of unnecessary merge conflicts. However, in some cases when running third-party linters in a CI/CD environment it can be helpful to generate these wrappers to make the linter happy. You can do that by invoking `encore gen wrappers`. ## [Graceful Shutdown](https://encore.dev/docs/go/primitives/service-structs\#graceful-shutdown) When defining a service struct, Encore supports notifying your service when it's time to gracefully shut down. This works by having your service struct implement the method `func (s *Service) Shutdown(force context.Context)`. If that method exists, Encore will call it when it's time to begin gracefully shutting down. Initially the shutdown is in "graceful mode", which means that you have a few seconds to complete ongoing work. The provided `force` context is canceled when the graceful shutdown window is over, and it's time to forcefully shut down. How much time you have from when `Shutdown` is called to when forceful shutdown begins depends on the cloud provider and the underlying infrastructure. Typically it's in the range 5-30 seconds. ##### Please note Encore automatically handles graceful shutdown of all Encore-managed functionality, such as HTTP servers, database connection pools, Pub/Sub message receivers, distributed tracing recorders, and so on. The graceful shutdown functionality is provided if you have additional, non-Encore-related resources that need graceful shutdown. Note that graceful shutdown in Encore is _cooperative_: Encore will wait indefinitely for your `Shutdown` method to return. If your `Shutdown` method does not return promptly after the `force` context is closed, the underlying infrastructure at your cloud provider will typically force-kill your service, which can lead to lingering connections and other such issues. In summary, when your `Shutdown(force context.Context)` function is called: - Immediately begin gracefully shutting down - When the `force` context is canceled, you should forcefully shut down the resources that haven't yet completed their shutdown - Wait until the shutdown is complete before returning from the `Shutdown` function [Raw Endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) [API Errors](https://encore.dev/docs/go/primitives/api-errors) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/service-structs.md) ## API Error Handling Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/go/primitives/defining-apis) - [API Calls](https://encore.dev/docs/go/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) - [Service Structs](https://encore.dev/docs/go/primitives/service-structs) - [API Errors](https://encore.dev/docs/go/primitives/api-errors) - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore supports returning structured error information from your APIs using the [encore.dev/beta/errs](https://pkg.go.dev/encore.dev/beta/errs) package. Errors are propagated across the network to the [generated clients](https://encore.dev/docs/go/cli/client-generation) and can be used within your front-ends without having to build any custom marshalling code. ## [The errs.Error type](https://encore.dev/docs/go/primitives/api-errors\#the-errserror-type) Structured errors are represented by the `errs.Error` type: ```go type Error struct { // Code is the error code to return. Code ErrCode `json:"code"` // Message is a descriptive message of the error. Message string `json:"message"` // Details are user-defined additional details. Details ErrDetails `json:"details"` // Meta are arbitrary key-value pairs for use within // the Encore application. They are not exposed to external clients. Meta Metadata `json:"-"` } ``` Returning an `*errs.Error` from an Encore API endpoint will result in Encore serializing this struct to JSON and returning it in the response. Additionally Encore will set the HTTP status code to match the error code (see the mapping table below). For example: ```go return &errs.Error{ Code: errs.NotFound, Message: "sprocket not found", } ``` Causes Encore to respond with a `HTTP 404` error with body: ```json { "code": "not_found", "message": "sprocket not found", "details": null } ``` ## [Error Wrapping](https://encore.dev/docs/go/primitives/api-errors\#error-wrapping) Encore applications are encouraged to always use the `errs` package to manipulate errors. It supports wrapping errors to gradually add more error information, and lets you easily define both structured error details to return to external clients, as well as internal key-value metadata for debugging and error handling. ```go func Wrap(err error, msg string, metaPairs ...interface{}) error ``` Use `errs.Wrap` to conveniently wrap an error, adding additional context and converting it to an `*errs.Error`. If `err` is nil it returns `nil`. If `err` is already an `*errs.Error` it copies the Code, Details, and Meta fields over. The variadic `metaPairs` parameter must be key-value pairs, where the key is always a `string` and the value can be any built-in type. Existing key-value pairs from the `err` are merged into the new `*Error`. ```go func WrapCode(err error, code ErrCode, msg string, metaPairs ...interface{}) error ``` `errs.WrapCode` is like `errs.Wrap` but also sets the error code. ```go func Convert(err error) error ``` `errs.Convert` converts an error to an `*errs.Error`. If the error is already an `*errs.Error` it returns it unmodified. If `err` is nil it returns nil. ## [Error Codes](https://encore.dev/docs/go/primitives/api-errors\#error-codes) The `errs` package defines error codes for common error scenarios. They are identical to the codes defined by `gRPC` for interoperability. The table below summarizes the error codes. You can find additional documentation about when to use them in the [package documentation](https://pkg.go.dev/encore.dev/beta/errs#ErrCode). | Code | String | HTTP Status | | --- | --- | --- | | `OK` | `"ok"` | 200 OK | | `Canceled` | `"canceled"` | 499 Client Closed Request | | `Unknown` | `"unknown"` | 500 Internal Server Error | | `InvalidArgument` | `"invalid_argument"` | 400 Bad Request | | `DeadlineExceeded` | `"deadline_exceeded"` | 504 Gateway Timeout | | `NotFound` | `"not_found"` | 404 Not Found | | `AlreadyExists` | `"already_exists"` | 409 Conflict | | `PermissionDenied` | `"permission_denied"` | 403 Forbidden | | `ResourceExhausted` | `"resource_exhausted"` | 429 Too Many Requests | | `FailedPrecondition` | `"failed_precondition"` | 400 Bad Request | | `Aborted` | `"aborted"` | 409 Conflict | | `OutOfRange` | `"out_of_range"` | 400 Bad Request | | `Unimplemented` | `"unimplemented"` | 501 Not Implemented | | `Internal` | `"internal"` | 500 Internal Server Error | | `Unavailable` | `"unavailable"` | 503 Unavailable | | `DataLoss` | `"data_loss"` | 500 Internal Server Error | | `Unauthenticated` | `"unauthenticated"` | 401 Unauthorized | ## [Error Building](https://encore.dev/docs/go/primitives/api-errors\#error-building) In cases where you have complex business logic, or multiple error returns, it's convenient to gradually add metadata to your error. For this purpose Encore provides `errs.Builder`. The builder lets you gradually set aspects of the error, using a chaining API design. Use `errs.B()` to get a new builder that you can start chaining with directly. When you want to return the constructed error call the `.Err()`method. For example: ```go func getBoard(ctx context.Context, boardID int64) (*Board, error) { // Construct a new error builder with errs.B() eb := errs.B().Meta("board_id", params.ID) b := &Board{ID: params.ID} err := sqldb.QueryRow(ctx, ` SELECT name, created FROM board WHERE id = $1 `, params.ID).Scan(&b.Name, &b.Created) if errors.Is(err, sqldb.ErrNoRows) { // Return a "board not found" error with code == NotFound return nil, eb.Code(errs.NotFound).Msg("board not found").Err() } else if err != nil { // Return a general error return nil, eb.Cause(err).Msg("could not get board").Err() } // ... } ``` ## [Inspecting API Errors](https://encore.dev/docs/go/primitives/api-errors\#inspecting-api-errors) When you call another API within Encore, the returned errors are always wrapped in `*errs.Error`. You can inspect the error information either by casting to `*errs.Error`, or using the below helper methods. ```go func Code(err error) ErrCode ``` `errs.Code` returns the error code. If the error was not an `*errs.Error` it returns `errs.Unknown`. ```go func Meta(err error) Metadata type Metadata map[string]interface{} ``` `errs.Meta` returns any structured metadata present in the error. If the error was not an `*errs.Error` it returns nil. Unlike when you return error information to external clients, all the metadata is sent to the calling service, making debugging even easier. ```go func Details(err error) ErrDetails ``` `errs.Details` returns the structured error details. If the error was not an `*errs.Error` or the error lacked details, it returns nil. [Service Structs](https://encore.dev/docs/go/primitives/service-structs) [Using SQL databases](https://encore.dev/docs/go/primitives/databases) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/api-errors.md) ## Encore Database Management Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Using SQL databases](https://encore.dev/docs/go/primitives/databases) - [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) - [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) - [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) - [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) - [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) - [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore treats SQL databases as logical resources and natively supports **PostgreSQL** databases. ## [Creating a database](https://encore.dev/docs/go/primitives/databases\#creating-a-database) To create a database, import `encore.dev/storage/sqldb` and call `sqldb.NewDatabase`, assigning the result to a package-level variable. Databases must be created from within an [Encore service](https://encore.dev/docs/go/primitives/services). For example: ```go todo/db.go todo/migrations/1_create_table.up.sql package todo // Create the todo database and assign it to the "tododb" variable var tododb = sqldb.NewDatabase("todo", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // Then, query the database using db.QueryRow, db.Exec, etc. ``` As seen above, the `sqldb.DatabaseConfig` specifies the directory containing the database migration files, which is how you define the database schema. See the [Defining the database schema](https://encore.dev/docs/go/primitives/databases#defining-the-database-schema) section below for more details. With this code in place, Encore will automatically create the database using [Docker](https://docker.com/) when you run the command `encore run` in your local environment. Make sure Docker is installed and running on your machine before running `encore run`. ##### Please note If your application is already running when you define a new database, you will need to stop and restart `encore run`. This is necessary for Encore to create the new database using Docker. ##### Related example [encoredev/examples/sql-database](https://github.com/encoredev/examples/tree/main/sql-database) Simple PostgreSQL example application. `$ encore app create --example=sql-database` ## [Database Migrations](https://encore.dev/docs/go/primitives/databases\#database-migrations) Encore automatically handles `up` migrations, while `down` migrations must be run manually. Each `up` migration runs sequentially, expressing changes in the database schema from the previous migration. ### [Naming Conventions](https://encore.dev/docs/go/primitives/databases\#naming-conventions) **File Name Format:** Migration files must start with a number followed by an underscore (`_`), and must increase sequentially. Each file name must end with `.up.sql`. **Examples:** - `1_first_migration.up.sql` - `2_second_migration.up.sql` - `3_migration_name.up.sql` You can also prefix migration files with leading zeroes for better ordering in the editor (e.g., `0001_migration.up.sql`). ### [Defining the Database Schema](https://encore.dev/docs/go/primitives/databases\#defining-the-database-schema) The first migration typically defines the initial table structure. For instance, a `todo` service might create `todo/migrations/1_create_table.up.sql` with the following content: ```sql CREATE TABLE todo_item ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, done BOOLEAN NOT NULL DEFAULT false ); ``` ### [Migration File Structure](https://encore.dev/docs/go/primitives/databases\#migration-file-structure) Migration files are created in a `migrations` directory within an Encore service package. Each file is named `_.up.sql`, where `` is a sequence number for ordering and `` describes the migration. **Example Directory Structure:** ```go /my-app β”œβ”€β”€ encore.app // ... and other top-level project files β”‚ └── todo // todo service (a Go package) Β Β  β”œβ”€β”€ migrations // todo service db migrations (directory) Β Β  β”‚ β”œβ”€β”€ 1_create_table.up.sql // todo service db migration Β Β  β”‚ └── 2_add_field.up.sql // todo service db migration Β Β  β”œβ”€β”€ todo.go // todo service code Β Β  └── todo_test.go // tests for todo service ``` ## [Inserting data into databases](https://encore.dev/docs/go/primitives/databases\#inserting-data-into-databases) Once you have created the database using `var mydb = sqldb.NewDatabase(...)` you can start inserting data into the database by calling methods on the `mydb` variable. The interface is similar to that of the Go standard library's `database/sql` package. Learn more in the [package docs](https://pkg.go.dev/encore.dev/storage/sqldb). One way of inserting data is with a helper function that uses the package function `sqldb.Exec`. For example, to insert a single todo item using the example schema above, we can use the following helper function `insert`: ```go todo/insert.go todo/db.go todo/migrations/1_create_table.up.sql // insert inserts a todo item into the database. func insert(ctx context.Context, id, title string, done bool) error { _, err := tododb.Exec(ctx, ` INSERT INTO todo_item (id, title, done) VALUES ($1, $2, $3) `, id, title, done) return err } ``` ## [Querying databases](https://encore.dev/docs/go/primitives/databases\#querying-databases) To query a database in your application, you similarly need to import `encore.dev/storage/sqldb` in your service package or sub-package. For example, to read a single todo item in the example schema above, we can use `sqldb.QueryRow`: ```go var item struct { ID int64 Title string Done bool } err := tododb.QueryRow(ctx, ` SELECT id, title, done FROM todo_item LIMIT 1 `).Scan(&item.ID, &item.Title, &item.Done) ``` If `QueryRow` does not find a matching row, it reports an error that can be checked against by importing the standard library `errors` package and calling `errors.Is(err, sqldb.ErrNoRows)`. Learn more in the [package docs](https://pkg.go.dev/encore.dev/storage/sqldb). ## [Provisioning databases](https://encore.dev/docs/go/primitives/databases\#provisioning-databases) Encore automatically provisions databases to match what your application requires. When you [define a database](https://encore.dev/docs/go/primitives/databases#creating-a-database), Encore will provision the database at your next deployment. Encore provisions databases in an appropriate way depending on the environment. When running locally, Encore creates a database cluster using [Docker](https://www.docker.com/). In the cloud, it depends on the [environment type](https://encore.dev/docs/platform/deploy/environments#environment-types): - In `production` environments, the database is provisioned through the Managed SQL Database service offered by the chosen cloud provider. - In `development` environments, the database is provisioned as a Kubernetes deployment with a persistent disk attached. See exactly what is provisioned for each cloud provider, and each environment type, in the [infrastructure documentation](https://encore.dev/docs/platform/infrastructure/infra). ## [Connecting to databases](https://encore.dev/docs/go/primitives/databases\#connecting-to-databases) It's often useful to be able to connect to the database from outside the backend application. For example for scripts, ad-hoc querying, or dumping data for analysis. Currently Encore does not expose user credentials for databases in the local environment or for environments on Encore Cloud. You can use a connection string to connect instead, see below. ### [Using the Encore CLI](https://encore.dev/docs/go/primitives/databases\#using-the-encore-cli) Encore's CLI comes with built-in support for connecting to databases: - `encore db shell [--env=]` opens a [psql](https://www.postgresql.org/docs/current/app-psql.html) shell to the database named `` in the given environment. Leaving out `--env` defaults to the local development environment. `encore db shell` defaults to read-only permissions. Use `--write`, `--admin` and `--superuser` flags to modify which permissions you connect with. - `encore db conn-uri [--env=]` outputs a connection string for the database named ``. When specifying a cloud environment, the connection string is temporary. Leaving out `--env` defaults to the local development environment. - `encore db proxy [--env=]` sets up a local proxy that forwards any incoming connection to the databases in the specified environment. Leaving out `--env` defaults to the local development environment. See `encore help db` for more information on database management commands. ### [Using database user credentials](https://encore.dev/docs/go/primitives/databases\#using-database-user-credentials) For cloud environments on AWS/GCP you can view database user credentials (created by Encore when provisioning databases) via the Encore Cloud dashboard: - Open your app in the [Encore Cloud dashboard](https://app.encore.cloud/), navigate to the **Infrastructure** page for the appropriate environment, and locate the `USERS` section within the relevant **Database Cluster**. ## [Handling migration errors](https://encore.dev/docs/go/primitives/databases\#handling-migration-errors) When Encore applies database migrations, there's always a possibility the migrations don't apply cleanly. This can happen for many reasons: - There's a problem with the SQL syntax in the migration - You tried to add a `UNIQUE` constraint but the values in the table aren't actually unique - The existing database schema didn't look like you thought it did, so the database object you tried to change doesn't actually exist - ... and so on If that happens, Encore rolls back the migration. If it happens during a cloud deployment, the deployment is aborted. Once you fix the problem, re-run `encore run` (locally) or push the updated code (in the cloud) to try again. Encore tracks which migrations have been applied in the `schema_migrations` table: ```sql database=# \d schema_migrations Table "public.schema_migrations" Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- version | bigint | | not null | dirty | boolean | | not null | Indexes: "schema_migrations_pkey" PRIMARY KEY, btree (version) ``` The `version` column tracks which migration was last applied. If you wish to skip a migration or re-run a migration, change the value in this column. For example, to re-run the last migration, run `UPDATE schema_migrations SET version = version - 1;`. _Note that Encore does not use the `dirty` flag by default._ [API Errors](https://encore.dev/docs/go/primitives/api-errors) [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/databases.md) ## Change Database Schema Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Using SQL databases](https://encore.dev/docs/go/primitives/databases) - [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) - [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) - [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) - [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) - [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) - [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore database schemas are changed over time using _migration files_. Each migration file has a sequence number, and migration files are run in sequence when deploying. Encore tracks which migrations have already run and only runs new ones. To change your database schema, add a new migration file using the next available migration number. For example, if you have two migration files already, the next migration file should be named `3_something.up.sql` where `something` is a short description of what the migration does. ##### Look out! Database migrations are applied before the application is restarted with the new code. Always make sure the old application code works with the new database schema, so that things don't break while your new code is being rolled out. ## [Example](https://encore.dev/docs/go/primitives/change-db-schema\#example) Let's say you have a single migration file that creates a `todo_item` table: **`todo/migrations/1_create_table.up.sql`** ```sql CREATE TABLE todo_item ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, done BOOLEAN NOT NULL ); ``` And now you want to add a `created` column to track when each todo was created. Add a new file: **`todo/migrations/2_add_created_col.up.sql`** ```sql ALTER TABLE todo_item ADD created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(); ``` The next deploy Encore will notice the new migration file and run it, adding a new column. [Using SQL databases](https://encore.dev/docs/go/primitives/databases) [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/change-db-schema.md) ## Connect Existing Database Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Using SQL databases](https://encore.dev/docs/go/primitives/databases) - [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) - [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) - [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) - [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) - [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) - [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore automatically provision the necessary infrastructure when you create a service and add a database. However, you may want to connect to an existing database for migration or prototyping purposes. It's simple to integrate your Encore app with an existing database in these cases. ## [Example](https://encore.dev/docs/go/primitives/connect-existing-db\#example) Let's say you have an external database hosted by DigitalOcean that you would like to connect to. The simplest approach is to create a dedicated package that lazily instantiates a database connection pool. We can store the password using Encore's [secrets manager](https://encore.dev/docs/go/primitives/secrets) to make it even easier. The connection string is something that looks like: ```perl postgresql://user:password@externaldb-do-user-1234567-0.db.ondigitalocean.com:25010/externaldb?sslmode=require ``` So we write something like: **`pkg/externaldb/externaldb.go`** ```go package externaldb import ( "context" "fmt" "github.com/jackc/pgx/v4/pgxpool" "go4.org/syncutil" ) // Get returns a database connection pool to the external database. // It is lazily created on first use. func Get(ctx context.Context) (*pgxpool.Pool, error) { // Attempt to setup the database connection pool if it hasn't // already been successfully setup. err := once.Do(func() error { var err error pool, err = setup(ctx) return err }) return pool, err } var ( // once is like sync.Once except it re-arms itself on failure once syncutil.Once // pool is the successfully created database connection pool, // or nil when no such pool has been setup yet. pool *pgxpool.Pool ) var secrets struct { // ExternalDBPassword is the database password for authenticating // with the external database hosted on DigitalOcean. ExternalDBPassword string } // setup attempts to set up a database connection pool. func setup(ctx context.Context) (*pgxpool.Pool, error) { connString := fmt.Sprintf("postgresql://%s:%s@externaldb-do-user-1234567-0.db.ondigitalocean.com:25010/externaldb?sslmode=require", "user", secrets.ExternalDBPassword) return pgxpool.Connect(ctx, connString) } ``` Before running, remember to use `encore secrets set` to store the `ExternalDBPassword` to use. (But don't worry, Encore will remind you if you forget.) ## [Other infrastructure](https://encore.dev/docs/go/primitives/connect-existing-db\#other-infrastructure) The same pattern can easily be adapted to other infrastructure components that Encore doesn't yet provide built-in support for: - Horizontally scalable databases like Cassandra, DynamoDB, BigTable, and so on - Document or graph databases like MongoDB or Neo4j - Other cloud primitives like queues, object storage buckets, and more - Or really any cloud services or APIs you can think of In this way you can easily integrate Encore with anything you want. [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/connect-existing-db.md) ## Insert Test Data Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Using SQL databases](https://encore.dev/docs/go/primitives/databases) - [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) - [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) - [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) - [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) - [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) - [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) When you're developing or testing, it's often useful to seed databases with test data. This can be done is several ways depending on your use case. ## [Using go:embed](https://encore.dev/docs/go/primitives/insert-test-data-db\#using-goembed) A straightforward way to insert test data is to conditionally insert it on startup using `go:embed` in combination with Encore's [metadata API](https://encore.dev/docs/go/develop/metadata) control in which environments the data gets inserted. E.g. only in your local environment. ### [Example](https://encore.dev/docs/go/primitives/insert-test-data-db\#example) Create a file with your test data named `fixtures.sql`. Then, for the service where you want to insert test data, add the following to its `.go` file in order to run on startup. ```go import ( _ "embed" "log" "encore.dev" ) //go:embed fixtures.sql var fixtures string func init() { if encore.Meta().Environment.Cloud == encore.CloudLocal { if _, err := sqldb.Exec(context.Background(), fixtures); err != nil { log.Fatalln("unable to add fixtures:", err) } } } ``` Not included in the above example is preventing adding duplicate data. This is straightforward to do by making the fixtures idempotent, or by tracking it with a database table. ## [Populating databases in Encore Cloud's Preview Environments](https://encore.dev/docs/go/primitives/insert-test-data-db\#populating-databases-in-encore-clouds-preview-environments) If you are using Encore Cloud's Preview Environment, it can sometimes be useful to populate new Preview Environments with test data to simplify testing. The best way to do this depends a bit on your use case, but a common way to do this is by using Encore's [webhooks](https://encore.dev/docs/platform/integrations/webhooks) functionality, which provides notifications for when a deployment is completed and includes information about the environment in question. [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/insert-test-data-db.md) ## Share Databases Between Services Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Using SQL databases](https://encore.dev/docs/go/primitives/databases) - [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) - [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) - [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) - [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) - [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) - [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) By default, each service in an Encore app has its own database. This approach has many benefits: - Which database is used and how it works is abstracted away from other services - The database is more isolated, making changes to it smaller and safer - By making the services more independent your application becomes more reliable by being able to more gracefully handle partial outages, such as if your database is temporarily overloaded or offline. But like everything else in software engineering, there are trade-offs involved, and sometimes it's simpler and more reliable to use a single database that's accessed by multiple services. Encore makes this easy to do. Each database in Encore is defined within a service. That service's name becomes the name of the database. Other services can then access that database by creating a database reference with `sqldb.Named("dbname")`. ## [Example](https://encore.dev/docs/go/primitives/share-db-between-services\#example) Let's say you have a simple `todo` service, with only one table: **`todo/migrations/1_create_table.up.sql`** ```sql CREATE TABLE todo_item ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, done BOOLEAN NOT NULL DEFAULT FALSE ); ``` You want to create a `report` service that produces various reports for internal business processes, but for simplicity you decide it makes sense to directly access the `todo` database. All that's needed is to define the `todoDB` variable like so: **`report/report.go`** ```go package report import ( "context" "encore.dev/storage/sqldb" ) // todoDB connects to the "todo" service's database. var todoDB = sqldb.Named("todo") type ReportResponse struct { Total int } // CountCompletedTodos generates a report with the number of completed todo items. //encore:api method=GET path=/report/todo func CountCompletedTodos(ctx context.Context) (*ReportResponse, error) { var report ReportResponse err := todoDB.QueryRow(ctx,` SELECT COUNT(*) FROM todo_item WHERE completed = TRUE `).Scan(&report.Total) return &report, err } ``` With that, Encore understands that the `report` service depends on the `todo` service's database, and orchestrates the necessary connections to make that happen. And like everything else with Encore, it works exactly the same regardless of where it's running: for local development as well as in the cloud. [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/share-db-between-services.md) ## PostgreSQL Extensions Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Using SQL databases](https://encore.dev/docs/go/primitives/databases) - [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) - [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) - [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) - [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) - [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) - [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore uses the [encoredotdev/postgres](https://github.com/encoredev/postgres-image) docker image for local development, CI/CD, and for databases hosted on Encore Cloud. The docker image ships with the following PostgreSQL extensions pre-installed and available for use (via `CREATE EXTENSION`): | Extension | Version | Description | | --- | --- | --- | | refint | 1.0 | functions for implementing referential integrity (obsolete) | | pg\_buffercache | 1.3 | examine the shared buffer cache | | pg\_freespacemap | 1.2 | examine the free space map (FSM) | | plpgsql | 1.0 | PL/pgSQL procedural language | | citext | 1.6 | data type for case-insensitive character strings | | adminpack | 2.1 | administrative functions for PostgreSQL | | moddatetime | 1.0 | functions for tracking last modification time | | amcheck | 1.3 | functions for verifying relation integrity | | seg | 1.4 | data type for representing line segments or floating-point intervals | | pg\_stat\_statements | 1.10 | track planning and execution statistics of all SQL statements executed | | pg\_trgm | 1.6 | text similarity measurement and index searching based on trigrams | | isn | 1.2 | data types for international product numbering standards | | btree\_gist | 1.7 | support for indexing common datatypes in GiST | | intarray | 1.5 | functions, operators, and index support for 1-D arrays of integers | | pg\_surgery | 1.0 | extension to perform surgery on a damaged relation | | uuid-ossp | 1.1 | generate universally unique identifiers (UUIDs) | | insert\_username | 1.0 | functions for tracking who changed a table | | bloom | 1.0 | bloom access method - signature file based index | | pgcrypto | 1.3 | cryptographic functions | | dblink | 1.2 | connect to other PostgreSQL databases from within a database | | tsm\_system\_rows | 1.0 | TABLESAMPLE method which accepts number of rows as a limit | | pg\_prewarm | 1.2 | prewarm relation data | | old\_snapshot | 1.0 | utilities in support of old\_snapshot\_threshold | | pageinspect | 1.11 | inspect the contents of database pages at a low level | | intagg | 1.1 | integer aggregator and enumerator (obsolete) | | pg\_visibility | 1.2 | examine the visibility map (VM) and page-level visibility info | | cube | 1.5 | data type for multidimensional cubes | | tablefunc | 1.0 | functions that manipulate whole tables, including crosstab | | xml2 | 1.1 | XPath querying and XSLT | | fuzzystrmatch | 1.1 | determine similarities and distance between strings | | pg\_walinspect | 1.0 | functions to inspect contents of PostgreSQL Write-Ahead Log | | btree\_gin | 1.3 | support for indexing common datatypes in GIN | | sslinfo | 1.2 | information about SSL certificates | | tcn | 1.0 | Triggered change notifications | | hstore | 1.8 | data type for storing sets of (key, value) pairs | | dict\_int | 1.0 | text search dictionary template for integers | | earthdistance | 1.1 | calculate great-circle distances on the surface of the Earth | | file\_fdw | 1.0 | foreign-data wrapper for flat file access | | autoinc | 1.0 | functions for autoincrementing fields | | ltree | 1.2 | data type for hierarchical tree-like structures | | unaccent | 1.1 | text search dictionary that removes accents | | pgrowlocks | 1.2 | show row-level locking information | | tsm\_system\_time | 1.0 | TABLESAMPLE method which accepts time in milliseconds as a limit | | dict\_xsyn | 1.0 | text search dictionary template for extended synonym processing | | pgstattuple | 1.5 | show tuple-level statistics | | postgres\_fdw | 1.1 | foreign-data wrapper for remote PostgreSQL servers | | lo | 1.1 | Large Object maintenance | | postgis\_sfcgal-3 | 3.4.2 | PostGIS SFCGAL functions | | address\_standardizer\_data\_us-3 | 3.4.2 | Address Standardizer US dataset example | | address\_standardizer-3 | 3.4.2 | Used to parse an address into constituent elements. Generally used to support geocoding address normalization step. | | postgis\_topology-3 | 3.4.2 | PostGIS topology spatial types and functions | | postgis-3 | 3.4.2 | PostGIS geometry and geography spatial types and functions | | postgis\_raster-3 | 3.4.2 | PostGIS raster types and functions | | postgis\_tiger\_geocoder-3 | 3.4.2 | PostGIS tiger geocoder and reverse geocoder | | vector | 0.7.0 | vector data type and ivfflat and hnsw access methods | | postgis | 3.4.2 | PostGIS geometry and geography spatial types and functions | | address\_standardizer | 3.4.2 | Used to parse an address into constituent elements. Generally used to support geocoding address normalization step. | | postgis\_topology | 3.4.2 | PostGIS topology spatial types and functions | | postgis\_tiger\_geocoder | 3.4.2 | PostGIS tiger geocoder and reverse geocoder | | address\_standardizer\_data\_us | 3.4.2 | Address Standardizer US dataset example | | postgis\_sfcgal | 3.4.2 | PostGIS SFCGAL functions | | postgis\_raster | 3.4.2 | PostGIS raster types and functions | [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/database-extensions.md) ## Database Troubleshooting Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Using SQL databases](https://encore.dev/docs/go/primitives/databases) - [Change SQL database schema](https://encore.dev/docs/go/primitives/change-db-schema) - [Integrate with existing databases](https://encore.dev/docs/go/primitives/connect-existing-db) - [Insert test data in a database](https://encore.dev/docs/go/primitives/insert-test-data-db) - [Share databases between services](https://encore.dev/docs/go/primitives/share-db-between-services) - [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) - [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) When you run your application locally with `encore run`, Encore provisions local databases using [Docker](https://docker.com/). If this fails with a database error, it can often be resolved by making sure you have Docker installed and running, or by restarting the Encore daemon using `encore daemon`. If this does not resolve the issue, here are steps to resolve common errors: **Error: sqldb: unknown database** This error is often caused by a problem with the initial migration file, such as incorrect naming or location. - Verify that you've [created the migration file](https://encore.dev/docs/go/primitives/databases#defining-a-database-schema) correctly, then try `encore run` again. **Error: could not connect to the database** When you can't connect to the database in your local environment, there's likely an issue with Docker: - Make sure that you have [Docker](https://docker.com/) installed and running, then try `encore run` again. - If this fails, restart the Encore daemon by running `encore daemon`, then try `encore run` again. **Error: Creating PostgreSQL database cluster Failed** This means Encore was not able to create the database. Often this is due to a problem with Docker. - Check if you have permission to access Docker by running `docker images`. - Set the correct permissions with `sudo usermod -aG docker $USER` (Learn more in the [Docker documentation](https://docs.docker.com/engine/install/linux-postinstall/)) - Then log out and log back in so that your group membership is refreshed. **Error: unable to save docker image** This error is often caused by a problem with Docker. - Make sure that you have [Docker](https://docker.com/) installed and running. - In Docker, open **Settings > Advanced** and make sure that the setting `Allow the default Docker socket to be used` is checked. - If it still fails, restart the Encore daemon by running `encore daemon`, then try `encore run` again. **Error: unable to add CA to cert pool** This error is commonly caused by the presence of the file `$HOME/.postgresql/root.crt` on the filesystem. When this file is present the PostgreSQL client library will assume the database server has that root certificate, which will cause the above error. - Remove or rename the file, then try `encore run` again. [PostgreSQL Extensions](https://encore.dev/docs/go/primitives/databases/extensions) [Object Storage](https://encore.dev/docs/go/primitives/object-storage) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/database-troubleshooting.md) ## Object Storage Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Object Storage is a simple and scalable solution to store files and unstructured data in your backend application. The most common implementation is Amazon S3 ("Simple Storage Service") and its semantics are universally supported by every major cloud provider. Encore.go provides a cloud-agnostic API for working with Object Storage, allowing you to store and retrieve files with ease. It has support for Amazon S3, Google Cloud Storage, as well as any other S3-compatible implementation (such as DigitalOcean Spaces, MinIO, etc.). Additionally, when you use Encore's Object Storage API you also automatically get: - Automatic tracing and instrumentation of all Object Storage operations - Built-in local development support, storing objects on the local filesystem - Support for integration testing, using a local, in-memory storage backend ## [Creating a Bucket](https://encore.dev/docs/go/primitives/object-storage\#creating-a-bucket) The core of Object Storage is the **Bucket**, which represents a collection of files. In Encore, buckets must be declared as package level variables, and cannot be created inside functions. Regardless of where you create a bucket, it can be accessed from any service by referencing the variable it's assigned to. When creating a bucket you can configure additional properties, like whether the objects in the bucket should be versioned. See the complete specification in the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#NewBucket). For example, to create a bucket for storing profile pictures: ```go package user import "encore.dev/storage/objects" var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{ Versioned: false, }) ``` ## [Uploading files](https://encore.dev/docs/go/primitives/object-storage\#uploading-files) To upload a file to a bucket, use the `Upload` method on the bucket variable. It returns a writer that you can use to write the contents of the file. To complete the upload, call the `Close` method on the writer. To abort the upload, either cancel the context or call the `Abort` method on the writer. The `Upload` method additionally takes a set of options to configure the upload, like setting attributes (`objects.WithUploadAttrs`) or to reject the upload if the object already exists (`objects.WithPreconditions`). See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#Bucket.Upload) for more details. ```go package user import ( "context" "io" "net/http" "encore.dev/beta/auth" "encore.dev/beta/errs" "encore.dev/storage/objects" ) var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{}) //encore:api auth raw method=POST path=/upload-profile-picture func UploadProfilePicture(w http.ResponseWriter, req *http.Request) { // Store the user's profile picture with their user id as the key. userID, _ := auth.UserID() key := string(userID) // We store the profile writer := ProfilePictures.Upload(req.Context(), key) _, err := io.Copy(writer, req.Body) if err != nil { // If something went wrong with copying data, abort the upload and return an error. writer.Abort() errs.HTTPError(w, err) return } if err := writer.Close(); err != nil { errs.HTTPError(w, err) return } // All good! Return a 200 OK. w.WriteHeader(http.StatusOK) } ``` ## [Downloading files](https://encore.dev/docs/go/primitives/object-storage\#downloading-files) To download a file from a bucket, use the `Download` method on the bucket variable. It returns a reader that you can use to read the contents of the file. The `Download` method additionally takes a set of options to configure the download, like downloading a specific version if the bucket is versioned (`objects.WithVersion`). See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#Bucket.Download) for more details. For example, to download the user's profile picture and serve it: ```go package user import ( "context" "io" "net/http" "encore.dev" "encore.dev/beta/auth" "encore.dev/beta/errs" "encore.dev/storage/objects" ) var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{}) //encore:api public raw method=GET path=/profile-picture/:userID func ServeProfilePicture(w http.ResponseWriter, req *http.Request) { userID := encore.CurrentRequest().PathParams.Get("userID") reader := ProfilePictures.Download(req.Context(), userID) // Did we encounter an error? if err := reader.Err(); err != nil { errs.HTTPError(w, err) return } // Assuming all images are JPEGs. w.Header().Set("Content-Type", "image/jpeg") io.Copy(w, reader) } ``` ## [Listing objects](https://encore.dev/docs/go/primitives/object-storage\#listing-objects) To list objects in a bucket, use the `List` method on the bucket variable. It returns an iterator of `(error, *objects.ListEntry)` pairs that you can use to easily iterate over the objects in the bucket using a `range` loop. For example, to list all profile pictures: ```go for err, entry := range ProfilePictures.List(ctx, &objects.Query{}) { if err != nil { // Handle error } // Do something with entry } ``` The `*objects.Query` type can be used to limit the number of objects returned, or to filter them to a specific key prefix. See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#Bucket.List) for more details. ## [Deleting objects](https://encore.dev/docs/go/primitives/object-storage\#deleting-objects) To delete an object from a bucket, use the `Remove` method on the bucket variable. For example, to delete a profile picture: ```go err := ProfilePictures.Remove(ctx, "my-user-id") if err != nil && !errors.Is(err, objects.ErrObjectNotFound) { // Handle error } ``` ## [Retrieving object attributes](https://encore.dev/docs/go/primitives/object-storage\#retrieving-object-attributes) You can retrieve information about an object using the `Attrs` method on the bucket variable. It returns the attributes of the object, like its size, content type, and ETag. For example, to get the attributes of a profile picture: ```go attrs, err := ProfilePictures.Attrs(ctx, "my-user-id") if errors.Is(err, objects.ErrObjectNotFound) { // Object not found } else if err != nil { // Some other error } // Do something with attrs ``` For convenience there is also `Exists` which returns a boolean indicating whether the object exists. ```go exists, err := ProfilePictures.Exists(ctx, "my-user-id") if err != nil { // Handle error } else if !exists { // Object does not exist } ``` ## [Using Public Buckets](https://encore.dev/docs/go/primitives/object-storage\#using-public-buckets) Encore supports creating public buckets where objects can be accessed directly via HTTP/HTTPS without authentication. This is useful for serving static assets like images, videos, or other public files. To create a public bucket, set `Public: true` in the `BucketConfig`: ```go var PublicAssets = objects.NewBucket("public-assets", objects.BucketConfig{ Public: true, }) ``` Once configured as public, you can get the public URL for any object using the `PublicURL` method: ```go // Get the public URL for an object url := PublicAssets.PublicURL("path/to/image.jpg") // The URL can be used directly or shared publicly fmt.Println(url) // e.g. https://assets.example.com/path/to/image.jpg ``` When self-hosting, see how to configure public buckets in the [infrastructure configuration docs](https://encore.dev/docs/ts/self-host/configure-infra). When deploying with Encore Cloud it will automatically configure the bucket to be publicly accessible and [configure CDN](https://encore.dev/docs/platform/infrastructure/infra#production-infrastructure) for optimal content delivery. ### [Using bucket references](https://encore.dev/docs/go/primitives/object-storage\#using-bucket-references) Encore uses static analysis to determine which services are accessing each bucket, and what operations each service is performing. That information is used to provision infrastructure correctly, render architecture diagrams, and configure IAM permissions. This means that `*objects.Bucket` variables can't be passed around however you'd like, as it makes static analysis impossible in many cases. To work around these restrictions Encore allows you to get a "reference" to a bucket that can be passed around any way you want by calling `objects.BucketRef`. To ensure Encore still is aware of which permissions each service needs, the call to `objects.BucketRef` must be made from within a service. Additionally, it must pre-declare the permissions it needs; those permissions are then assumed to be used by the service. It looks like this (using the `ProfilePictures` topic above): ```go ref := objects.BucketRef[objects.Downloader](ProfilePictures) // ref is of type objects.Downloader, which allows downloading. ``` Encore provides permission interfaces for each operation that can be performed on a bucket: - `objects.Downloader` for downloading objects - `objects.Uploader` for uploading objects - `objects.Lister` for listing objects - `objects.Attrser` for getting object attributes - `objects.Remover` for removing objects - `objects.SignedDownloader` for generating signed download URLs for objects - `objects.SignedUploader` for generating signed upload URLs for objects If you need multiple permissions they can be combined by creating an interface that embeds the permissions you need. ```go type myPerms interface { objects.Downloader objects.Uploader } ref := objects.BucketRef[myPerms](ProfilePictures) ``` For convenience Encore provides an `objects.ReadWriter` interface that gives complete read-write access with all the permissions above. See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#BucketRef) for more details. ## [Signed Upload URLs](https://encore.dev/docs/go/primitives/object-storage\#signed-upload-urls) You can use `SignedUploadURL` to create signed URLs to allow clients to upload content directly into the bucket over the internet. The URL is always restricted to one filename, and has a set expiration date. Anyone in possession of the URL can upload data under this filename without any additional authentication. ```go url, err := ProfilePictures.SignedUploadURL(ctx, "my-user-id", objects.WithTTL(time.Duration(7200)*time.Second)) // Pass url to client ``` The client can now `PUT` to this URL with the content as a binary payload. ```bash curl -X PUT --data-binary @/home/me/dog-wizard.jpeg "https://storage.googleapis.com/profile-pictures/my-user-id/?x-goog-signature=b7a1<...>" ``` ### [Why signed upload URLs?](https://encore.dev/docs/go/primitives/object-storage\#why-signed-upload-urls) Signed URLs are an alternative to accepting the content payload directly in your API. Content upload requests are sometimes inconvenient to handle well: they can be long running and very large. With signed URLs, the content flows directly into the storage bucket, and only object IDs and metadata go through your API service. The trade-off is that the upload flow becomes more complex from a client point of view. ## [Signed Download URLs](https://encore.dev/docs/go/primitives/object-storage\#signed-download-urls) You can use `SignedDownloadURL` to create signed URLs to allow clients to download content directly from the bucket, even if it's private. The URL is always restricted to one filename, and has a set expiration date. Anyone in possession of the URL can download the file without any additional authentication. ```go url, err := Documents.SignedDownloadURL(ctx, "letter-1234", objects.WithTTL(time.Duration(7200)*time.Second)) // Pass url to client ``` ### [Why signed download URLs?](https://encore.dev/docs/go/primitives/object-storage\#why-signed-download-urls) Similar to the upload case, signed download URLs is a way to avoid handing large files or bulk traffic through your API. With signed URLs, the content flows directly from the storage bucket, and only object IDs and metadata go through your API service. Note: unless the content is private, prefer serving urls with `PublicURL()` over signed URLs. Public URLs go over CDN, which is typically significantly more performant and cost effective. [Troubleshooting](https://encore.dev/docs/go/primitives/databases/troubleshooting) [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/object-storage.md) ## Encore Cron Jobs Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) When you need to run periodic and recurring tasks, Encore.go provides a declarative way of using Cron Jobs. When a Cron Job is defined in your application, Encore automatically calls your specified API according to the defined schedule. This eliminates the need for infrastructure maintenance, as Encore manages scheduling, monitoring, and execution of Cron Jobs. ##### Please note Cron Jobs do not run when developing locally or in [Preview Environments](https://encore.dev/docs/platform/deploy/preview-environments), but you can always call the API manually to test the behavior. ##### Related example [encoredev/examples/uptime](https://github.com/encoredev/examples/tree/main/uptime) Uptime Monitoring app that uses a Cron Job to periodically check the uptime of a website. `$ encore app create --example=uptime` ## [Defining a Cron Job](https://encore.dev/docs/go/primitives/cron-jobs\#defining-a-cron-job) To define a Cron Job, import the `encore.dev/cron` [package](https://pkg.go.dev/encore.dev/cron), and call the `cron.NewJob()` function and store it as a package-level variable. ### [Example](https://encore.dev/docs/go/primitives/cron-jobs\#example) ```go import "encore.dev/cron" // Send a welcome email to everyone who signed up in the last two hours. var _ = cron.NewJob("welcome-email", cron.JobConfig{ Title: "Send welcome emails", Every: 2 * cron.Hour, Endpoint: SendWelcomeEmail, }) // SendWelcomeEmail emails everyone who signed up recently. // It's idempotent: it only sends a welcome email to each person once. //encore:api private func SendWelcomeEmail(ctx context.Context) error { // ... return nil } ``` The `"welcome-email"` argument to `cron.NewJob` is a unique ID you give to each Cron Job. If you later refactor the code and move the Cron Job definition to another package, we use this ID to keep track that it's the same Cron Job and not a different one. When this code gets deployed Encore will automatically register the Cron Job in Encore Cloud and begin calling the `SendWelcomeEmail` API every hour. The Encore Cloud dashboard provides a convenient user interface for monitoring and debugging Cron Job executions across all your environments via the `Cron Jobs` menu item: ![Cron Jobs UI](https://encore.dev/assets/docs/cron.png) ## [Keep in mind when using Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs\#keep-in-mind-when-using-cron-jobs) - Cron Jobs do not execute during local development or in [Preview Environments](https://encore.dev/docs/platform/deploy/preview-environments). However, you can manually invoke the API to test its behavior. - In Encore Cloud, Cron Job executions are limited to **once every hour**, with the exact minute randomized within that hour for users on the Free Tier. To enable more frequent executions or to specify the exact minute within the hour, consider [deploying to your own cloud](https://encore.dev/docs/platform/deploy/own-cloud) or upgrading to the [Pro plan](https://encore.dev/pricing). - Both public and private APIs are supported for Cron Jobs. - Ensure that the API endpoints used in Cron Jobs are idempotent, as they may be called multiple times under certain network conditions. - The API endpoints used in Cron Jobs must not take any request parameters. That is, their signatures must be `func(context.Context) error` or `func(context.Context) (*T, error)`. ## [Cron schedules](https://encore.dev/docs/go/primitives/cron-jobs\#cron-schedules) Above we used the `Every` field, which executes the Cron Job on a periodic basis. It runs around the clock each day, starting at midnight (UTC). In order to ensure a consistent delay between each run, the interval used **must divide 24 hours evenly**. For example, `10 * cron.Minute` and `6 * cron.Hour` are both allowed (since 24 hours is evenly divisible by both), whereas `7 * cron.Hour` is not (since 24 is not evenly divisible by 7). The Encore compiler will catch this and give you a helpful error at compile-time if you try to use an invalid interval. ### [Cron expressions](https://encore.dev/docs/go/primitives/cron-jobs\#cron-expressions) For more advanced use cases, such as running a Cron Job on a specific day of the month, or a specific week day, or similar, the `Every` field is not expressive enough. For these use cases, Encore provides full support for [Cron expressions](https://en.wikipedia.org/wiki/Cron) by using the `Schedule` field instead of the `Every` field. Cron expressions allow you to define precise schedules for your tasks, including specific days of the week, specific hours of the day, and more. Note that all times are expressed in UTC. For example: ```go // Run the monthly accounting sync job at 4am (UTC) on the 15th day of each month. var _ = cron.NewJob("accounting-sync", cron.JobConfig{ Title: "Cron Job Example", Schedule: "0 4 15 * *", Endpoint: AccountingSync, }) ``` [Object Storage](https://encore.dev/docs/go/primitives/object-storage) [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/cron-jobs.md) ## Pub/Sub Overview Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Publishers & Subscribers (Pub/Sub) let you build systems that communicate by broadcasting events asynchronously. This is a great way to decouple services for better reliability and responsiveness. Encore's Backend Framework lets you use Pub/Sub in a cloud-agnostic declarative fashion. At deployment, Encore automatically [provisions the required infrastructure](https://encore.dev/docs/platform/infrastructure/infra). ## [Creating a Topic](https://encore.dev/docs/go/primitives/pubsub\#creating-a-topic) The core of Pub/Sub is the **Topic**, a named channel on which you publish events. Topics must be declared as package level variables, and cannot be created inside functions. Regardless of where you create a topic, it can be published to from any service, and subscribed to from any service. When creating a topic, it must be given an event type, a unique name, and a configuration to define its behaviour. See the complete specification in the [package documentation](https://pkg.go.dev/encore.dev/pubsub#NewTopic). For example, to create a topic with events about user signups: ```go package user import "encore.dev/pubsub" type SignupEvent struct{ UserID int } var Signups = pubsub.NewTopic[*SignupEvent]("signups", pubsub.TopicConfig{ DeliveryGuarantee: pubsub.AtLeastOnce, }) ``` ##### Related example [encoredev/examples/uptime](https://github.com/encoredev/examples/tree/main/uptime) Event-driven example application using Pub/Sub. `$ encore app create --example=uptime` ### [At-least-once delivery](https://encore.dev/docs/go/primitives/pubsub\#at-least-once-delivery) The above example configures the topic to ensure that, for each subscription, events will be delivered _at least once_. This means that if the topic believes the event was not processed, it will attempt to deliver the message again. **Therefore, all subscription handlers should be [idempotent](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning).** This helps ensure that if the handler is called two or more times, from the outside there's no difference compared to calling it once. This can be achieved using a database to track if you have already performed the action that the event is meant to trigger, or ensuring that the action being performed is also idempotent in nature. ### [Exactly-once delivery](https://encore.dev/docs/go/primitives/pubsub\#exactly-once-delivery) Topics can also be configured to deliver events _exactly once_ by setting the `DeliveryGuarantee` field to `pubsub.ExactlyOnce`. This enables stronger guarantees on the infrastructure level to minimize the likelihood of message re-delivery. However, there are still some rare circumstances when a message might be redelivered. For example, if a networking issue causes the acknowledgement of successful processing the message to be lost before the cloud provider receives it (the [Two Generals' Problem](https://en.wikipedia.org/wiki/Two_Generals%27_Problem)). As such, if correctness is critical under all circumstances, it's still advisable to design your subscription handlers to be idempotent. By enabling exactly-once delivery on a topic the cloud provider enforces certain throughput limitations: - AWS: 300 messages per second for the topic (see [AWS SQS Quotas](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/quotas-messages.html)). - GCP: At least 3,000 messages per second across all topics in the region (can be higher on the region see [GCP PubSub Quotas](https://cloud.google.com/pubsub/quotas#quotas)). ##### Take care Exactly-once delivery does not perform message deduplication on the publishing side. If `Publish` is called twice with the same message, the message will be delivered twice. ### [Ordered Topics](https://encore.dev/docs/go/primitives/pubsub\#ordered-topics) Topics are unordered by default, meaning that messages can be delivered in any order. This allows for better throughput on the topic as messages can be processed in parallel. However, in some cases, messages must be delivered in the order they were published for a given entity. To create an ordered topic, configure the topic's `OrderingAttribute` to match the `pubsub-attr` tag on one of the top-level fields of the event type. This field ensures that messages delivered to the same subscriber are delivered in the order of publishing for that specific field value. Messages with a different value on the ordering attribute are delivered in an unspecified order. To maintain topic order, messages with the same ordering key aren't delivered until the earliest message is processed or dead-lettered, potentially causing delays due to [head-of-line blocking](https://en.wikipedia.org/wiki/Head-of-line_blocking). Mitigate processing issues by ensuring robust logging and alerts, and appropriate subscription retry policies. ##### Please note The `OrderingAttribute` currently has no effect in local environments. #### [Throughput limitations](https://encore.dev/docs/go/primitives/pubsub\#throughput-limitations) Each cloud provider enforces certain throughput limitations for ordered topics: - **AWS:** 300 messages per second for the topic (see [AWS SQS Quotas](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/quotas-messages.html)) - **GCP:** 1 MBps for each ordering key (See [GCP Pub/Sub Resource Limits](https://cloud.google.com/pubsub/quotas#resource_limits)) #### [Ordered topic example](https://encore.dev/docs/go/primitives/pubsub\#ordered-topic-example) ```go package example import ( "context" "encore.dev/pubsub" ) type CartEvent struct { ShoppingCartID int `pubsub-attr:"cart_id"` Event string } var CartEvents = pubsub.NewTopic[*CartEvent]("cart-events", pubsub.TopicConfig{ DeliveryGuarantee: pubsub.AtLeastOnce, OrderingAttribute: "cart_id", }) func Example(ctx context.Context) error { // These are delivered in order as they all have the same shopping cart ID CartEvents.Publish(ctx, &CartEvent{ShoppingCartID: 1, Event: "item_added"}) CartEvents.Publish(ctx, &CartEvent{ShoppingCartID: 1, Event: "checkout_started"}) CartEvents.Publish(ctx, &CartEvent{ShoppingCartID: 1, Event: "checkout_completed"}) // This event may be delivered at any point as it has a different shopping cart ID CartEvents.Publish(ctx, &CartEvent{ShoppingCartID: 2, Event: "item_added"}) } ``` ## [Publishing events](https://encore.dev/docs/go/primitives/pubsub\#publishing-events) To publish an **Event**, call `Publish` on the topic passing in the event object (which is the type specified in the `pubsub.NewTopic[Type]` constructor). For example: ```go messageID, err := Signups.Publish(ctx, &SignupEvent{UserID: id}) if err != nil { return err } // If we get here the event has been successfully published, // and all registered subscribers will receive the event. // The messageID variable contains the unique id of the message, // which is also provided to the subscribers when processing the event. ``` By defining the `Signups` topic variable as an exported variable you can also publish to the topic from other services in the same way. ### [Using topic references](https://encore.dev/docs/go/primitives/pubsub\#using-topic-references) Encore uses static analysis to determine which services are publishing messages to what topics. That information is used to provision infrastructure correctly, render architecture diagrams, and configure IAM permissions. This means that `*pubsub.Topic` variables can't be passed around however you'd like, as it makes static analysis impossible in many cases. To work around these restrictions Encore allows you to get a reference to a topic that can be passed around any way you want. It looks like this (using the `Signups` topic above): ```go signupRef := pubsub.TopicRef[pubsub.Publisher[*SignupEvent]](Signups) // signupRef is of type pubsub.Publisher[*SignupEvent], which allows publishing. ``` The difference between a **TopicRef** and a **Topic** is that topic references need to pre-declare what permissions are needed. Encore then assumes that all the permissions you declare are used. For example, if you declare a **TopicRef** with the `pubsub.Publisher` permission (as seen above) Encore assumes that the service will publish messages to the topic and provisions the infrastructure to support that. Note that a **TopicRef** must be declared _within a service_, but the reference itself can be freely passed around to library code, be dependency injected into [service structs](https://encore.dev/docs/go/how-to/dependency-injection), and so on. ## [Subscribing to Events](https://encore.dev/docs/go/primitives/pubsub\#subscribing-to-events) To **Subscribe** to events, you create a Subscription as a package level variable by calling the [`pubsub.NewSubscription`](https://pkg.go.dev/encore.dev/pubsub#NewSubscription) function. Each subscription needs: - the topic to subscribe to - a name which is unique for the topic - a configuration object with at least a `Handler` function to process the events - a configuration object Here's an example of how you create a subscription to a topic: ```go package email import ( "encore.dev/pubsub" "user" ) var _ = pubsub.NewSubscription( user.Signups, "send-welcome-email", pubsub.SubscriptionConfig[*SignupEvent]{ Handler: SendWelcomeEmail, }, ) func SendWelcomeEmail(ctx context.Context, event *SignupEvent) error { // send email... return nil } ``` Subscriptions can be in the same service as the topic is declared, or in any other service of your application. Each subscription to a single topic receives the events independently of any other subscriptions to the same topic. This means that if one subscription is running very slowly, it will grow a backlog of unprocessed events. However, any other subscriptions will still be processing events in real-time as they are published. The `ctx` passed to the handler function is cancelled when the `AckDeadline` for the subscription is reached. This is the time when the message is considered to have timed out and can be redelivered to another subscriber. The timeout defaults to 30 seconds if you don't explicitly configure `AckDeadline`. ### [Method-based handlers](https://encore.dev/docs/go/primitives/pubsub\#method-based-handlers) When using [service structs](https://encore.dev/docs/go/primitives/service-structs) for dependency injection it's common to want to define the subscription handler as a method on the service struct, to be able to access the injected dependencies. The pubsub package provides the `pubsub.MethodHandler` function for this purpose: ```go //encore:service type Service struct { /* ... */ } func (s *Service) SendWelcomeEmail(ctx context.Context, event *SignupEvent) error { // ... } var _ = pubsub.NewSubscription( user.Signups, "send-welcome-email", pubsub.SubscriptionConfig[*SignupEvent]{ Handler: pubsub.MethodHandler((*Service).SendWelcomeEmail), }, ) ``` Note that `pubsub.MethodHandler` only allows referencing methods on the service struct type, not any other type. ### [Subscription configuration](https://encore.dev/docs/go/primitives/pubsub\#subscription-configuration) When creating a subscription you can configure behavior such as message retention and retry policy, using the `SubscriptionConfig` type. See the [package documentation](https://pkg.go.dev/encore.dev/pubsub#SubscriptionConfig) for the complete configuration options. ##### Please note The `SubscriptionConfig` struct fields must be defined as compile-time constants, and cannot be defined in terms of function calls. This is necessary for Encore to understand the exact requirements of the subscription, in order to provision the correct infrastructure upon deployment. ### [Error Handling](https://encore.dev/docs/go/primitives/pubsub\#error-handling) If a subscription function returns an error, the event being processed will be retried, based on the retry policy [configured on that subscription](https://pkg.go.dev/encore.dev/pubsub#SubscriptionConfig). After the `MaxRetries` is hit, the event will be placed into a dead-letter queue (DLQ) for that subscriber. This allows the subscription to continue processing events until the bug which caused the event to fail can be fixed. Once fixed, the messages on the dead-letter queue can be manually released to be processed again by the subscriber. ## [Testing Pub/Sub](https://encore.dev/docs/go/primitives/pubsub\#testing-pubsub) Encore uses a special testing implementation of Pub/Sub topics. When running tests, topics are aware of which test is running. This gives you the following guarantees: - Your subscriptions will not be triggered by events published. This allows you to test the behaviour of publishers independently of side effects caused by subscribers. - Message ID's generated on publish are deterministic (based on the order of publishing), thus your assertions can make use of that fact. - Each test is isolated from other tests, meaning that events published in one test will not impact other tests (even if you use parallel testing). Encore provides a helper function, [`et.Topic`](https://pkg.go.dev/encore.dev/et#Topic), to access the testing topic. You can use this object to extract the events that have been published to it during a test. Here's an example implementation: ```go package user import ( "testing" "encore.dev/et" "github.com/stretchr/testify/assert" ) func Test_Register(t *testing.T) { t.Parallel() ... Call Register() and assert changes to the database ... // Get all published messages on the Signups topic from this test. msgs := et.Topic(Signups).PublishedMessages() assert.Len(t, msgs, 1) } ``` ## [Ensuring consistency between services](https://encore.dev/docs/go/primitives/pubsub\#ensuring-consistency-between-services) Ensuring consistency between services in event-driven applications can be challenging, especially when database writes and Pub/Sub publishing are not transactional. This can lead to inconsistencies between services. To address this issue without adding excessive complexity, consider using a transactional outbox pattern. For more information on implementing this pattern with Encore, see the [Pub/Sub Outbox guide](https://encore.dev/docs/primitives/pubsub-outbox). ## [The benefits of Pub/Sub](https://encore.dev/docs/go/primitives/pubsub\#the-benefits-of-pubsub) Pub/Sub is a powerful building block in a backend application. It can be used to improve app reliability by reducing the blast radius of faulty components and bottlenecks. It can also be used to increase the speed of response to the user, and even helps reduce cognitive overhead for developers by inverting the dependencies between services. For those not familiar with Pub/Sub, lets take a look at an example API in a user registration service. The behavior we want to implement is that upon registration, we send a welcome email to the user and create a record of the signup in our analytics system. Now let's see how we could implement this only using APIs, compared to how a Pub/Sub implementation might look. ### [An API only approach](https://encore.dev/docs/go/primitives/pubsub\#an-api-only-approach) Using API calls between services, we might design a system which looks like this when the user registers: ![](https://encore.dev/assets/docs/pubsub-rpc-example.png) 1. The `user` service starts a database transaction and records the user in its database. 2. The `user` service makes a call to the `email` service to send a welcome email. 3. The `email` service then calls an email provider to actually send the email. 4. Upon success, the `email` service replies to the `user` service that the request was processed. 5. The `user` service then calls the `analytics` service to record the signup. 6. The `analytics` service the writes to the data warehouse to record the information. 7. The `analytics` service then replies to the `user` service that the request was processed. 8. The `user` service commits the database transaction. 9. The `user` service then can reply to the user to say the registration was successful. Notice how we have to wait for everything to complete before we can reply to the user to tell then we've registered them. This means that if our email provider takes 3 seconds to send the email, we've now taken 3 seconds to respond to the user, when in reality once the user was written to the database, we could have responded to the user instantly at that point to confirm the registration. Another downside to this approach is if our data warehouse is currently broken and reporting errors, our system will also report errors whenever anybody tries to signup! Given analytics is purely internal and doesn't impact users, why should the analytics system being down impact user signup? ### [A Pub/Sub approach](https://encore.dev/docs/go/primitives/pubsub\#a-pubsub-approach) A more ideal solution would be if we could decouple the behaviour of emailing the user and recording our analytics, such that the user service only has to record the user in its own database and let the user know they are registered - without worrying about the downstream impacts. Thankfully, this is exactly what [Pub/Sub topics](https://pkg.go.dev/encore.dev/pubsub#Topic) allow us to do. In this example, when a user registers we: 1. The `user` service starts a database transaction and records the user in its database. 2. Publish a signup event to the `signups` topic. 3. Commit the transaction and reply to the user to say the registration was successful. At this point the user is free to continue interacting with the application and we've isolated the registration behaviour from the rest of the application. In parallel, the `email` and `analytics` services will receive the signup event from the `signups` topic and will then perform their respective tasks. If either service returns an error, the event will automatically be backed off and retried until the service is able to process the event successfully, or reaches the maximum number of attempts and is placed into the deadletter queue (DLQ). ![](https://encore.dev/assets/docs/pubsub-topic-example.png) Notice how in this version, the processing time of the two other services did not impact the end user and in fact the `user` service is not even aware of the `email` and `analytics` services. This means that new systems which need to know about new users signing up can be added to the application, without the need to change the `user` service or impacting its performance. [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) [Caching](https://encore.dev/docs/go/primitives/caching) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/pubsub.md) ## Encore Caching Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) A cache is a high-speed storage layer, commonly used in distributed systems to improve user experiences by reducing latency, improving system performance, and avoiding expensive computation. For scalable systems you typically want to deploy the cache as a separate infrastructure resource, allowing you to run multiple instances of your application concurrently. Encore's built-in Caching API lets you use high-performance caches (using [Redis](https://redis.io/)) in a cloud-agnostic declarative fashion. At deployment, Encore will automatically [provision the required infrastructure](https://encore.dev/docs/platform/infrastructure/infra). ## [Cache clusters](https://encore.dev/docs/go/primitives/caching\#cache-clusters) To use caching in Encore, you must first define a _cache cluster_. Each cache cluster defined in your application will be provisioned as a separate Redis instance by Encore. This gives you fine-grained control over which service(s) should use the same cache cluster and which should have a separate one. It looks like this: ```go import "encore.dev/storage/cache" var MyCacheCluster = cache.NewCluster("my-cache-cluster", cache.ClusterConfig{ // EvictionPolicy tells Redis how to evict keys when the cache reaches // its memory limit. For typical cache use cases, cache.AllKeysLRU is a good default. EvictionPolicy: cache.AllKeysLRU, }) ``` ##### Please note When starting out it's recommended to use a single cache cluster that's shared between your different services. ## [Keyspaces](https://encore.dev/docs/go/primitives/caching\#keyspaces) When using a cache, each cached item is stored at a particular key, which is typically an arbitrary string. If you use a cache cluster to cache different sets of data, it's important that distinct data set have non-overlapping keys. Each value stored in the cache also has a specific type, and certain cache operations can only be performed on certain types. For example, a common cache operation is to increment an integer value that is stored in the cache. If you try to apply this operation on a value that is not an integer, an error is returned. Encore provides a simple, type-safe solution to these problems through Keyspaces. In order to begin storing data in your cache, you must first define a Keyspace. Each keyspace has a Key type and a Value type. The Key type is much like a map key, in that it tells Encore where in the cache the item is stored. The Key type is combined with the Key Pattern to produce a string that is the Redis cache key. The Value type is the type of the values stored in that keyspace. For many keyspaces this is specified in the name of the constructor. For example, `NewIntKeyspace` stores `int64` values. For example, if you want to rate limit the number of requests per user ID it looks like this: ```go import ( "encore.dev/beta/auth" "encore.dev/beta/errs" "encore.dev/middleware" ) // RequestsPerUser tracks the number of requests per user. // The cache items expire after 10 seconds without activity. var RequestsPerUser = cache.NewIntKeyspace[auth.UID](cluster, cache.KeyspaceConfig{ KeyPattern: "requests/:key", DefaultExpiry: cache.ExpireIn(10 * time.Second), }) // RateLimitMiddleware is a global middleware that limits the number of authenticated requests // to 10 requests per 10 seconds. //encore:middleware target=all func RateLimitMiddleware(req middleware.Request, next middleware.Next) middleware.Response { if userID, ok := auth.UserID(); ok { val, err := RequestsPerUser.Increment(req.Context(), userID, 1) // NOTE: this "fails open", meaning if we can't communicate with the cache // we default to allowing the requests. // // Consider whether that's the correct behavior for your application, // or if you want to return an error to the user in that case. if err == nil && val > 10 { return middleware.Response{ Err: &errs.Error{Code: errs.ResourceExhausted, Message: "rate limit exceeded"}, } } } return next(req) } ``` As you can see, the `RequestsPerUser` defines a `KeyPattern` which is set to `"requests/:key"`. Here `:key` refers to the value of the Key type, which is the `auth.UID` value passed in. If you want the cache key to contain multiple values, you can define a struct type and pass that as the key. Then change the `KeyPattern` to specify the struct fields. For example: ```go type MyKey struct { UserID auth.UID ResourcePath string // the resource being accessed } // ResourceRequestsPerUser tracks the number of requests per user and resource. // The cache items expire after 10 seconds without activity. var ResourceRequestsPerUser = cache.NewIntKeyspace[MyKey](cluster, cache.KeyspaceConfig{ KeyPattern: "requests/:UserID/:ResourcePath", DefaultExpiry: cache.ExpireIn(10 * time.Second), }) // ... then: key := MyKey{UserID: "some-user-id", ResourcePath: "/foo"} ResourceRequestsPerUser.Increment(ctx, key, 1) ``` ##### Please note Encore ensures that all the struct fields are present in the `KeyPattern`, and that the placeholder values are all valid field names. That way the connection between the struct fields and the `KeyPattern` become compile-time type-safe as well. Also note that Encore ensures there are no conflicting `KeyPattern` definitions across each cache cluster. Each keyspace must define its own, non-conflicting `KeyPattern`. This way, you can feel safe that there won't be any accidental overwrites of cache values, even with multiple services sharing the same cache cluster. ## [Keyspace operations](https://encore.dev/docs/go/primitives/caching\#keyspace-operations) Encore comes with a full suite of keyspace types, each with a wide variety of cache operations. Basic keyspace types include [strings](https://pkg.go.dev/encore.dev/storage/cache#NewStringKeyspace), [integers](https://pkg.go.dev/encore.dev/storage/cache#NewIntKeyspace), [floats](https://pkg.go.dev/encore.dev/storage/cache#NewFloatKeyspace), and [struct types](https://pkg.go.dev/encore.dev/storage/cache#NewStructKeyspace). These keyspaces all share the same set of methods (along with a few keyspace-specific ones). There are also more advanced keyspaces for storing [sets of basic types](https://pkg.go.dev/encore.dev/storage/cache#NewSetKeyspace) and [ordered lists of basic types](https://pkg.go.dev/encore.dev/storage/cache#NewListKeyspace). These keyspaces offer a different, specialized set of methods specific to set and list operations. For a list of the supported operations, see the [package documentation](https://pkg.go.dev/encore.dev/storage/cache). ## [Testing](https://encore.dev/docs/go/primitives/caching\#testing) When running tests, Encore spins up an in-memory cache separately for each test. This way you don't have to think about clearing the cache between tests, or worrying about whether one test affects another. Each test is automatically fully isolated. ## [Local development](https://encore.dev/docs/go/primitives/caching\#local-development) For local development, Encore maintains a local, in-memory implementation of Redis. This implementation is designed to store a small amount of keys (currently 100). When the number of keys exceeds this value, keys are randomly purged to get below the limit. This is designed in order to simulate the ephemeral, transient nature of caches while also limiting memory use. The precise behavior for local development may change over time and should not be relied on. [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) [Secrets](https://encore.dev/docs/go/primitives/secrets) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/caching.md) ## Secrets Management Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Wouldn't it be nice to store secret values like API keys, database passwords, and private keys directly in the source code? Of course, we can’t do that – it's horrifyingly insecure! (Unfortunately, it's also [very common](https://www.ndss-symposium.org/ndss-paper/how-bad-can-it-git-characterizing-secret-leakage-in-public-github-repositories/).) Encore's built-in secrets manager makes it simple to store secrets in a secure way and lets you use them in your program like regular variables. ##### Related example [encoredev/examples/slack-bot](https://github.com/encoredev/examples/tree/main/slack-bot) Slack Bot example application using secrets to store a Slack key. `$ encore app create --example=slack-bot` ## [Using secrets in your application](https://encore.dev/docs/go/primitives/secrets\#using-secrets-in-your-application) To use a secret in your application, first define it directly in your code by creating an unexported struct named `secrets`, where all fields are of type `string`. For example: ```go var secrets struct { SSHPrivateKey string // ed25519 private key for SSH server GitHubAPIToken string // personal access token for deployments // ... } ``` When you've defined secrets in your program, the Encore compiler will check that they are set before running or deploying your application. If a secret is not set, you will get a compilation error notifying you that a secret value is missing. Once you've provided values for all secrets, you can just use them in your application like a regular variable. For example: ```go func callGitHub(ctx context.Context) { req, _ := http.NewRequestWithContext(ctx, "GET", "https:///api.github.com/user", nil) req.Header.Add("Authorization", "token " + secrets.GitHubAPIToken) resp, err := http.DefaultClient.Do(req) // ... handle err and resp } ``` ##### Please note Secret keys are globally unique for your whole application. If multiple services use the same secret name they both receive the same secret value at runtime. ## [Storing secret values](https://encore.dev/docs/go/primitives/secrets\#storing-secret-values) ### [Using the Encore Cloud dashboard](https://encore.dev/docs/go/primitives/secrets\#using-the-encore-cloud-dashboard) The simplest way to set up secrets is with the Secrets Manager in the Encore Cloud dashboard. Open your app in [app.encore.cloud](https://app.encore.cloud/), go to **Settings** in the main navigation, and then click on **Secrets** in the settings menu. From here you can create secrets, save secret values, and configure different values for different environments. ![](https://encore.dev/assets/docs/secrets.png) ### [Using the CLI](https://encore.dev/docs/go/primitives/secrets\#using-the-cli) If you prefer, you can also set up secrets from the CLI using: `encore secret set --type ` `` defines which environment types the secret value applies to. Use a comma-separated list of `production`, `development`, `preview`, and `local`. Shorthands: `prod`, `dev`, `pr`. For example `encore secret set --type prod SSHPrivateKey` sets the secret value for production environments, and `encore secret set --type dev,preview,local GitHubAPIToken` sets the secret value for development, preview, and local environments. In some cases, it can be useful to define a secret for a specific environment instead of an environment type. You can do so with `encore secret set --env `. Secret values for specific environments take precedence over values for environment types. ### [Environment settings](https://encore.dev/docs/go/primitives/secrets\#environment-settings) Each secret can only have one secret value for each environment type. For example: If you have a secret value that's shared between `development`, `preview` and `local`, and you want to override the value for `local`, you must first edit the existing secret and remove `local` using the Secrets Manager in the [Encore Cloud dashboard](https://app.encore.cloud/). You can then add a new secret value for `local`. The end result should look something like the picture below. ![](https://encore.dev/assets/docs/secretoverride.png) ## [How it works: Where secrets are stored](https://encore.dev/docs/go/primitives/secrets\#how-it-works-where-secrets-are-stored) When you store a secret Encore stores it encrypted using Google Cloud Platform's [Key Management Service](https://cloud.google.com/security-key-management) (KMS). - **Production / Your own cloud:** When you deploy to production using your own cloud account on GCP or AWS, Encore provisions a secrets manager in your account (using either KMS or AWS Secrets Manager) and replicates your secrets to it. The secrets are then injected into the container using secret environment variables. - **Local:** For local secrets Encore automatically replicates them to developers' machines when running `encore run`. - **Development / Encore Cloud:** Environments on Encore's development cloud (running on GCP under the hood) work the same as self-hosted GCP environments, using GCP Secrets Manager. ### [Overriding local secrets](https://encore.dev/docs/go/primitives/secrets\#overriding-local-secrets) When setting secrets via the `encore secret set` command, they are automatically synced to all developers working on the same application, courtesy of Encore Cloud. In some cases, however, you want to override a secret only for your local machine. This can be done by creating a file named `.secrets.local.cue` in the root of your Encore application, next to the `encore.app` file. The file contains key-value pairs of secret names to secret values. For example: ```cue GitHubAPIToken: "my-local-override-token" SSHPrivateKey: "custom-ssh-private-key" ``` [Caching](https://encore.dev/docs/go/primitives/caching) [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/secrets.md) ## Encore Code Snippets Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) When you're familiar with how Encore works, you can simplify your development workflow by copy-pasting these examples. If you're looking for details on how Encore works, please refer to the relevant docs section. ## [APIs](https://encore.dev/docs/go/primitives/code-snippets\#apis) ### [Defining APIs](https://encore.dev/docs/go/primitives/code-snippets\#defining-apis) ```go package hello // service name //encore:api public func Ping(ctx context.Context, params *PingParams) (*PingResponse, error) { msg := fmt.Sprintf("Hello, %s!", params.Name) return &PingResponse{Message: msg}, nil } ``` ### [Defining Request and Response schemas](https://encore.dev/docs/go/primitives/code-snippets\#defining-request-and-response-schemas) ```go // PingParams is the request data for the Ping endpoint. type PingParams struct { Name string } // PingResponse is the response data for the Ping endpoint. type PingResponse struct { Message string } ``` ### [Calling APIs](https://encore.dev/docs/go/primitives/code-snippets\#calling-apis) ```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 } ``` **Hint:** Import the service package and call the API endpoint using a regular function call. ### [Receive Webhooks](https://encore.dev/docs/go/primitives/code-snippets\#receive-webhooks) ```go import "net/http" // Webhook receives incoming webhooks from Some Service That Sends Webhooks. //encore:api public raw func Webhook(w http.ResponseWriter, req *http.Request) { // ... operate on the raw HTTP request ... } ``` **Hint:** Like any other API endpoint, this will be exposed at: `https://-.encr.app/service.Webhook` ## [Databases](https://encore.dev/docs/go/primitives/code-snippets\#databases) ### [Creating a SQL database](https://encore.dev/docs/go/primitives/code-snippets\#creating-a-sql-database) To create a database, import `encore.dev/storage/sqldb` and call `sqldb.NewDatabase`, assigning the result to a package-level variable. `sqldb.DatabaseConfig` specifies the directory containing the database migration files, which is how you define the database schema. ```go todo/db.go todo/migrations/1_create_table.up.sql package todo // Create the todo database and assign it to the "tododb" variable var tododb = sqldb.NewDatabase("todo", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // Then, query the database using db.QueryRow, db.Exec, etc. ``` ### [Inserting data into a database](https://encore.dev/docs/go/primitives/code-snippets\#inserting-data-into-a-database) One way of inserting data is with a helper function that uses the package function `sqldb.Exec`: ```go import "encore.dev/storage/sqldb" // insert inserts a todo item into the database. func insert(ctx context.Context, id, title string, done bool) error { _, err := tododb.Exec(ctx, ` INSERT INTO todo_item (id, title, done) VALUES ($1, $2, $3) `, id, title, done) return err } ``` ### [Querying a database](https://encore.dev/docs/go/primitives/code-snippets\#querying-a-database) To read a single todo item in the example schema above, we can use `sqldb.QueryRow`: ```go import "encore.dev/storage/sqldb" var item struct { ID int64 Title string Done bool } err := tododb.QueryRow(ctx, ` SELECT id, title, done FROM todo_item LIMIT 1 `).Scan(&item.ID, &item.Title, &item.Done) ``` **Hint:** If `sqldb.QueryRow` does not find a matching row, it reports an error that can be checked against by importing the standard library `errors` package and calling `errors.Is(err, sqldb.ErrNoRows)`. ## [Defining a Cron Job](https://encore.dev/docs/go/primitives/code-snippets\#defining-a-cron-job) ```go import "encore.dev/cron" var _ = cron.NewJob("welcome-email", cron.JobConfig{ Title: "Send welcome emails", Every: 2 * cron.Hour, Endpoint: SendWelcomeEmail, }) //encore:api private func SendWelcomeEmail(ctx context.Context) error { // ... return nil } ``` **Hint:** Cron Jobs do not run in your local development environment. ## [PubSub](https://encore.dev/docs/go/primitives/code-snippets\#pubsub) ### [Creating a PubSub topic](https://encore.dev/docs/go/primitives/code-snippets\#creating-a-pubsub-topic) ```go import "encore.dev/pubsub" type SignupEvent struct { UserID int } var Signups = pubsub.NewTopic[*SignupEvent]("signups", pubsub.TopicConfig { DeliveryGuarantee: pubsub.AtLeastOnce, }) ``` **Hint:** Topics are declared as package level variables and cannot be created inside functions. Regardless of where you create a topic, it can be published and subscribed to from any service. ### [Publishing an Event (Pub)](https://encore.dev/docs/go/primitives/code-snippets\#publishing-an-event-pub) ```go if _, err := Signups.Publish(ctx, &SignupEvent{UserID: id}); err != nil { return err } if err := tx.Commit(); err != nil { return err } ``` **Hint:** If you want to publish to the topic from another service, import the topic package variable (`Signups` in this example) and call publish on it from there. ### [Subscribing to Events (Sub)](https://encore.dev/docs/go/primitives/code-snippets\#subscribing-to-events-sub) Create a Subscription as a package level variable by calling `pubsub.NewSubscription`. ```go var _ = pubsub.NewSubscription( user.Signups, "send-welcome-email", pubsub.SubscriptionConfig[*SignupEvent] { Handler: SendWelcomeEmail, }, ) func SendWelcomeEmail(ctx context.Context, event *SignupEvent) error { ... send email ... return nil } ``` ## [Defining a Cache cluster](https://encore.dev/docs/go/primitives/code-snippets\#defining-a-cache-cluster) ```go import "encore.dev/storage/cache" var MyCacheCluster = cache.NewCluster("my-cache-cluster", cache.ClusterConfig{ // EvictionPolicy tells Redis how to evict keys when the cache reaches // its memory limit. For typical cache use cases, cache.AllKeysLRU is a good default. EvictionPolicy: cache.AllKeysLRU, }) ``` ## [Secrets](https://encore.dev/docs/go/primitives/code-snippets\#secrets) ### [Defining Secrets](https://encore.dev/docs/go/primitives/code-snippets\#defining-secrets) ```go var secrets struct { GitHubAPIToken string // personal access token for deployments SomeOtherSecret string // some other secret } ``` **Hint:** The variable must be an unexported struct named `secrets`, and all the fields must be of type `string`. ### [Setting secret values](https://encore.dev/docs/go/primitives/code-snippets\#setting-secret-values) ```bash $ encore secret set --type ``` **Hint:**`` defines which environment types the secret value applies to. Use a comma-separated list of `production`, `development`, `preview`, and `local`. For each Secret, there can only be one secret value for each environment type. ### [Using secrets](https://encore.dev/docs/go/primitives/code-snippets\#using-secrets) ```go func callGitHub(ctx context.Context) { req, _ := http.NewRequestWithContext(ctx, "GET", "https:///api.github.com/user", nil) req.Header.Add("Authorization", "token " + secrets.GitHubAPIToken) resp, err := http.DefaultClient.Do(req) // ... handle err and resp } ``` **Hint:** Secret keys are globally unique for your whole application; if multiple services use the same secret name they both receive the same secret value at runtime. [Secrets](https://encore.dev/docs/go/primitives/secrets) [Authentication](https://encore.dev/docs/go/develop/auth) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/primitives/code-snippets.md) ## API Authentication Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Almost every application needs to know who's calling it, whether the user represents a person in a consumer-facing app or an organization in a B2B app. Encore supports both use cases in a simple yet powerful way. As described in the docs for [defining APIs](https://encore.dev/docs/go/primitives/defining-apis), Encore offers three access levels for APIs: - `//encore:api public` – defines a public API that anybody on the internet can call. - `//encore:api private` – defines a private API that is never accessible to the outside world. It can only be called from other services in your app and via cron jobs. - `//encore:api auth` – defines a public API that anybody can call, but that requires valid authentication. When an API is defined with access level `auth`, outside calls to that API must specify an authorization header, in the form `Authorization: Bearer `. The token is passed to a designated auth handler function and the API call is allowed to go through only if the auth handler determines the token is valid. For more advanced use cases you can also customize the authentication information you want. See the section on [accepting structured auth information](https://encore.dev/docs/go/develop/auth#accepting-structured-auth-information) below. ##### Please note You can optionally send in auth data to `public` and `private` APIs, in which case the auth handler will be used. When used for `private` APIs, they are still not accessible from the outside world. ## [The auth handler](https://encore.dev/docs/go/develop/auth\#the-auth-handler) Encore applications can designate a special function to handle authentication, by defining a function and annotating it with `//encore:authhandler`. This annotation tells Encore to run the function whenever an incoming API call contains authentication data. The auth handler is responsible for validating the incoming authentication data and returning an `auth.UID` (a string type representing a **user id**). The `auth.UID` can be whatever you wish, but in practice it usually maps directly to the primary key stored in a user table (either defined in the Encore service or in an external service like [Firebase](https://encore.dev/docs/go/how-to/firebase-auth) or [Auth0](https://encore.dev/docs/go/how-to/auth0-auth)). ### [With custom user data](https://encore.dev/docs/go/develop/auth\#with-custom-user-data) Oftentimes it's convenient for the rest of your application to easily be able to look up information about the authenticated user making the request. If that's the case, define the auth handler like so: ```go import "encore.dev/beta/auth" // Data can be named whatever you prefer (but must be exported). type Data struct { Username string // ... } // AuthHandler can be named whatever you prefer (but must be exported). //encore:authhandler func AuthHandler(ctx context.Context, token string) (auth.UID, *Data, error) { // Validate the token and look up the user id and user data, // for example by calling Firebase Auth. } ``` ##### Related example [encoredev/examples/clerk](https://github.com/encoredev/examples/tree/main/clerk) Example application showing an auth handler implementation with Clerk. `$ encore app create --example=clerk` ##### Related docs [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) ### [Without custom user data](https://encore.dev/docs/go/develop/auth\#without-custom-user-data) When you don't require custom user data and it's sufficient to use `auth.UID`, simply skip it in the return type: ```go import "encore.dev/beta/auth" // AuthHandler can be named whatever you prefer (but must be exported). //encore:authhandler func AuthHandler(ctx context.Context, token string) (auth.UID, error) { // Validate the token and look up the user id, // for example by calling Firebase Auth. } ``` ## [Accepting structured auth information](https://encore.dev/docs/go/develop/auth\#accepting-structured-auth-information) In the examples above the function accepts a `Bearer` token as a string argument. In that case Encore parses the `Authorization` HTTP header and passes the token to the auth handler. In cases where you have different or more complex authorization requirements, you can instead specify a data structure that specifies one or more fields to be parsed from the HTTP request. For example: ```go type MyAuthParams struct { // SessionCookie is set to the value of the "session" cookie. // If the cookie is not set it's nil. SessionCookie *http.Cookie `cookie:"session"` // ClientID is the unique id of the client, sourced from the URL query string. ClientID string `query:"client_id"` // Authorization is the raw value of the "Authorization" header // without any parsing. Authorization string `header:"Authorization"` } //encore:authhandler func AuthHandler(ctx context.Context, p *MyAuthParams) (auth.UID, error) { // ... } ``` This example tells Encore that the application accepts authentication information via the `session` cookie, the `client_id` query string parameter, and the `Authorization` header. These fields are automatically filled in when the auth handler is called (if present in the request). You can of course combine auth params like this with custom user data (see the section above). ##### Please note Cookies are generally only used by browsers and are automatically added to requests made by browsers. As a result Encore does not include cookie fields in generated clients' authentication payloads or in the [Local Development Dashboard](https://encore.dev/docs/go/observability/dev-dash). ## [Handling auth errors](https://encore.dev/docs/go/develop/auth\#handling-auth-errors) When a token doesn't match your auth rules (for example if it's expired, the token has been revoked, or the token is invalid), you should return a non-nil error from the auth handler. Encore passes the error message on to the user when you use [Encore's built-in error package](https://encore.dev/docs/go/primitives/api-errors), so we recommend using that with the error code `Unauthenticated` to communicate what happened. For example: ```go import "encore.dev/beta/errs" //encore:authhandler func AuthHandler(ctx context.Context, token string) (auth.UID, error) { return "", &errs.Error{ Code: errs.Unauthenticated, Message: "invalid token", } } ``` ##### Take care Note that for security reasons you may not want to reveal too much information about why a request did not pass your auth checks. There are many subtle security considerations when dealing with authentication and we don't have time to go into all of them here. Whenever possible we recommend using a third-party auth provider instead of rolling your own authentication. ## [Using auth data](https://encore.dev/docs/go/develop/auth\#using-auth-data) Once the user has been identified by the auth handler, the API handler is called as usual. If it wishes to inspect the authenticated user, it can use the `encore.dev/beta/auth` package: - `auth.Data()` returns the custom user data returned by the auth handler (if any) - `auth.UserID()` returns `(auth.UID, bool)` to get the authenticated user id (if any) For an incoming request from the outside to an API that uses the `auth` access level, these are guaranteed to be set since the API won't be called if the auth handler doesn't succeed. Encore automatically propagates the auth data when you make API calls to other Encore API endpoints. ##### Please note If an endpoint calls another endpoint during its processing, and the original does not have an authenticated user, the request will fail. This behavior preserves the guarantees that `auth` endpoints always have an authenticated user. ## [Optional authentication](https://encore.dev/docs/go/develop/auth\#optional-authentication) While Encore always calls the auth handler for API endpoints marked as `auth`, you can also call `public` API endpoints with authentication data. This can be useful for APIs that support both a "logged in" and "logged out" experience. For example, a site like Reddit might have a `post.List` endpoint that returns the list of posts, but if you're logged in it also includes whether or not you have upvoted or downvoted each post. To support such use cases, Encore runs the auth handler for `public` API endpoints if (and only if) the request includes any authentication information (such as the `Authorization` header). In that case, the request processing behavior varies depending on the value of the `error` returned from the auth handler: - If the error is nil, the request is considered to be an authenticated request and `auth.UID()` and `auth.Data()` will include the information the auth handler returned. - If the error is non-nil and the error code is `errs.Unauthenticated` (like shown above), the request continues as an unauthenticated request, behaving exactly as if there was no authentication data provided at all. - If the error is non-nil and the error code is anything else, the request is aborted and Encore returns that error to the caller. To be able to determine if the request has an authenticated user, check the second return value from `auth.UserID()`. ## [Overriding auth information](https://encore.dev/docs/go/develop/auth\#overriding-auth-information) Encore supports overriding the auth information for an outgoing request using the [`auth.WithContext`](https://pkg.go.dev/encore.dev/beta/auth#WithContext) function. This function returns a new context with the auth information set to the specified values. Note that this only affects the auth information passed along with the request, and not the current request being processed (if any). This function is often useful when testing APIs that use authentication. For example: ```go ctx := auth.WithContext(context.Background(), auth.UID("my-user-id"), &MyAuthData{Email: "hello@example.com"}) // ... Make an API call using `ctx` to override the auth information for that API call. ``` [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) [Configuration](https://encore.dev/docs/go/develop/config) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/auth.md) ## Encore Configuration Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Configuration files let you define default behavior for your application, and override it for specific environments. This allows you to make changes without affecting deployments in other environments. Encore supports configuration files written in [CUE](https://cuelang.org/), which is a superset of JSON. It adds the following: - C-style comments - Quotes may be omitted from field names without special characters - Commas at the end of fields are optional - A comma after last element in list is allowed - The outer curly braces on the file are optional - [Expressions](https://cuelang.org/docs/tutorials/tour/expressions/) such as interpolation, comprehensions and conditionals are supported. ##### Take care For sensitive data use Encore's [secrets management](https://encore.dev/docs/go/primitives/secrets) functionality instead of configuration. ## [Using Config](https://encore.dev/docs/go/develop/config\#using-config) Inside your service, you can call `config.Load[*SomeConfigType]()` to load the config. This must be done at the package level, and not inside a function. See more in the [package documentation](https://pkg.go.dev/encore.dev/config#Load). Here's an example implementation: ```go package mysvc import ( "encore.dev/config" ) type SomeConfigType struct { ReadOnly config.Bool // Put the system into read-only mode Example config.String } var cfg *SomeConfigType = config.Load[*SomeConfigType]() ``` The type you pass as a type parameter to this function will be used to generate a `encore.gen.cue` file in your services directory. This file will contain both the CUE definition for your configuration type, and some [metadata](https://encore.dev/docs/go/develop/config#provided-meta-values) that Encore will provide to your service at runtime. This allows you to change the final value of your configuration based on the environment the application is running in. Any files ending with `.cue` in your service directory or sub-directories will be loaded by Encore and given to CUE to unify and compute a final configuration. Example CUE files ```cue mysvc/encore.gen.cue mysvc/myconfig.cue // Code generated by encore. DO NOT EDIT. package mysvc #Meta: { APIBaseURL: string Environment: { Name: string Type: "production" | "development" | "ephemeral" | "test" Cloud: "aws" | "gcp" | "encore" | "local" } } #Config: { ReadOnly: bool // Put the system into read-only mode Example: string } #Config ``` ##### Please note Loading configuration is only supported in services and the loaded data can not be referenced from packages outside that service. ### [CUE tags in Go Structs](https://encore.dev/docs/go/develop/config\#cue-tags-in-go-structs) You can use the `cue` tag in your Go to specify additional constraints on your configuration. For example: ```go type FooBar { A int `cue:">100"` B int `cue:"A-50"` // If A is set, B can be inferred by CUE C int `cue:"A+B"` // Which then allows CUE to infer this too } var _ = config.Load[*FooBar]() ``` Will result in the following CUE type definition being generated: ```cue #Config: { A: int & >100 B: int & A-50 // If A is set, B can be inferred by CUE C: int & A+B // Which then allows CUE to infer this too } ``` ## [Config Wrappers](https://encore.dev/docs/go/develop/config\#config-wrappers) Encore provides type wrappers for config in the form of `config.Value[T]` and `config.Values[T]` which expand into functions of type `T` and `[]T` respectively. These functions allow you to override the default value of your configuration in your CUE files inside tests, where only code run from that test will see the override. In the future we plan to support real-time updating of configuration values on running applications, thus using these wrappers in your configuration today will future proof your code and allow you to automatically take advantage of this feature when it is available. Any type supported in API requests and responses can be used as the type for a config wrapper. However for convenience, Encore ships with the following inbuilt aliases for the config wrappers: - `config.String`, `config.Bool`, `config.Int`, `config.Uint`, `config.Int8`, `config.Int16`, `config.Int32`, `config.In64`, `config.Uint8`, `config.Uint16`, `config.Uint32`, `config.Uint64`, `config.Float32`, `config.Float64`, `config.Bytes`, `config.Time`, `config.UUID` Example Application using Wrappers ```go svc/svc.go svc/servers.cue type mysvc import ( "encore.dev/config" ) type Server struct { // The config wrappers do not have to be in the top level struct Enabled config.Bool Port config.Int } type SvcConfig struct { GameServerPorts config.Values[Server] } var cfg = config.Load[*SvcConfig]() func startServers() { for _, server := range cfg.GameServerPorts() { if server.Enabled() { go startServer(server.Port()) } } } func startServer(port int) { // ... } ``` ## [Provided Meta Values](https://encore.dev/docs/go/develop/config\#provided-meta-values) When your application is running, Encore will provide information about that environment to your CUE files, which you can use to filter on. These fields can be found in the `encore.gen.cue` file which Encore will generate when you add a call to load config. Encore provides the following meta values: - **APIBaseURL**: The base URL of the Encore API, which can be used to make API calls to the application. - **Environment**: A struct containing information about the environment the application is running in. **Name**: The name of the environment **Type**: One of `production`, `development`, `ephemeral` or `test`. **Cloud**: The cloud the app is running on, which is one of `aws`, `gcp`, `encore` or `local`. The following are useful conditionals you can use in your CUE files: ```cue // An application running due to `encore run` if #Meta.Environment.Type == "development" && #Meta.Environment.Cloud == "local" {} // An application running in a development environment in the Cloud if #Meta.Environment.Type == "development" && #Meta.Environment.Cloud != "local" {} // An application running in a production environment if #Meta.Environment.Type == "production" {} // An application running in an environment that Encore has created // for an open Pull Request on Github if #Meta.Environment.Type == "ephemeral" {} ``` ## [Testing with Config](https://encore.dev/docs/go/develop/config\#testing-with-config) Through the provided meta values, your applications configuration can have different values in tests, compared to when the application is running. This can be useful to prevent external side effects from your tests, such as emailing customers across all test. Sometimes however, you may want to test specific behaviors based on different configurations (such as disabling user signups), in this scenario using the Meta data does not give you fine enough control. To allow you to set a configuration value at a per test level, Encore provides the helper function [`et.SetCfg`](https://pkg.go.dev/encore.dev/et#SetCfg). You can use this function to set a new value only in the current test and any sub tests, while all other tests will continue to use the value defined in the CUE files. ```cue config.cue signup.go signup_test.go // By default we want to send emails SendEmails: bool | *true // But in all tests we want to disable emails if #Meta.Environment.Type == "test" { SendEmails: false } ``` ## [Useful CUE Patterns](https://encore.dev/docs/go/develop/config\#useful-cue-patterns) If you're new to CUE, we'd recommend checking out the [CUE documentation](https://cuelang.org/docs/) and [cuetorials](https://cuetorials.com/), however to get you started, here are some useful patterns you can use in your CUE files. ### [Defaults](https://encore.dev/docs/go/develop/config\#defaults) CUE supports the concept of a default value, which it will use if no other concrete value is provided. This can be useful for when you normally want one value, but occasionally might want to provide an override in a certain scenario. A default value is specified by prefixing it with a `*`. ```cue // ReadOnlyMode is a boolean and if we don't provide a value, it // will default to false. ReadOnlyMode: bool | *false if #Meta.Environment.Name == "old-prod" { // On this environment, we want to set ReadOnlyMode to true ReadOnlyMode: true } ``` ### [Validation within CUE](https://encore.dev/docs/go/develop/config\#validation-within-cue) Any field prefixed with an `_` will not be exported to the concrete configuration once evaluated by CUE and can be used to hold intermediate values. Because CUE allows you to define the same field as many times as you want, as long as the values unify, we can build complex validation logic. ```cue import ( "list" // import CUE's list package ) // Set some port numbers defaulting just to 8080 // but in development including 8443 portNumbers: [...int] | *[8080] if #Meta.Environment.Type == "development" { portNumbers: [8080, 8443] } // Port numbers must be an array and all values // are integers 1024 or above. portNumbers: [...int & >= 1024] // The ports are considered valid if they contain the port number 8080. _portsAreValid: list.Contains(portNumbers, 8080) // Ensure that the ports are valid by constraining the value to be true. // CUE will report an error if the value is false (that is if the portNumbers list // does not contain the value 8080). _portsAreValid: true ``` ### [Switch Statements](https://encore.dev/docs/go/develop/config\#switch-statements) If statements in CUE do not have else branches, which can make it difficult to write complex conditionals, we however can use an array to emulate a switch statement, where the first value that matches the condition is returned. The following example will set `SendEmailsFrom` to a single string. ```cue SendEmailsFrom: [\ // These act as individual case statements\ if #Meta.Environment.Type == "production" { "noreply@example.com" },\ if #Meta.Environment.Name == "staging" { "staging@example.com" },\ \ // This last value without a condition acts as the default case\ "dev-system@example.dev",\ ][0] // Return the first value which matches the condition ``` ### [Using Map Keys as Values](https://encore.dev/docs/go/develop/config\#using-map-keys-as-values) CUE allows us to extract map keys and use them as values to simplify the config we need to write and minimize duplication. ```cue // Define the type we want to use #Server: { server: string port: int & > 1024 enabled: bool | *true } // Specify that servers is a map of strings to #Server // where they key we assign the variable Name servers: [Name=string]: #Server & { // Then we union the key with the value of server server: Name } servers: { "Foo": { port: 8080 }, "Bar": { port: 8081 enabled: false }, } ``` This will result in the concrete configuration of: ```json { "servers": { "Foo": { "server": "Foo", "port": 8080, "enabled": true }, "Bar": { "server": "Bar", "port": 8081, "enabled": false } } } ``` [Authentication](https://encore.dev/docs/go/develop/auth) [CORS](https://encore.dev/docs/go/develop/cors) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/config.md) ## CORS Configuration Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) CORS is a web security concept that defines which website origins are allowed to access your API. A deep-dive into CORS is out of scope for this documentation, but [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) provides a good overview. In short, CORS affects requests made by browsers to resources hosted on other origins (a combination of the scheme, domain, and port). ## [Configuring CORS](https://encore.dev/docs/go/develop/cors\#configuring-cors) Encore provides a default CORS configuration that is suitable for many APIs. You can override these settings by specifying the `global_cors` key in the `encore.app` file, which has the following structure: ```cue { // debug enables CORS debug logging. "debug": true | false, // allow_headers allows an app to specify additional headers that should be // accepted by the app. // // If the list contains "*", then all headers are allowed. "allow_headers": [...string], // expose_headers allows an app to specify additional headers that should be // exposed from the app, beyond the default set always recognized by Encore. // // If the list contains "*", then all headers are exposed. "expose_headers": [...string], // allow_origins_without_credentials specifies the allowed origins for requests // that don't include credentials. If nil it defaults to allowing all domains // (equivalent to ["*"]). "allow_origins_without_credentials": [...string], // allow_origins_with_credentials specifies the allowed origins for requests // that include credentials. If a request is made from an Origin in this list // Encore responds with Access-Control-Allow-Origin: . // // The URLs in this list may include wildcards (e.g. "https://*.example.com" // or "https://*-myapp.example.com"). "allow_origins_with_credentials": [...string], } ``` ## [Allowed origins](https://encore.dev/docs/go/develop/cors\#allowed-origins) The main CORS configuration is the list of allowed origins, meaning which websites are allowed to access your API (via browsers). For this purpose, CORS makes a distinction between requests that contain authentication information (cookies, HTTP authentication, or client certificates) and those that do not. CORS applies stricter rules to authenticated requests. By default, Encore allows unauthenticated requests from all origins but disallows requests that do include authorization information from other origins. This is a good default for many APIs. This can be changed by setting the `allow_origins_without_credentials` key (see above). For convenience Encore also allows all origins when developing locally. For security reasons it's necessary to explicitly specify which origins are allowed to make authenticated requests. This is done by setting the `allow_origins_with_credentials` key (see above). ## [Allowed headers and exposed headers](https://encore.dev/docs/go/develop/cors\#allowed-headers-and-exposed-headers) CORS also lets you specify which headers are allowed to be sent by the client ("allowed headers"), and which headers are exposed to scripts running in the browser ("exposed headers"). Encore automatically configures headers by parsing your program using static analysis. If your API defines a request or response type that contains a header field, Encore automatically adds the header to the list of exposed and allowed headers in request types respectively. To add additional headers to these lists, you can set the `allow_headers` and `expose_headers` keys (see above). This can be useful when your application relies on custom headers in e.g. raw endpoints that aren't seen by Encore's static analysis. [Configuration](https://encore.dev/docs/go/develop/config) [Metadata](https://encore.dev/docs/go/develop/metadata) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/cors.md) ## Encore Metadata API Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) While Encore tries to provide a cloud-agnostic environment, sometimes it's helpful to know more about the environment your application is running in. For this reason Encore provides an API for accessing metadata about the [application](https://encore.dev/docs/go/develop/metadata#application-metadata) and the environment it's running in, as well as information about the [current request](https://encore.dev/docs/go/develop/metadata#current-request) as part of the `encore.dev` package. ## [Application Metadata](https://encore.dev/docs/go/develop/metadata\#application-metadata) Calling `encore.Meta()` will return an [encore.AppMetadata](https://pkg.go.dev/encore.dev/#AppMetadata) instance which contains information about the application, including: - `AppID` \- the application name. - `APIBaseURL` \- the URL the application API can be publicly accessed on. - `Environment` \- the [environment](https://encore.dev/docs/platform/deploy/environments) the application is currently running in. - `Build` \- the revision information of the build from the version control system. - `Deploy` \- the deployment ID and when this version of the app was deployed. ## [Current Request](https://encore.dev/docs/go/develop/metadata\#current-request) `encore.CurrentRequest()` can be called from anywhere within your application and will return an [encore.Request](https://pkg.go.dev/encore.dev/#Request) instance which will provides information about why the current code is running. The [encore.Request](https://pkg.go.dev/encore.dev/#Request) type contains information about the running request, such as: - The service and endpoint being called - Path and path parameter information - When the request started This works automatically as a result of Encore's request tracking, and works even in other goroutines that were spawned during request handling. If no request is processed by the caller, which can happen if you call it during service initialization, the Type field returns None. If `CurrentRequest()` is called from a goroutine spawned during request processing it will continue to report the same request even if the request handler has already returned. This can be useful on [raw endpoints](https://encore.dev/docs/go/primitives/raw-endpoints) with [path parameters](https://encore.dev/docs/go/primitives/defining-apis#rest-apis) as the standard `http.Request` object passed into the raw endpoint does not provide access to the parsed path parameters, however by calling `encore.CurrentRequest().PathParams()` you can get access to the parsed path parameters. ## [Example Use Cases](https://encore.dev/docs/go/develop/metadata\#example-use-cases) ### [Using Cloud Specific Services](https://encore.dev/docs/go/develop/metadata\#using-cloud-specific-services) All the [clouds](https://encore.dev/docs/platform/deploy/own-cloud) contain a large number of services, not all of which Encore natively supports. By using information about the [environment](https://encore.dev/docs/platform/deploy/environments), you can define the implementation of these and use different services for each environment's provider. For instance if you are pushing audit logs into a data warehouse, when running on GCP you could use BigQuery, but when running on AWS you could use Redshift, when running locally you could simply write them to a file. ```go package audit import ( "encore.dev" "encore.dev/beta/auth" ) func Audit(ctx context.Context, action message, user auth.UID) error { switch encore.Meta().Environment.Cloud { case encore.CloudAWS: return writeIntoRedshift(ctx, action, user) case encore.CloudGCP: return writeIntoBigQuery(ctx, action, user) case encore.CloudLocal: return writeIntoFile(ctx, action, user) default: return fmt.Errorf("unknown cloud: %s", encore.Meta().Environment.Cloud) } } ``` ### [Checking Environment type](https://encore.dev/docs/go/develop/metadata\#checking-environment-type) When implementing a signup system, you may want to skip email verification on user signups when developing the application. Using the `encore.Meta()` API, we can check the environment and decide whether to send an email or simply mark the user as verified upon signup. ```go package user import "encore.dev" //encore:api public func Signup(ctx context.Context, params *SignupParams) (*SignupResponse, error) { // ... // If this is a testing environment, skip sending the verification email switch encore.Meta().Environment.Type { case encore.EnvTest, encore.EnvDevelopment: if err := MarkEmailVerified(ctx, userID); err != nil { return nil, err } default: if err := SendVerificationEmail(ctx, userID); err != nil { return nil, err } } // ... } ``` [CORS](https://encore.dev/docs/go/develop/cors) [Middleware](https://encore.dev/docs/go/develop/middleware) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/metadata.md) ## Encore Middleware Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Middleware is a way to write reusable code that runs before or after (or both) the handling of API requests, often across several (or all) API endpoints. It's commonly used to implement cross-cutting concerns like [request logging](https://encore.dev/docs/go/observability/logging), [authentication](https://encore.dev/docs/go/develop/auth), [tracing](https://encore.dev/docs/go/observability/tracing), and so on. One of the benefits of Encore is that all of these use cases are already handled out-of-the-box, so there's no need to use middleware for those things. Nonetheless, there are several use cases where it can be useful to write reusable functionality that applies to multiple API endpoints, and middleware is a good solution in those cases. Encore provides built-in support for middleware by defining a function with the `//encore:middleware` directive. The middleware directive takes a `target` parameter that specifies which API endpoints it applies to. ## [Middleware functions](https://encore.dev/docs/go/develop/middleware\#middleware-functions) A typical middleware implementation looks like this: ```go import ( "encore.dev/beta/errs" "encore.dev/middleware" ) //encore:middleware global target=all func ValidationMiddleware(req middleware.Request, next middleware.Next) middleware.Response { // If the payload has a Validate method, use it to validate the request. payload := req.Data().Payload if validator, ok := payload.(interface { Validate() error }); ok { if err := validator.Validate(); err != nil { // If the validation fails, return an InvalidArgument error. err = errs.WrapCode(err, errs.InvalidArgument, "validation failed") return middleware.Response{Err: err} } } return next(req) } ``` Middleware forms a chain, allowing each middleware to introspect and process the incoming request before handing it off to the next middleware by calling the `next` function that's passed in as an argument. For the last middleware in the chain, calling `next` results in the actual API handler being called. The `req` parameter provides information about the incoming request (see [package docs](https://pkg.go.dev/encore.dev/middleware#Request)). The `next` function returns a [`middleware.Response`](https://pkg.go.dev/encore.dev/middleware#Response) object which contains the response from the API, describing whether there was an error, and on success the actual response payload. This enables middleware to also introspect and even modify the outgoing response, like this: ```go //encore:middleware target=tag:cache func CachingMiddleware(req middleware.Request, next middleware.Next) middleware.Response { data := req.Data() // Check if we have the response cached. Use the request path as the cache key. cacheKey := data.Path if cached, err := loadFromCache(cacheKey, data.API.ResponseType); err == nil && cached != nil { return middleware.Response{Payload: cached} } // Otherwise forward the request to the handler return next(req) } ``` This uses `target=tag:cache` to have the middleware only apply to APIs that have that tag. More on this below in [Targeting APIs](https://encore.dev/docs/go/develop/middleware#targeting-apis). ##### Take care Middleware functions can also be defined as methods on a Dependency Injection struct declared with `//encore:service`. For example: ```go //encore:service type Service struct{} //encore:middleware target=all func (s *Service) MyMiddleware(req middleware.Request, next middleware.Next) middleware.Response { // ... } ``` See the [Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) docs for more information. ## [Middleware ordering](https://encore.dev/docs/go/develop/middleware\#middleware-ordering) Middleware can either be defined inside a service, in which case it only runs for APIs within that service, or it can be defined as a `global` middleware, in which case it applies to all services. For global middleware the `target` directive still applies and enables you to easily match a subset of APIs. ##### Take care Global middleware always run before all service-specific middleware, and then run in the order they are defined in the source code based on file name lexicographic ordering. To avoid surprises it's best to define all middleware in a file called `middleware.go` in each service, and to create a single top-level package to contain all global middleware. ## [Targeting APIs](https://encore.dev/docs/go/develop/middleware\#targeting-apis) The `target` directive can either be provided as `target=all` (meaning it applies to all APIs) or a list of tags, in the form `target=tag:foo,tag:bar`. Note that these tags are evaluated with `OR`, meaning the middleware applies to an API if the API has at least one of those tags. APIs can be defined with tags by adding `tag:foo` at the end of the `//encore:api` directive: ```go //encore:api public method=GET path=/user/:id tag:cache func GetUser(ctx context.Context, id string) (*User, error) { // ... } ``` [Metadata](https://encore.dev/docs/go/develop/metadata) [Testing](https://encore.dev/docs/go/develop/testing) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/middleware.md) ## Encore Testing Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Go comes with excellent built-in support for automated tests. Encore builds on top of this foundation, and lets you write tests in exactly the same way. We won't cover the basics of how to write tests here, see [the official Go docs](https://golang.org/pkg/testing/) for that. Let's instead focus on the difference between testing in Encore compared to a standard Go application. The main difference is that since Encore requires an extra compilation step, you must run your tests using `encore test` instead of `go test`. This is a wrapper that compiles the Encore app and then runs `go test`. It supports all the same flags that the `go test` command does. For example, use `encore test ./...` to run tests in all sub-directories, or just `encore test` for the current directory. ## [Test tracing](https://encore.dev/docs/go/develop/testing\#test-tracing) Encore comes with built-in test tracing for local development. You only need to open Encore's local development dashboard at [localhost:9400](http://localhost:9400/) to see traces for all your tests. This makes it very simple to understand the root cause for why a test is failing. ![](https://encore.dev/assets/docs/test_trace.png) ## [Integration testing](https://encore.dev/docs/go/develop/testing\#integration-testing) Since Encore removes almost all boilerplate, most of the code you write is business logic that involves databases and calling APIs between services. Such behavior is most easily tested with integration tests. When running tests, Encore automatically sets up the databases you need in a separate database cluster. They are additionally configured to skip `fsync` and to use an in-memory filesystem since durability is not a concern for automated tests. This drastically reduces the speed overhead of writing integration tests. In general, Encore applications tend to focus more on integration tests compared to traditional applications that are heavier on unit tests. This is nothing to worry about and is the recommended best practice. ### [Temporary databases](https://encore.dev/docs/go/develop/testing\#temporary-databases) When Encore runs tests, by default it reuses the same database for all tests, to improve performance. However, this means that you need to take care when writing tests to ensure tests don't interfere with each other. If you instead want to have a separate database for a given test, you can use [`et.NewTestDatabase`](https://pkg.go.dev/encore.dev/et#NewTestDatabase) to create a temporary database that only exists for the duration of the test. The temporary test database is a fully-migrated database. It does not include any data written by other tests. ##### Please note Under the hood, when you start running tests, Encore sets up a fresh "template database" and runs the database migrations against that database. When you later call `et.NewTestDatabase`, Encore creates a new database by cloning the template database. ### [Service Structs](https://encore.dev/docs/go/develop/testing\#service-structs) In tests, [service structs](https://encore.dev/docs/go/primitives/service-structs) are initialized on demand when the first API call is made to that service and then that instance of the service struct for all future tests. This means your tests can run faster as they don't have to each initialize all the service struct's each time a new test starts. However, in some situations you might be storing state in the service struct that would interfere with other tests. When you have a test you want to have its own instance of the service struct, you can use the `et.EnableServiceInstanceIsolation()` function within the test to enable this for just that test, while the rest of your tests will continue to use the shared instance. ## [Test-only infrastructure](https://encore.dev/docs/go/develop/testing\#test-only-infrastructure) Encore allows tests to define infrastructure resources specifically for testing. This can be useful for testing library code that interacts with infrastructure. For example, the [x.encore.dev/pubsub/outbox](https://pkg.go.dev/x.encore.dev/infra/pubsub/outbox) package defines a test-only database that is used to do integration testing of the outbox functionality. ## [Testing from your IDE](https://encore.dev/docs/go/develop/testing\#testing-from-your-ide) ### [GoLand / IntelliJ](https://encore.dev/docs/go/develop/testing\#goland--intellij) Encore has an officially supported plugin [available in the JetBrains marketplace](https://plugins.jetbrains.com/plugin/20010-encore). It lets you run unit tests directly from within your IDE with support for debug mode and breakpoints. ### [Visual Studio Code (VS Code)](https://encore.dev/docs/go/develop/testing\#visual-studio-code-vs-code) There's no official VS Code plugin available yet, but we are happy to include your contribution if you build one. Reach out on [Discord](https://encore.dev/discord) if you need help to get started. For advice on debugging when using VS Code, see the [Debugging docs](https://encore.dev/docs/go/how-to/debug). [Middleware](https://encore.dev/docs/go/develop/middleware) [Mocking](https://encore.dev/docs/go/develop/testing/mocking) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/testing.md) ## Mocking in Encore Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore comes with built-in support for mocking out APIs and services, which makes it easier to test your application in isolation. ## [Mocking Endpoints](https://encore.dev/docs/go/develop/testing/mocking\#mocking-endpoints) Let's say you have an endpoint that calls an external API in our `products` service: ```go //encore:api private func GetPrice(ctx context.Context, p *PriceParams) (*PriceResponse, error) { // Call external API to get the price } ``` When testing this function, you don't want to call the real external API since that would be slow and cause your tests to fail if the API is down. Instead, you want to mock out the API call and return a fake response. In Encore, you can do this by adding a mock implementation of the endpoint using the `et.MockEndpoint` function inside your test: ```go package shoppingcart import ( "context" "testing" "encore.dev/et" // Encore's test support package "your_app/products" ) func Test_Something(t *testing.T) { t.Parallel() // Run this test in parallel with other tests without the mock implementation interfering // Create a mock implementation of pricing API which will only impact this test and any sub-tests et.MockEndpoint(products.GetPrice, func(ctx context.Context, p *products.PriceParams) (*products.PriceResponse, error) { return &products.PriceResponse{Price: 100}, nil }) // ... the rest of your test code here ... } ``` When any code within the test, or any sub-test calls the `GetPrice` API, the mock implementation will be called instead. The mock will not impact any other tests running in parallel. The function you pass to `et.MockEndpoint` must have the same signature as the real endpoint. If you want to mock out the API for all tests in the package, you can add the mock implementation to the `TestMain` function: ```go package shoppingcart import ( "context" "os" "testing" "encore.dev/et" "your_app/products" ) func TestMain(m *testing.M) { // Create a mock implementation of pricing API which will impact all tests within this package et.MockEndpoint(products.GetPrice, func(ctx context.Context, p *products.PriceParams) (*products.PriceResponse, error) { return &products.PriceResponse{Price: 100}, nil }) // Now run the tests os.Exit(m.Run()) } ``` Mocks can be changed at any time, including removing them by setting the mock implementation to `nil`. ## [Mocking services](https://encore.dev/docs/go/develop/testing/mocking\#mocking-services) As well as mocking individual APIs, you can also mock entire services. This can be useful if you want to inject a different set of dependencies into your service for testing, or a service that your code depends on. This can be done using the `et.MockService` function: ```go package shoppingcart import ( "context" "testing" "encore.dev/et" // Encore's test support package "your_app/products" ) func Test_Something(t *testing.T) { t.Parallel() // Run this test in parallel with other tests without the mock implementation interfering // Create a instance of the products service which will only impact this test and any sub-tests et.MockService("products", &products.Service{ SomeField: "a testing value", }) // ... the rest of your test code here ... } ``` When any code within the test, or any sub-test calls the `products` service, the mock implementation will be called instead. Unlike `et.MockEndpoint`, the mock implementation does not need to have the same signature, and can be any object. The only requirement is that any of the services APIs that are called during the test must be implemented by as a receiver method on the mock object. (This also includes APIs that are defined as package level functions in the service, and are not necessarily defined as receiver methods on that services struct). To help with compile time safety on service mocking, for every service Encore will automatically generate an `Interface` interface which contains all the APIs defined in the service. This interface can be passed as a generic argument to `et.MockService` to ensure that the mock object implements all the APIs defined in the service: ```go type myMockObject struct{} func (m *myMockObject) GetPrice(ctx context.Context, p *products.PriceParams) (*products.PriceResponse, error) { return &products.PriceResponse{Price: 100}, nil } func Test_Something(t *testing.T) { t.Parallel() // Run this test in parallel with other tests without the mock implementation interfering // This will cause a compile time error if myMockObject does not implement all the APIs defined in the products service et.MockService[products.Interface]("products", &myMockObject{}) } ``` ### [Automatic generation of mock objects](https://encore.dev/docs/go/develop/testing/mocking\#automatic-generation-of-mock-objects) Thanks to the generated `Interface` interface, it's possible to automatically generate mock objects for your services using either [Mockery](https://vektra.github.io/mockery/latest/) or [GoMock](https://github.com/uber-go/mock). [Testing](https://encore.dev/docs/go/develop/testing) [Validation](https://encore.dev/docs/go/develop/validation) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/mocking.md) ## Request Validation Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) When receiving incoming requests it's best practice to validate the payload to make sure it meets your expectations, contains all the necessary fields, and so on. Encore provides an out-of-the-box middleware that automatically validates incoming requests if the request type implements the method `Validate() error`. If it does, Encore will call this method after deserializing the request payload, and only call your API handler (and other middleware) if the validation function returns `nil`. If the validation function returns an [`*errs.Error`](https://encore.dev/docs/go/primitives/api-errors) that error is reported unmodified to the caller. Other errors are converted to an `*errs.Error` with code `InvalidArgument`, which results in a HTTP response with status code `400 Bad Request`. This design means that it's easy to use your validation library of choice. In the future we're looking to provide an out-of-the-box validation library for an even better developer experience. [Mocking](https://encore.dev/docs/go/develop/testing/mocking) [Environment Variables](https://encore.dev/docs/go/develop/env-vars) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/validation.md) ## Encore Environment Variables Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore works out of the box without configuration, but provides several environment variables for advanced use cases such as debugging, testing, or adapting Encore to specific workflow requirements. ## [Daemon & Development Dashboard](https://encore.dev/docs/go/develop/env-vars\#daemon--development-dashboard) These variables control how the Encore daemon operates and where it exposes its services. ### [ENCORE\_DAEMON\_LOG\_PATH](https://encore.dev/docs/go/develop/env-vars\#encore_daemon_log_path) Controls the location of the Encore daemon log file. **Default:**`/encore/daemon.log` **Example:** ```bash export ENCORE_DAEMON_LOG_PATH=/var/log/encore/daemon.log ``` ### [ENCORE\_DEVDASH\_LISTEN\_ADDR](https://encore.dev/docs/go/develop/env-vars\#encore_devdash_listen_addr) Overrides the listen address for the local development dashboard. **Default:** Automatically assigned by the daemon **Format:** Network address (e.g., `localhost:9400`) **Example:** ```bash export ENCORE_DEVDASH_LISTEN_ADDR=localhost:8080 encore run ``` ### [ENCORE\_MCPSSE\_LISTEN\_ADDR](https://encore.dev/docs/go/develop/env-vars\#encore_mcpsse_listen_addr) Overrides the listen address for the MCP SSE (Model Context Protocol Server-Sent Events) endpoint. **Default:** Automatically assigned by the daemon **Format:** Network address **Example:** ```bash export ENCORE_MCPSSE_LISTEN_ADDR=localhost:9401 ``` ### [ENCORE\_OBJECTSTORAGE\_LISTEN\_ADDR](https://encore.dev/docs/go/develop/env-vars\#encore_objectstorage_listen_addr) Overrides the listen address for the object storage service endpoint. **Default:** Automatically assigned by the daemon **Format:** Network address **Example:** ```bash export ENCORE_OBJECTSTORAGE_LISTEN_ADDR=localhost:9402 ``` ## [Advanced Development](https://encore.dev/docs/go/develop/env-vars\#advanced-development) These variables are primarily useful for advanced development scenarios, such as contributing to Encore itself or using custom builds. ### [ENCORE\_RUNTIMES\_PATH](https://encore.dev/docs/go/develop/env-vars\#encore_runtimes_path) Specifies the path to the Encore runtimes directory. **Default:** Auto-detected relative to the Encore installation (`/runtimes`) **Example:** ```bash export ENCORE_RUNTIMES_PATH=/path/to/custom/runtimes ``` ### [ENCORE\_GOROOT](https://encore.dev/docs/go/develop/env-vars\#encore_goroot) Specifies the path to the custom Encore Go runtime. **Default:** Auto-detected relative to the Encore installation (`/encore-go`) **Example:** ```bash export ENCORE_GOROOT=/path/to/custom/encore-go ``` ##### Please note For most users, these paths are automatically detected and don't need to be set. They are primarily useful when contributing to Encore or testing custom builds. [Validation](https://encore.dev/docs/go/develop/validation) [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/develop/env-vars.md) ## Encore CLI Reference Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) ## [Running](https://encore.dev/docs/go/cli/cli-reference\#running) #### [Run](https://encore.dev/docs/go/cli/cli-reference\#run) Runs your application. ```bash $ encore run [--debug] [--watch=true] [--port NUMBER] [flags] ``` #### [Test](https://encore.dev/docs/go/cli/cli-reference\#test) Tests your application Takes all the same flags as `go test`. ```bash $ encore test ./... [go test flags] ``` #### [Check](https://encore.dev/docs/go/cli/cli-reference\#check) Checks your application for compile-time errors using Encore's compiler. ```bash $ encore check ``` #### [Exec](https://encore.dev/docs/go/cli/cli-reference\#exec) Runs executable scripts against the local Encore app. Compiles and runs a go script with the local Encore app environment setup. ```shell $ encore exec [...args] ``` The command directory should contain Go files with package main with a main function. The additional arguments are passed directly to the built binary. ##### [Example](https://encore.dev/docs/go/cli/cli-reference\#example) Run a database seed script ```shell $ encore exec cmd/seed ``` ## [App](https://encore.dev/docs/go/cli/cli-reference\#app) Commands to create and link Encore apps #### [Clone](https://encore.dev/docs/go/cli/cli-reference\#clone) Clone an Encore app to your computer ```bash $ encore app clone [app-id] [directory] ``` #### [Create](https://encore.dev/docs/go/cli/cli-reference\#create) Create a new Encore app ```bash $ encore app create [name] ``` ### [Init](https://encore.dev/docs/go/cli/cli-reference\#init) Create a new Encore app from an existing repository ```bash $ encore app init [name] ``` #### [Link](https://encore.dev/docs/go/cli/cli-reference\#link) Link an Encore app with the server ```bash $ encore app link [app-id] ``` ## [Auth](https://encore.dev/docs/go/cli/cli-reference\#auth) Commands to authenticate with Encore #### [Login](https://encore.dev/docs/go/cli/cli-reference\#login) Log in to Encore ```bash $ encore auth login ``` #### [Logout](https://encore.dev/docs/go/cli/cli-reference\#logout) Logs out the currently logged in user ```bash $ encore auth logout ``` #### [Signup](https://encore.dev/docs/go/cli/cli-reference\#signup) Create a new Encore account ```bash $ encore auth signup ``` #### [Whoami](https://encore.dev/docs/go/cli/cli-reference\#whoami) Show the current logged in user ```bash $ encore auth whoami ``` ## [Daemon](https://encore.dev/docs/go/cli/cli-reference\#daemon) Encore CLI daemon commands #### [Restart](https://encore.dev/docs/go/cli/cli-reference\#restart) If you experience unexpected behavior, try restarting the daemon using: ```bash $ encore daemon ``` #### [Env](https://encore.dev/docs/go/cli/cli-reference\#env) Outputs Encore environment information ```bash $ encore daemon env ``` ## [Database Management](https://encore.dev/docs/go/cli/cli-reference\#database-management) Database management commands #### [Connect to database via shell](https://encore.dev/docs/go/cli/cli-reference\#connect-to-database-via-shell) Connects to the database via psql shell Defaults to connecting to your local environment. Specify --env to connect to another environment. ```bash $ encore db shell [--env=] ``` `encore db shell` defaults to read-only permissions. Use `--write`, `--admin` and `--superuser` flags to modify which permissions you connect with. #### [Connection URI](https://encore.dev/docs/go/cli/cli-reference\#connection-uri) Outputs a database connection string for ``. Defaults to connecting to your local environment. Specify --env to connect to another environment. ```bash $ encore db conn-uri [--env=] [flags] ``` #### [Proxy](https://encore.dev/docs/go/cli/cli-reference\#proxy) Sets up local proxy that forwards any incoming connection to the databases in the specified environment. ```bash $ encore db proxy [--env=] [flags] ``` #### [Reset](https://encore.dev/docs/go/cli/cli-reference\#reset) Resets the databases for the given services. Use --all to reset all databases. ```bash $ encore db reset [service-names...] [flags] ``` ## [Code Generation](https://encore.dev/docs/go/cli/cli-reference\#code-generation) Code generation commands #### [Generate client](https://encore.dev/docs/go/cli/cli-reference\#generate-client) Generates an API client for your app. For more information about the generated clients, see [this page](https://encore.dev/docs/go/cli/client-generation). By default, `encore gen client` generates the client based on the version of your application currently running in your local environment. You can change this using the `--env` flag and specifying the environment name. Use `--lang=` to specify the language. Supported language codes are: - `go`: A Go client using the net/http package - `typescript`: A TypeScript client using the in-browser Fetch API - `javascript`: A JavaScript client using the in-browser Fetch API - `openapi`: An OpenAPI spec ```bash $ encore gen client [] [--env=] [--services=foo,bar] [--excluded-services=baz,qux] [--lang=] [flags] ``` ## [Logs](https://encore.dev/docs/go/cli/cli-reference\#logs) Streams logs from your application ```bash $ encore logs [--env=prod] [--json] ``` ## [Kubernetes](https://encore.dev/docs/go/cli/cli-reference\#kubernetes) Kubernetes management commands #### [Configure](https://encore.dev/docs/go/cli/cli-reference\#configure) Updates your kubectl config to point to the Kubernetes cluster(s) for the specified environment ```bash $ encore k8s configure --env=ENV_NAME ``` ## [Secrets Management](https://encore.dev/docs/go/cli/cli-reference\#secrets-management) Secret management commands #### [Set](https://encore.dev/docs/go/cli/cli-reference\#set) Set a secret value for a specific environment: ```bash $ encore secret set --env ``` Set a secret value for an environment type: ```bash $ encore secret set --type ``` Where `` defines which environment types the secret value applies to. Use a comma-separated list of `production`, `development`, `preview`, and `local`. Shorthands: `prod`, `dev`, `pr`. **Examples** Entering a secret directly in terminal: ```bash $ encore secret set --type dev MySecret Enter secret value: ... Successfully created secret value for MySecret. ``` Piping a secret from a file: ```bash $ encore secret set --type dev,local MySecret < my-secret.txt Successfully created secret value for MySecret. ``` Note that this strips trailing newlines from the secret value. #### [List](https://encore.dev/docs/go/cli/cli-reference\#list) Lists secrets, optionally for a specific key ```bash $ encore secret list [keys...] ``` #### [Archive](https://encore.dev/docs/go/cli/cli-reference\#archive) Archives a secret value ```bash $ encore secret archive ``` #### [Unarchive](https://encore.dev/docs/go/cli/cli-reference\#unarchive) Unarchives a secret value ```bash $ encore secret unarchive ``` ## [Version](https://encore.dev/docs/go/cli/cli-reference\#version) Reports the current version of the encore application ```bash $ encore version ``` #### [Update](https://encore.dev/docs/go/cli/cli-reference\#update) Checks for an update of encore and, if one is available, runs the appropriate command to update it. ```bash $ encore version update ``` ## [VPN](https://encore.dev/docs/go/cli/cli-reference\#vpn) VPN management commands #### [Start](https://encore.dev/docs/go/cli/cli-reference\#start) Sets up a secure connection to private environments ```bash $ encore vpn start ``` #### [Status](https://encore.dev/docs/go/cli/cli-reference\#status) Determines the status of the VPN connection ```bash $ encore vpn status ``` #### [Stop](https://encore.dev/docs/go/cli/cli-reference\#stop) Stops the VPN connection ```bash $ encore vpn stop ``` ## [Build](https://encore.dev/docs/go/cli/cli-reference\#build) Generates an image for your app, which can be used to [self-host](https://encore.dev/docs/go/self-host/docker-build) your app. #### [Docker](https://encore.dev/docs/go/cli/cli-reference\#docker) Builds a portable Docker image of your Encore application. ```bash $ encore build docker ``` **Flags** `--base string` defines the base image to build from (default "scratch") `--push` pushes image to remote repository ## [LLM Rules](https://encore.dev/docs/go/cli/cli-reference\#llm-rules) Generate llm rules in an existing app #### [Init](https://encore.dev/docs/go/cli/cli-reference\#init-1) Initialize the llm rules files ```bash $ encore llm-rules init ``` [Environment Variables](https://encore.dev/docs/go/develop/env-vars) [Client Generation](https://encore.dev/docs/go/cli/client-generation) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/cli/cli-reference.md) ## Client Generation Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore makes it simple to write scalable distributed backends by allowing you to make function calls that Encore translates into RPC calls. Encore also generates API clients with interfaces that look like the original Go functions, with the same parameters and response signature as the server. The generated clients are single files that use only the standard functionality of the target language, with full type safety. This allow anyone to look at the generated client and understand exactly how it works. The structure of the generated code varies by language, to ensure it's idiomatic and easy to use, but always includes all publicly accessible endpoints, data structures, and documentation strings. Encore currently supports generating the following clients: - **Go** \- Using `net/http` for the underlying HTTP transport. - **TypeScript** \- Using the browser `fetch` API for the underlying HTTP client. - **JavaScript** \- Using the browser `fetch` API for the underlying HTTP client. - **OpenAPI** \- Using the OpenAPI Specification's language-agnostic interface to HTTP APIs. (Experimental) If there's a language you think should be added, please submit a pull request or create a feature request on [GitHub](https://github.com/encoredev/encore/issues/new), or [reach out on Discord](https://encore.dev/discord). ##### Take care If you ship the generated client to end customers, keep in mind that old clients will continue to be used after you make changes. To prevent issues with the generated clients, avoid making breaking changes in APIs that your clients access. ## [Generating a Client](https://encore.dev/docs/go/cli/client-generation\#generating-a-client) To generate a client, use the `encore gen client` command. It generates a type-safe client using the most recent API metadata running in a particular environment for the given Encore application. For example: ```bash # Generate a TypeScript client for calling the hello-a8bc application based on the primary environment encore gen client hello-a8bc --output=./client.ts # Generate a Go client for the hello-a8bc application based on the locally running code encore gen client hello-a8bc --output=./client.go --env=local # Generate an OpenAPI client for the hello-a8bc application based on the primary environment encore gen client hello-a8bc --lang=openapi --output=./openapi.json ``` ### [Environment Selection](https://encore.dev/docs/go/cli/client-generation\#environment-selection) By default, `encore gen client` generates the client based on the version of your application currently running in your local environment. You can change this using the `--env` flag and specifying the environment name. ##### Please note The generated client can be used with any environment, not just the one it was generated for. However, the APIs, data structures and marshalling logic will be based on whatever is present and running in that environment at the point in time the client is generated. ### [Service filtering](https://encore.dev/docs/go/cli/client-generation\#service-filtering) By default `encore gen client` outputs code for all services with at least one publicly accessible (or authenticated) API. You can narrow down this set of services by specifying the `--services` (or `-s`) flag. It takes a comma-separated list of service names. For example, to generate a typescript client for the `email` and `users` services, run: ```bash encore gen client --services=email,users -o client.ts ``` ### [Output Mode](https://encore.dev/docs/go/cli/client-generation\#output-mode) By default the client's code will be output to stdout, allowing you to pipe it into your clipboard, or another tool. However, using `--output` you can specify a file location to write the client to. If output is specified, you do not need to specify the language as Encore will detect the language based on the file extension. ### [Example Script](https://encore.dev/docs/go/cli/client-generation\#example-script) You could combine this into a `package.json` file for your Typescript frontend, to allow you to run `npm run gen` in that project to update the client to match the code running in your staging environment. ```json { "scripts": { // ... "gen": "encore gen client hello-a8bc --output=./client.ts --env=staging" // ... } } ``` ## [Using the Client](https://encore.dev/docs/go/cli/client-generation\#using-the-client) The generated client has all the data structures required as parameters or returned as response values as needed by any of the public or authenticated API's of your Encore application. Each service is exposed as object on the client, with each public or authenticated API exposed as a function on those objects. For instance, if you had a service called `email` with a function `Send`, on the generated client you would call this using; `client.email.Send(...)`. For more tips and examples of using a generated JavaScript/Typescript client, see the [Integrate with a web frontend](https://encore.dev/docs/how-to/integrate-frontend#generating-a-request-client) docs. ### [Creating an instance](https://encore.dev/docs/go/cli/client-generation\#creating-an-instance) When constructing a client, you need to pass a `BaseURL` as the first parameter; this is the URL at which the API can be accessed. The client provides two helpers: - `Local` \- This is a constant provided, which will always point at your locally running instance environment. - `Environment("name")` \- This is a function which allows you to specify an environment by name However, BaseURL is a string, so if the two helpers do not provide enough flexibility you can pass any valid URL to be used as the BaseURL. ### [Authentication](https://encore.dev/docs/go/cli/client-generation\#authentication) If your application has any API's which require [authentication](https://encore.dev/docs/develop/auth), then additional options will generated into the client, which can be used when constructing the client. Just like with API's schemas, the data type required by your application's `auth handler` will be part of the client library, allowing you to set it in two ways: If your credentials won't change during the lifetime of the client, simply passing the authentication data to the client through the `WithAuth` (Go) or `auth` (TypeScript) options. However, if the authentication credentials can change, you can also pass a function which will be called before each request and can return a new instance of the authentication data structure or return the existing instance. ### [HTTP Client Override](https://encore.dev/docs/go/cli/client-generation\#http-client-override) If required, you can override the underlying HTTP implementation with your own implementation. This is useful if you want to perform logging of the requests being made, or route the traffic over a secured tunnel such as a VPN. In Go this can be configured using the `WithHTTPClient` option. You are required to provide an implementation of the `HTTPDoer` interface, which the [http.Client](https://pkg.go.dev/net/http#Client) implements. For TypeScript clients, this can be configured using the `fetcher` option and must conform to the same prototype as the browsers inbuilt [fetch\\ API](https://developer.mozilla.org/en-US/docs/Web/API/fetch). ### [Structured Errors](https://encore.dev/docs/go/cli/client-generation\#structured-errors) Errors created or wrapped using Encore's [`errs package`](https://encore.dev/docs/develop/errors) will be returned to the client and deserialized as an `APIError`, allowing the client to perform adaptive error handling based on the type of error returned. You can perform a type check on errors caused by calling an API to see if it is an `APIError`, and once cast as an `APIError` you can access the `Code`, `Message` and `Details` fields. For TypeScript Encore generates a `isAPIError` type guard which can be used. The `Code` field is an enum with all the possible values generated in the library, alone with description of when we would expect them to be returned by your API. See the [errors documentation](https://encore.dev/docs/develop/errors#error-codes) for an online reference of this list. ## [Example CLI Tool](https://encore.dev/docs/go/cli/client-generation\#example-cli-tool) For instance, we could build a simple CLI application to use our [url shortener](https://encore.dev/docs/tutorials/rest-api), and handle any structured errors in a way which makes sense for that error code. ```go package main import ( "context" "fmt" "os" "time" "shorten_cli/client" ) func main() { // Create a new client with the default BaseURL client, err := client.New( client.Environment("production"), client.WithAuth(os.Getenv("SHORTEN_API_KEY")), ) if err != nil { panic(err) } // Timeout if the request takes more than 5 seconds ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Call the Shorten function in the URL service resp, err := client.Url.Shorten( ctx, client.UrlShortenParams{ URL: os.Args[1] }, ) if err != nil { // Check the error returned if err, ok := err.(*client.APIError); ok { switch err.Code { case client.ErrUnauthenticated: fmt.Println("SHORTEN_API_KEY was invalid, please check your environment") os.Exit(1) case client.ErrAlreadyExists: fmt.Println("The URL you provided was already shortened") os.Exit(0) } } panic(err) // if here then something has gone wrong in an unexpected way } fmt.Printf("https://short.encr.app/%s", resp.ID) } ``` [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/cli/client-generation.md) ## Infrastructure Namespaces Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore's CLI allows you to create and switch between multiple, independent _infrastructure namespaces_. Infrastructure namespaces are isolated from each other, and each namespace contains its own independent data. This makes it trivial to switch tasks, confident your old state and data will be waiting for you when you return. If you've ever worked on a new feature that involves making changes to the database schema, only to context switch to reviewing a Pull Request and had to reset your database, you know the feeling. With Encore's infrastructure namespaces, this is a problem of the past. Run `encore namespace switch --create pr:123` (or `encore ns switch -c pr:123` for short) to create and switch to a new namespace. The next `encore run` will run in the new namespace, with a completely fresh database. When you're done, run `encore namespace switch -` to switch back to your previous namespace. ## [Usage](https://encore.dev/docs/go/cli/infra-namespaces\#usage) Below are the commands for working with namespaces. Note that you can use `encore ns` as a short form for `encore namespace`. ```bash # List your namespaces (* indicates the current namespace) $ encore namespace list # Create a new namespace $ encore namespace create my-ns # Switch to a namespace $ encore namespace switch my-ns # Switch to a namespace, creating it if it doesn't exist $ encore namespace switch --create my-ns # Switch to the previous namespace $ encore namespace switch - # Delete a namespace (and all associated data) $ encore namespace delete my-ns ``` Most other Encore commands that interact or use infrastructure take an optional `--namespace` (`-n` for short) that overrides the current namespace. If left unspecified, the current namespace is used. For example: ```bash # Run the app using the "my-ns" namespace $ encore run --namespace my-ns # Open a database shell to the "my-ns" namespace $ encore db shell DATABASE_NAME --namespace my-ns # Reset all databases within the "my-ns" namespace $ encore db reset --all --namespace my-ns ``` [Client Generation](https://encore.dev/docs/go/cli/client-generation) [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/cli/infra-namespaces.md) ## Encore CLI Configuration Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) The Encore CLI has a number of configuration options to customize its behavior. Configuration options can be set both for individual Encore applications, as well as globally for the local user. Configuration options can be set using `encore config `, and options can similarly be read using `encore config `. When running `encore config` within an Encore application, it automatically sets and gets configuration for that application. To set or get global configuration, use the `--global` flag. ## [Configuration files](https://encore.dev/docs/go/cli/config-reference\#configuration-files) The configuration is stored in one ore more TOML files on the filesystem. The configuration is read from the following files, in order: ### [Global configuration](https://encore.dev/docs/go/cli/config-reference\#global-configuration) - `$XDG_CONFIG_HOME/encore/config` - `$HOME/.config/encore/config` - `$HOME/.encoreconfig` ### [Application-specific configuration](https://encore.dev/docs/go/cli/config-reference\#application-specific-configuration) - `$APP_ROOT/.encore/config` Where `$APP_ROOT` is the directory containing the `encore.app` file. The files are read and merged, in the order defined above, with latter files taking precedence over earlier files. ## [Configuration options](https://encore.dev/docs/go/cli/config-reference\#configuration-options) #### [run.browser](https://encore.dev/docs/go/cli/config-reference\#runbrowser) Type: string Default: auto Must be one of: always, never, or auto Whether to open the Local Development Dashboard in the browser on `encore run`. If set to "auto", the browser will be opened if the dashboard is not already open. [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) [Telemetry](https://encore.dev/docs/go/cli/telemetry) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/cli/config-reference.md) ## Encore Telemetry Overview Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Telemetry helps us improve the Encore by collecting usage data. This data provides insights into how Encore is used, enabling us to make informed decisions to enhance performance, add new features, and fix bugs more efficiently. Encore only collects telemetry data in the local development tools and the Encore Cloud dashboard. It does **not** collect any telemetry data from your running applications or cloud services, ensuring complete privacy and security for your operations. ## [Why We Collect Data](https://encore.dev/docs/go/cli/telemetry\#why-we-collect-data) We collect telemetry data for several important reasons: 1. **Improvement of Features**: Understanding which features are most used helps us prioritize improvements and new feature development. 2. **Performance Monitoring**: Tracking performance metrics enables us to identify and resolve issues, ensuring a smoother user experience. 3. **Bug Detection**: Telemetry data can help us detect and fix bugs faster by providing context on how and when issues occur. 4. **User Experience**: Insights from telemetry data guide us in making Encore more intuitive and user-friendly. ## [How Data is Collected](https://encore.dev/docs/go/cli/telemetry\#how-data-is-collected) Encore collects data in a way that prioritizes user privacy and security. Here's how we do it: 1. **User Identifiable Data**: The data collected includes identifiable information that helps us understand specific user interactions and contexts. 2. **Types of Data**: We collect data on usage patterns, performance metrics, and error reports. 3. **Secure Transmission**: All data is transmitted securely using industry-standard encryption protocols. 4. **Minimal Impact**: Data collection is designed to have minimal impact on Encore's performance. ### [Example of Data Being Sent](https://encore.dev/docs/go/cli/telemetry\#example-of-data-being-sent) Here is an example of the type of data that is sent: ```json { "event": "app.create", "anonymousId": "a-uuid-unique-for-the-installation", "properties": { "error": false, "lang": "go", "template": "graphql" } } ``` ## [Data We Don't Collect](https://encore.dev/docs/go/cli/telemetry\#data-we-dont-collect) At Encore, we prioritize your privacy and ensure that no sensitive data is collected through our telemetry. Specifically, we do not collect: 1. **Environment Variables**: We do not collect any environment variables set in your development or production environments. 2. **File Paths**: The specific paths of your files and directories are not collected. 3. **Contents of Files**: We do not access or collect the contents of your code files or any other files in your projects. 4. **Logs**: No log files from your application or development environment are collected. 5. **Serialized Errors**: We do not collect serialized errors that may contain sensitive information. Our goal is to gather useful data that helps improve Encore while ensuring that your sensitive information remains private and secure. ## [Disabling Telemetry](https://encore.dev/docs/go/cli/telemetry\#disabling-telemetry) While telemetry helps us improve Encore, we understand that some users may prefer to opt out. Disabling telemetry is straightforward and can be done in two ways: 1. **Using the CLI Command**: You can disable telemetry by executing a simple command in your terminal. ```sh encore telemetry disable ``` 2. **Setting an Environment Variable**: Alternatively, you can disable telemetry by setting the `DISABLE_ENCORE_TELEMETRY` environment variable. ```sh export DISABLE_ENCORE_TELEMETRY=1 ``` 3. **Confirmation**: After disabling telemetry, either by the CLI command or environment variable, you will receive a confirmation message indicating that telemetry has been successfully disabled. 4. **Re-enabling Telemetry**: If you decide to re-enable telemetry later, you can do so with the following CLI command: ```sh encore telemetry enable ``` ## [Debugging Telemetry](https://encore.dev/docs/go/cli/telemetry\#debugging-telemetry) For users who want more visibility into what telemetry data is being sent, you can enable debug mode: 1. **Setting Debug Mode**: Enable debug mode by setting the `ENCORE_TELEMETRY_DEBUG` environment variable. ```sh export ENCORE_TELEMETRY_DEBUG=1 ``` 2. **Log Statements**: When debug mode is enabled, a log statement prepended by `[telemetry]` will be printed every time telemetry data is sent. ## [Conclusion](https://encore.dev/docs/go/cli/telemetry\#conclusion) Telemetry is a vital tool for improving Encore, but we respect your choice regarding data sharing. With easy-to-use commands and environment variables, you can manage your telemetry settings as you see fit. If you have any further questions or need assistance, please refer to our support documentation or contact our support team. Thank you for helping us make Encore better! [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) [MCP](https://encore.dev/docs/go/cli/mcp) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/cli/telemetry.md) ## Encore MCP Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore provides an MCP server that implements the [Model Context Protocol](https://modelcontextprotocol.io/introduction), an open standard that enables large language models (LLMs) to access contextual information about your application. Think of MCP as a standardized interfaceβ€”like a "USB-C port for AI applications"β€”that connects your Encore app's data and functionality to any LLM that supports the protocol. You can connect to Encore's MCP server from any MCP host (such as Claude Desktop, IDEs, or other AI tools) using either Server-Sent Events (SSE) or stdio transport. To set up this connection, simply run: ```bash cd my-encore-app encore mcp start MCP Service is running! MCP SSE URL: http://localhost:9900/sse?app=your-app-id MCP stdio Command: encore mcp run --app=your-app-id ``` Copy the appropriate URL or command to your MCP host's configuration, and you're ready to give your AI assistants rich context about your application. ## [Example: Integrating with Cursor](https://encore.dev/docs/go/cli/mcp\#example-integrating-with-cursor) [Cursor](https://cursor.com/) is one of the most popular AI powered IDE's, and it's simple to use Encore's MCP server together with Cursor. In order to add the Encore MCP server to Cursor, the fastest way is via the button below (make sure to update `your-app-id` in the configuration to your actual Encore app ID). [![Add encore-mcp MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=encore-mcp&config=eyJjb21tYW5kIjoiZW5jb3JlIG1jcCBydW4gLS1hcHA9eW91ci1hcHAtaWQifQ%3D%3D) If you prefer to configure it manually, create the file `.cursor/mcp.json` with the following settings: ```json { "mcpServers": { "encore-mcp": { "command": "encore", "args": ["mcp", "run", "--app=your-app-id"] } } } ``` Learn more in [Cursor's MCP docs](https://docs.cursor.com/context/model-context-protocol) Now when using Cursor's Agent mode, you can ask it to do advanced actions, such as: "Add an endpoint that publishes to a pub/sub topic, call it and verify that the publish is in the traces" ## [Command Reference](https://encore.dev/docs/go/cli/mcp\#command-reference) #### [Start](https://encore.dev/docs/go/cli/mcp\#start) Starts an SSE-based MCP server and displays connection information. ```bash $ encore mcp start [--app=] ``` #### [Run](https://encore.dev/docs/go/cli/mcp\#run) Establishes an stdio-based MCP session. This command is typically used by MCP hosts to communicate with the server through standard input/output streams. ```bash $ encore mcp run [--app=] ``` ## [Exposed Tools](https://encore.dev/docs/go/cli/mcp\#exposed-tools) Encore's MCP server exposes the following tools that provide AI models with detailed context about your application. These tools enable LLMs to understand your application's structure, retrieve relevant information, and take actions within your system. #### [Database Tools](https://encore.dev/docs/go/cli/mcp\#database-tools) - **get\_databases**: Retrieve metadata about all SQL databases defined in the application, including their schema, tables, and relationships. - **query\_database**: Execute SQL queries against one or more databases in the application. #### [API Tools](https://encore.dev/docs/go/cli/mcp\#api-tools) - **call\_endpoint**: Make HTTP requests to any API endpoint in the application. - **get\_services**: Retrieve comprehensive information about all services and their endpoints in the application. - **get\_middleware**: Retrieve detailed information about all middleware components in the application. - **get\_auth\_handlers**: Retrieve information about all authentication handlers in the application. #### [Trace Tools](https://encore.dev/docs/go/cli/mcp\#trace-tools) - **get\_traces**: Retrieve a list of request traces from the application, including their timing, status, and associated metadata. - **get\_trace\_spans**: Retrieve detailed information about one or more traces, including all spans, timing information, and associated metadata. #### [Source Code Tools](https://encore.dev/docs/go/cli/mcp\#source-code-tools) - **get\_metadata**: Retrieve the complete application metadata, including service definitions, database schemas, API endpoints, and other infrastructure components. - **get\_src\_files**: Retrieve the contents of one or more source files from the application. #### [PubSub Tools](https://encore.dev/docs/go/cli/mcp\#pubsub-tools) - **get\_pubsub**: Retrieve detailed information about all PubSub topics and their subscriptions in the application. #### [Storage Tools](https://encore.dev/docs/go/cli/mcp\#storage-tools) - **get\_storage\_buckets**: Retrieve comprehensive information about all storage buckets in the application. - **get\_objects**: List and retrieve metadata about objects stored in one or more storage buckets. #### [Cache Tools](https://encore.dev/docs/go/cli/mcp\#cache-tools) - **get\_cache\_keyspaces**: Retrieve comprehensive information about all cache keyspaces in the application. #### [Metrics Tools](https://encore.dev/docs/go/cli/mcp\#metrics-tools) - **get\_metrics**: Retrieve comprehensive information about all metrics defined in the application. #### [Cron Tools](https://encore.dev/docs/go/cli/mcp\#cron-tools) - **get\_cronjobs**: Retrieve detailed information about all scheduled cron jobs in the application. #### [Secret Tools](https://encore.dev/docs/go/cli/mcp\#secret-tools) - **get\_secrets**: Retrieve metadata about all secrets used in the application. #### [Documentation Tools](https://encore.dev/docs/go/cli/mcp\#documentation-tools) - **search\_docs**: Search the Encore documentation using Algolia's search engine. - **get\_docs**: Retrieve the full content of specific documentation pages. [Telemetry](https://encore.dev/docs/go/cli/telemetry) [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/cli/mcp.md) ## Local Development Dashboard Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore provides an efficient local development workflow that automatically provisions [local infrastructure](https://encore.dev/docs/platform/infrastructure/infra#local-development) and supports automated testing with dedicated test infrastructure. The local environment also comes with a built-in Local Development Dashboard to simplify development and improve productivity. It has several features to help you design, develop, and debug your application: - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) with Automatic API Documentation - API Explorer to call your APIs - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) for simple and powerful debugging - [Encore Flow](https://encore.dev/docs/go/observability/encore-flow) for visualizing your microservices architecture All these features update in real-time as you make changes to your application. To access the dashboard, start your Encore application with `encore run` and it will open automatically. You can also follow the link in your terminal: ```bash $ encore run API Base URL: http://localhost:4000 Dev Dashboard URL: http://localhost:9400/hello-world-cgu2 ``` [MCP](https://encore.dev/docs/go/cli/mcp) [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/observability/dev-dash.md) ## Encore Tracing Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Distributed systems often have many moving parts, making it difficult to understand what your code is doing and finding the root-cause to bugs. That’s where Tracing comes in. If you haven’t seen it before, it may just about change your life. Tracing is a revolutionary way to gain insight into what your applications are doing. It works by capturing the series of events as they occur during the execution of your code (a β€œtrace”). This works by propagating a trace id between all individual systems, then correlating and joining the information together to present a unified picture of what happened end-to-end. As opposed to the labor intensive instrumentation you'd normally need to go through to use tracing, Encore automatically captures traces for your entire application – in all environments. Uniquely, this means you can use tracing even for local development to help debugging and speed up iterations. You view traces in the [Local Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) and, when using Encore Cloud, you can also see traces in the [Encore Cloud dashboard](https://app.encore.cloud/) for Production and other environments. ## [Encore's tracing is more comprehensive and more performant than all other tools](https://encore.dev/docs/go/observability/tracing\#encores-tracing-is-more-comprehensive-and-more-performant-than-all-other-tools) Unlike other tracing solutions, Encore understands what each trace event is and captures unique insights about each one. This means you get access to more information than ever before: - Stack traces - Structured logging - HTTP requests - Network connection information - API calls - Database queries - etc. ## [Redacting sensitive data](https://encore.dev/docs/go/observability/tracing\#redacting-sensitive-data) Encore's tracing automatically captures request and response payloads to simplify debugging. For cases where this is undesirable, such as for passwords or personally identifiable information (PII), Encore supports redacting fields marked as containing sensitive data. See the documentation on [API Schemas](https://encore.dev/docs/go/primitives/defining-apis#sensitive-data) for more information. [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/observability/tracing.md) ## Microservices Flow Visualization Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Flow is a visual tool that gives you an always up-to-date view of your entire system, helping you reason about your microservices architecture and identify which services depend on each other and how they work together. ## [Birds-eye view](https://encore.dev/docs/go/observability/encore-flow\#birds-eye-view) Having access to a zoomed out representation of your system can be invaluable in pretty much all parts of the development cycle. Flow helps you: - Track down bottlenecks before they grow into big problems. - Get new team members onboarded much faster. - Pinpoint hot paths in your system, services that might need extra attention. Services and PubSub topics are represented as boxes, arrows indicate a dependency. In the example below the `login` service has dependencies on the `user` and `authentication` services. Dashed arrows shows publications or subscriptions to a topic. Here, `payment` publishes to the `payment-made` topic and `email` subscribe to it: ![](https://encore.dev/assets/docs/flow-diagram.png) ## [Highlight dependencies](https://encore.dev/docs/go/observability/encore-flow\#highlight-dependencies) Hover over a service, or PubSub topic, to instantly reveal the nature and scale of its dependencies. Here the `login` service and its dependencies are highlighted. We can see that `login` makes queries to the database and requests to two of the endpoints from the `user` service as well as requests to one endpoint from the `authentication` service: ![](https://encore.dev/assets/docs/flow-highlight.png) ## [Real-time updates](https://encore.dev/docs/go/observability/encore-flow\#real-time-updates) Flow is accessible in the [Local Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) and, when using Encore Cloud, in the [Encore Cloud dashboard](https://app.encore.cloud/) for cloud environments. When developing locally, Flow will auto update in real-time to reflect your architecture as you make code changes. This helps you be mindful of important dependencies and makes it clear if you introduce new ones. For cloud environments, Flow auto-updates with each deploy. In the example below a new subscription on the topic `payment-made` is introduced and then removed in `user` service: [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/observability/encore-flow.md) ## Service Catalog Overview Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) All developers agree API documentation is great to have, but the effort of maintaining it inevitably leads to docs becoming stale and out of date. To solve this, Encore uses the [Encore Application Model](https://encore.dev/docs/go/concepts/application-model) to automatically generate a Service Catalog along with complete documentation for all APIs. This ensures docs are always up-to-date as your APIs evolve. The API docs are available both in your [Local Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) and for your whole team in the [Encore Cloud dashboard](https://app.encore.cloud/). [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) [Logging](https://encore.dev/docs/go/observability/logging) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/observability/service-catalog.md) ## Logging for Observability Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore offers built-in support for Structured Logging, which combines a free-form log message with structured and type-safe key-value pairs. This enables straightforward analysis of what your application is doing, in a way that is easy for a computer to parse, analyze, and index. This makes it simple to quickly filter and search through logs. Encore’s logging is integrated with the built-in [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) functionality, and all logs are automatically included in the active trace. This dramatically simplifies debugging of your application. ## [Usage](https://encore.dev/docs/go/observability/logging\#usage) First, import `encore.dev/rlog` in your package. Then simply call one of the package methods `Info`, `Error`, or `Debug`. For example: ```go rlog.Info("log message", "user_id", 12345, "is_subscriber", true) rlog.Error("something went terribly wrong!", "err", err) ``` The first parameter is the log message. After that follows zero or more key-value pairs for structured logging for context. If you’re logging many log messages with the same key-value pairs each time it can be a bit cumbersome. To help with that, use `rlog.With()` to group them into a context object, which then copies the key-value pairs into each log event: ```go ctx := rlog.With("is_subscriber", true) ctx.Info("user logged in", "login_method", "oauth") // includes is_subscriber=true ``` For more information, see the [API Documentation](https://pkg.go.dev/encore.dev/rlog). ## [Live-streaming logs](https://encore.dev/docs/go/observability/logging\#live-streaming-logs) Encore also makes it simple to live-stream logs directly to your terminal, from any environment, by running: ```ini $ encore logs --env=prod ``` [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) [Metrics](https://encore.dev/docs/go/observability/metrics) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/observability/logging.md) ## Custom Metrics in Encore Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore provides built-in support for defining custom metrics in your Go applications. Once defined, metrics are automatically collected and displayed in the Encore Cloud Dashboard, and can be exported to third-party observability services. See the [Platform metrics documentation](https://encore.dev/docs/platform/observability/metrics) for information about integrations with third-party services like Grafana Cloud and Datadog. ## [Defining custom metrics](https://encore.dev/docs/go/observability/metrics\#defining-custom-metrics) Define custom metrics by importing the [`encore.dev/metrics`](https://pkg.go.dev/encore.dev/metrics) package and creating a new metric using one of the `metrics.NewCounter` or `metrics.NewGauge` functions. For example, to count the number of orders processed: ```go import "encore.dev/metrics" var OrdersProcessed = metrics.NewCounter[uint64]("orders_processed", metrics.CounterConfig{}) func process(order *Order) { // ... OrdersProcessed.Increment() } ``` ## [Metric types](https://encore.dev/docs/go/observability/metrics\#metric-types) Encore currently supports two metric types: counters and gauges. **Counters** measure the count of something. A counter's value must always increase, never decrease. (Note that the value gets reset to 0 when the application restarts.) Typical use cases include counting the number of requests, the amount of data processed, and so on. **Gauges** measure the current value of something. Unlike counters, a gauge's value can fluctuate up and down. Typical use cases include measuring CPU usage, the number of active instances running of a process, and so on. For information about their respective APIs, see the API documentation for [Counter](https://pkg.go.dev/encore.dev/metrics#Counter) and [Gauge](https://pkg.go.dev/encore.dev/metrics#Gauge). ### [Counter example](https://encore.dev/docs/go/observability/metrics\#counter-example) ```go import "encore.dev/metrics" var RequestsReceived = metrics.NewCounter[uint64]("requests_received", metrics.CounterConfig{}) func handleRequest() { RequestsReceived.Increment() // ... handle request } ``` ### [Gauge example](https://encore.dev/docs/go/observability/metrics\#gauge-example) ```go import "encore.dev/metrics" var ActiveConnections = metrics.NewGauge[int64]("active_connections", metrics.GaugeConfig{}) func onConnect() { ActiveConnections.Add(1) } func onDisconnect() { ActiveConnections.Add(-1) } ``` ## [Defining labels](https://encore.dev/docs/go/observability/metrics\#defining-labels) Encore's metrics package provides a type-safe way of attaching labels to metrics. To define labels, create a struct type representing the labels and then use `metrics.NewCounterGroup` or `metrics.NewGaugeGroup`. The Labels type must be a named struct, where each field corresponds to a single label. Each field must be of type `string`, `int`, or `bool`. ### [Counter with labels](https://encore.dev/docs/go/observability/metrics\#counter-with-labels) ```go import "encore.dev/metrics" type Labels struct { Success bool } var OrdersProcessed = metrics.NewCounterGroup[Labels, uint64]("orders_processed", metrics.CounterConfig{}) func process(order *Order) { var success bool // ... populate success with true/false ... OrdersProcessed.With(Labels{Success: success}).Increment() } ``` ### [Gauge with labels](https://encore.dev/docs/go/observability/metrics\#gauge-with-labels) ```go import "encore.dev/metrics" type ConnectionLabels struct { Region string } var ActiveConnections = metrics.NewGaugeGroup[ConnectionLabels, int64]("active_connections", metrics.GaugeConfig{}) func onConnect(region string) { ActiveConnections.With(ConnectionLabels{Region: region}).Add(1) } ``` ##### Take care Each combination of label values creates a unique time series tracked in memory and stored by the monitoring system. Using numerous labels can lead to a combinatorial explosion, causing high cloud expenses and degraded performance. As a general rule, limit the unique time series to tens or hundreds at most, rather than thousands. [Logging](https://encore.dev/docs/go/observability/logging) [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/observability/metrics.md) ## Encore CI/CD Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore seamlessly integrates with any CI/CD pipeline through its CLI tools. You can automate Docker image creation using the `encore build` command as part of your deployment workflow. ## [Integrating with CI/CD Platforms](https://encore.dev/docs/go/self-host/ci-cd\#integrating-with-cicd-platforms) While every CI/CD pipeline is unique, integrating Encore follows a straightforward process. Here are the key steps: 1. Install the Encore CLI in your CI environment 2. Use `encore build docker` to create Docker images 3. Push the images to your container registry 4. Deploy to your infrastructure Refer to your CI/CD platform's documentation for more details on how to integrate CLI tools like `encore build`. ### [GitHub actions example](https://encore.dev/docs/go/self-host/ci-cd\#github-actions-example) This example shows how to build, push, and deploy an Encore Docker image to DigitalOcean using GitHub Actions. The DigitalOcean application is set up re-deploy the application every time an image with the tag `latest` is uploaded. ```yaml name: Build, Push and Deploy a Encore Docker Image to DigitalOcean on: push: branches: [ main ] permissions: contents: read packages: write jobs: build-push-deploy-image: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Download Encore CLI script uses: sozo-design/curl@v1.0.2 with: args: --output install.sh -L https://encore.dev/install.sh - name: Install Encore CLI run: bash install.sh - name: Log in to DigitalOcean container registry run: docker login registry.digitalocean.com -u my-email@gmail.com -p ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - name: Build Docker image run: /home/runner/.encore/bin/encore build docker myapp - name: Tag Docker image run: docker tag myapp registry.digitalocean.com//:latest - name: Push Docker image run: docker push registry.digitalocean.com//:latest ``` ## [Building Docker Images](https://encore.dev/docs/go/self-host/ci-cd\#building-docker-images) The `encore build docker` command provides several options to customize your builds: ```bash # Build specific services and gateways encore build docker --services=service1,service2 --gateways=api-gateway MY-IMAGE:TAG # Customize the base image encore build docker --base=node:18-alpine MY-IMAGE:TAG ``` The image will default to run on port 8080, but you can customize it by setting the `PORT` environment variable when starting your image. ```bash docker run -e PORT=8081 -p 8081:8081 MY-IMAGE:TAG ``` Learn more about the `encore build docker` command in the [build Docker images](https://encore.dev/docs/go/self-host/docker-build) guide. Continue to learn how to [configure infrastructure](https://encore.dev/docs/go/self-host/configure-infra). [Metrics](https://encore.dev/docs/go/observability/metrics) [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/self-host/ci-cd.md) ## Docker Image Building Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore supports building Docker images directly from the CLI, which can then be self-hosted on your own infrastructure of choice. This can be a good choice if Encore Cloud isn't a good fit for your use case, or if you want to [migrate away](https://encore.dev/docs/go/migration/migrate-away). ## [Building your own Docker image](https://encore.dev/docs/go/self-host/docker-build\#building-your-own-docker-image) To build your own Docker image, use `encore build docker MY-IMAGE:TAG` from the CLI. This will compile your application using the host machine and then produce a Docker image containing the compiled application. The base image defaults to `scratch` for GO apps and `node:slim` for TS, but can be customized with `--base`. This is exactly the same code path that Encore's CI system uses to build Docker images, ensuring compatibility. By default, all your services are included and started by the Docker image. If you want to specify specific services and gateways to include, you can use the `--services` and `--gateways` flags. ```bash encore build docker --services=service1,service2 --gateways=api-gateway MY-IMAGE:TAG ``` The image will default to run on port 8080, but you can customize it by setting the `PORT` environment variable when starting your image. ```bash docker run -e PORT=8081 -p 8081:8081 MY-IMAGE:TAG ``` Congratulations, you've built your own Docker image! πŸŽ‰ Continue to learn how to [configure infrastructure](https://encore.dev/docs/go/self-host/configure-infra). [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/self-host/self-host.md) ## Configure Infrastructure Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) If you are using infrastructure resources, such as SQL databases, Pub/Sub, or metrics, you will need to configure your Docker image with the necessary configuration. The `build` command lets you provide this by specifying a path to a config file using the `--config` flag. ```bash encore build docker --config path/to/infra-config.json MY-IMAGE:TAG ``` The configuration file should be a JSON file using the [Encore Infra Config](https://encore.dev/schemas/infra.schema.json) schema. This supports configuring things like: - How to access infrastructure resources (what provider to use, what credentials to use, etc.) - How to call other services over the network ("service discovery"), most notably their base URLs. - Observability configuration (where to export metrics, etc.) - Metadata about the environment the application is running in, to power Encore's metadata APIs - The values for any application-defined secrets. This configuration is necessary for the application to behave correctly. ## [Example](https://encore.dev/docs/go/self-host/configure-infra\#example) Here's an example configuration file you can use. ```json { "$schema": "https://encore.dev/schemas/infra.schema.json", "metadata": { "app_id": "my-app", "env_name": "my-env", "env_type": "production", "cloud": "gcp", "base_url": "https://my-app.com" }, "sql_servers": [\ {\ "host": "my-db-host:5432",\ "databases": {\ "my-db": {\ "username": "my-db-owner",\ "password": {"$env": "DB_PASSWORD"}\ }\ }\ }\ ], "service_discovery": { "myservice": { "base_url": "https://myservice:8044" } }, "redis": { "my-redis": { "database_index": 0, "auth": { "type": "acl", "username": "encoreredis", "password": {"$env": "REDIS_PASSWORD"} }, "host": "my-redis-host", } }, "metrics": { "type": "prometheus", "remote_write_url": "https://my-remote-write-url" }, "graceful_shutdown": { "total": 30 }, "auth": [\ {\ "type": "key",\ "id": 1,\ "key": {"$env": "SVC_TO_SVC_KEY"}\ }\ ], "secrets": { "AppSecret": {"$env": "APP_SECRET"} }, "pubsub": [\ {\ "type": "gcp_pubsub",\ "project_id": "my-project",\ "topics": {\ "my-topic": {\ "name": "gcp-topic-name",\ "subscriptions": {\ "encore-subscription": {\ "name": "gcp-subscription-name"\ }\ }\ }\ }\ }\ ], "object_storage": [\ {\ "type": "gcs",\ "buckets": {\ "my-gcs-bucket": {\ "name": "my-gcs-bucket",\ }\ }\ }\ ] } ``` ## [Configuring Infrastructure](https://encore.dev/docs/go/self-host/configure-infra\#configuring-infrastructure) To use infrastructure resources, additional configuration must be added so that Encore is aware of how to access each infrastructure resource. See below for examples of each type of infrastructure resource. ### [1\. Basic Environment Metadata Configuration](https://encore.dev/docs/go/self-host/configure-infra\#1-basic-environment-metadata-configuration) ```json { "metadata": { "app_id": "my-encore-app", "env_name": "production", "env_type": "production", "cloud": "aws", "base_url": "https://api.myencoreapp.com" } } ``` - `app_id`: The ID of your Encore application. - `env_name`: The environment name, such as `production`, `staging`, or `development`. - `env_type`: Specifies the type of environment (`production`, `test`, `development`, or `ephemeral`). - `cloud`: The cloud provider hosting the infrastructure (e.g., `aws`, `gcp`, or `azure`). - `base_url`: The base URL for services in the environment. ### [2\. Graceful Shutdown Configuration](https://encore.dev/docs/go/self-host/configure-infra\#2-graceful-shutdown-configuration) ```json { "graceful_shutdown": { "total": 30, "shutdown_hooks": 10, "handlers": 20 } } ``` - `total`: The total time allowed for the shutdown process in seconds. - `shutdown_hooks`: The time allowed for executing shutdown hooks. - `handlers`: The time allocated for processing request handlers during the shutdown. ### [3\. Authentication Methods Configuration](https://encore.dev/docs/go/self-host/configure-infra\#3-authentication-methods-configuration) Private endpoints will not require authentication if no authentication methods are specified. This is typically fine when services are deployed on a private network such as a VPC. But sometimes you might need to connect to other services over the public internet, in which case you'll want to ensure private endpoints are only accessible to other backend services. To do that you can configure authentication methods. Encore currently supports authentication through a shared key, which you can specify in your infrastructure configuration file. ```json { "auth": [\ {\ "type": "key",\ "id": 1,\ "key": {\ "$env": "SERVICE_API_KEY"\ }\ }\ ] } ``` - `type`: The authentication method type (e.g., `key`). - `id`: The ID associated with the authentication method. - `key`: The authentication key, which can be set using an environment variable reference. ### [4\. Service Discovery Configuration](https://encore.dev/docs/go/self-host/configure-infra\#4-service-discovery-configuration) Service discovery is used to access other services over the network. You can configure service discovery in the infrastructure configuration file. If you export all services into the same docker image, you don't need to configure service discovery as it will be automatically configured when the services are started. ```json { "service_discovery": { "myservice": { "base_url": "https://myservice.myencoreapp.com", "auth": [\ {\ "type": "key",\ "id": 1,\ "key": {\ "$env": "MY_SERVICE_API_KEY"\ }\ }\ ] } } } ``` - `myservice`: This is the name of the service as it is declared in your Encore app. - `base_url`: The base URL for the service. - `auth`: Authentication methods used for accessing the service. If no authentication methods are specified, the service will use the auth methods defined in the `auth` section. ### [5\. Metrics Configuration](https://encore.dev/docs/go/self-host/configure-infra\#5-metrics-configuration) Similarly to cloud infrastructure resources, Encore supports configurable metrics exports: - Prometheus - DataDog - GCP Cloud Monitoring - AWS CloudWatch This is configured by setting the metrics field. Below are examples for each of the supported metrics providers: #### [5.1. Prometheus Configuration](https://encore.dev/docs/go/self-host/configure-infra\#51-prometheus-configuration) ```json { "metrics": { "type": "prometheus", "collection_interval": 15, "remote_write_url": { "$env": "PROMETHEUS_REMOTE_WRITE_URL" } } } ``` #### [5.2. Datadog Configuration](https://encore.dev/docs/go/self-host/configure-infra\#52-datadog-configuration) ```json { "metrics": { "type": "datadog", "collection_interval": 30, "site": "datadoghq.com", "api_key": { "$env": "DATADOG_API_KEY" } } } ``` #### [5.3. GCP Cloud Monitoring Configuration](https://encore.dev/docs/go/self-host/configure-infra\#53-gcp-cloud-monitoring-configuration) ```json { "metrics": { "type": "gcp_cloud_monitoring", "collection_interval": 60, "project_id": "my-gcp-project", "monitored_resource_type": "gce_instance", "monitored_resource_labels": { "instance_id": "1234567890", "zone": "us-central1-a" }, "metric_names": { "cpu_usage": "compute.googleapis.com/instance/cpu/usage_time" } } } ``` #### [5.4. AWS CloudWatch Configuration](https://encore.dev/docs/go/self-host/configure-infra\#54-aws-cloudwatch-configuration) ```json { "metrics": { "type": "aws_cloudwatch", "collection_interval": 60, "namespace": "MyAppMetrics" } } ``` ### [6\. SQL Database Configuration](https://encore.dev/docs/go/self-host/configure-infra\#6-sql-database-configuration) The SQL databases you've declared in your Encore app must be configured in the infrastructure configuration file. There must be exactly one database configuration for each declared database. You can configure multiple SQL servers if needed. ```json { "sql_servers": [\ {\ "host": "db.myencoreapp.com:5432",\ "tls_config": {\ "disabled": false,\ "ca": "---BEGIN CERTIFICATE---\n...",\ "disable_tls_hostname_verification": false,\ "disable_ca_verification": false\ },\ "databases": {\ "my-database": {\ "name": "my-postgres-db-name",\ "max_connections": 100,\ "min_connections": 10,\ "username": "db_user",\ "password": {\ "$env": "DB_PASSWORD"\ }\ }\ }\ }\ ] } ``` - `my-database`: This is the name of the database as it is declared in your Encore app. - `name`: The name of the database on the database server. Defaults to the declared Encore name. - `host`: SQL server host, optionally including the port. - `tls_config`: TLS configuration for secure connections. If the server uses TLS with a non-system CA root, or requires a client certificate, specify the appropriate fields as PEM-encoded strings. Otherwise, they can be left empty. - `databases`: List of databases, each with connection settings. ### [7\. Secrets Configuration](https://encore.dev/docs/go/self-host/configure-infra\#7-secrets-configuration) #### [7.1. Using Direct Secrets](https://encore.dev/docs/go/self-host/configure-infra\#71-using-direct-secrets) You can set the secret value directly in the configuration file, or use an environment variable reference to set the secret value. ```json { "secrets": { "API_TOKEN": "embedded-secret-value", "DB_PASSWORD": { "$env": "DB_PASSWORD" } } } ``` - `API_TOKEN`: This is the name of a secret as it is declared in your Encore app. #### [7.2. Using Environment Reference](https://encore.dev/docs/go/self-host/configure-infra\#72-using-environment-reference) As an alternative, you can use an environment variable reference to set the secret value. The env variable should be set in the environment where the application is running. The content of the environment variable should be a JSON string where each key is the secret name and the value is the secret value. ```json { "secrets": { "$env": "SECRET_JSON" } } ``` ### [8\. Redis Configuration](https://encore.dev/docs/go/self-host/configure-infra\#8-redis-configuration) ```json { "redis": { "my-redis": { "host": "redis.myencoreapp.com:6379", "database_index": 0, "auth": { "type": "auth", "auth_string": { "$env": "REDIS_AUTH_STRING" } }, "max_connections": 50, "min_connections": 5 } } } ``` - `my-redis`: This is the name of the redis resource as it is declared in your Encore app. - `host`: Redis server host, optionally including the port. - `auth`: Authentication configuration for the Redis server. - `key_prefix`: Prefix applied to all keys. ### [9\. Pub/Sub Configuration](https://encore.dev/docs/go/self-host/configure-infra\#9-pubsub-configuration) Encore currently supports the following Pub/Sub providers: - `nsq` for [NSQ](https://nsq.io/) - `gcp` for [Google Cloud Pub/Sub](https://cloud.google.com/pubsub) - `aws` for AWS [SNS](https://aws.amazon.com/sns/) \+ [SQS](https://aws.amazon.com/sqs/) - `azure` for [Azure Service Bus](https://azure.microsoft.com/en-us/products/service-bus) The configuration for each provider is different. Below are examples for each provider. #### [9.1. GCP Pub/Sub](https://encore.dev/docs/go/self-host/configure-infra\#91-gcp-pubsub) ```json { "pubsub": [\ {\ "type": "gcp_pubsub",\ "project_id": "my-gcp-project",\ "topics": {\ "my-topic": {\ "name": "my-topic",\ "project_id": "my-gcp-project",\ "subscriptions": {\ "my-subscription": {\ "name": "my-subscription",\ "push_config": {\ "id": "my-push",\ "service_account": "service-account@my-gcp-project.iam.gserviceaccount.com"\ }\ }\ }\ }\ }\ }\ ] } ``` - `my-topic`: This is the name of the topic as it is declared in your Encore app. - `my-subscription`: This is the name of the subscription as it is declared in your Encore app. - `project_id`: The default GCP project ID. This can be overridden by setting the `project_id` field in the topic or subscription. - `name`: The name of the topic or subscription. - `push_config/id`: The id will be appended to `/__encore/pubsub/push/` to form the full push path of your service, e.g. `/__encore/pubsub/push/`. This is the path your service expects to receive push messages on. - `push_config/service_account`: The service account configured for the push subscription. #### [9.2. AWS SNS/SQS](https://encore.dev/docs/go/self-host/configure-infra\#92-aws-snssqs) ```json { "pubsub": [\ {\ "type": "aws_sns_sqs",\ "topics": {\ "my-topic": {\ "arn": "arn:aws:sns:us-east-1:123456789012:my-topic",\ "subscriptions": {\ "my-queue": {\ "url": "https://sqs.eu-east-1.amazonaws.com/123456789012/my-queue"\ }\ }\ }\ }\ }\ ] } ``` - `my-topic`: This is the name of the topic as it is declared in your Encore app. - `my-queue`: This is the name of the queue as it is declared in your Encore app. - `arn`: The ARN of the SNS topic. - `url`: The URL of the SQS queue. #### [9.3. NSQ Configuration](https://encore.dev/docs/go/self-host/configure-infra\#93-nsq-configuration) ```json { "pubsub": [\ {\ "type": "nsq",\ "hosts": "nsq.myencoreapp.com:4150",\ "topics": {\ "my-topic": {\ "name": "my-topic",\ "subscriptions": {\ "my-subscription": {\ "name": "my-subscription"\ }\ }\ }\ }\ }\ ] } ``` - `my-topic`: This is the name of the topic as it is declared in your Encore app. - `my-subscription`: This is the name of the subscription as it is declared in your Encore app. ### [10\. Object Storage Configuration](https://encore.dev/docs/go/self-host/configure-infra\#10-object-storage-configuration) Encore currently supports the following object storage providers: - `gcs` for [Google Cloud Storage](https://cloud.google.com/storage) - `s3` for [AWS S3](https://aws.amazon.com/s3/) or a custom S3-compatible provider #### [10.1. GCS Configuration](https://encore.dev/docs/go/self-host/configure-infra\#101-gcs-configuration) ```json { "object_storage": [\ {\ "type": "gcs",\ "buckets": {\ "my-gcs-bucket": {\ "name": "my-gcs-bucket",\ "key_prefix": "my-optional-prefix/",\ "public_base_url": "https://my-gcs-bucket-cdn.example.com/my-optional-prefix"\ }\ }\ }\ ] } ``` - `my-gcs-bucket`: This is the name of the bucket as it is declared in your Encore app. - `name`: The full name of the GCS bucket. - `key_prefix`: An optional prefix to apply to all keys in the bucket. - `public_base_url`: A URL to use for public access to the bucket. This field is required if you configure your bucket to be public. Encore will append the object key to this URL when generating public URLs. The optional prefix will not be appended. #### [10.2. S3 Configuration](https://encore.dev/docs/go/self-host/configure-infra\#102-s3-configuration) ```json { "object_storage": [\ {\ "type": "s3",\ "region": "us-east-1",\ "buckets": {\ "my-s3-bucket": {\ "name": "my-s3-bucket",\ "key_prefix": "my-optional-prefix/",\ "public_base_url": "https://my-gcs-bucket-cdn.example.com/my-optional-prefix"\ }\ }\ }\ ] } ``` - `my-s3-bucket`: This is the name of the bucket as it is declared in your Encore app. - `region`: The AWS region where the bucket is located. - `name`: The full name of the S3 bucket. - `key_prefix`: An optional prefix to apply to all keys in the bucket. - `public_base_url`: A URL to use for public access to the bucket. This field is required if you configure your bucket to be public. Encore will append the object key to this URL when generating public URLs. The optional prefix will not be appended. #### [10.3. Custom S3 Provider Configuration](https://encore.dev/docs/go/self-host/configure-infra\#103-custom-s3-provider-configuration) You can also configure a custom S3 provider by specifying the endpoint, access key id, and secret access key. Custom S3 providers are useful if you are using a S3-compatible storage provider such as [Cloudflare R2](https://developers.cloudflare.com/r2/). ```json { "object_storage": [\ {\ "type": "s3",\ "region": "auto",\ "endpoint": "https://...",\ "access_key_id": "...",\ "secret_access_key": {\ "$env": "BUCKET_SECRET_ACCESS_KEY"\ },\ "buckets": {\ "my-custom-bucket": {\ "name": "my-custom-bucket",\ "key_prefix": "my-optional-prefix/",\ "public_base_url": "https://my-gcs-bucket-cdn.example.com/my-optional-prefix"\ }\ }\ }\ ] } ``` - `my-custom-bucket`: This is the name of the bucket as it is declared in your Encore app. - `region`: The region where the bucket is located. - `name`: The full name of the bucket - `key_prefix`: An optional prefix to apply to all keys in the bucket. - `public_base_url`: A URL to use for public access to the bucket. This field is required if you configure your bucket to be public. Encore will append the object key to this URL when generating public URLs. The optional prefix will not be appended. This guide covers typical infrastructure configurations. Adjust according to your specific requirements to optimize your Encore app's infrastructure setup. [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/self-host/configure-infra.md) ## Break Up Monolith Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) It's common to want to break out specific functionality into separate services. Perhaps you want to independently scale a specific service, or simply want to structure your codebase in smaller pieces. Encore makes it simple to evolve your system architecture over time, and enables you to deploy your application in multiple different ways without making code changes. ## [How to break out a service from a monolith](https://encore.dev/docs/go/how-to/break-up-monolith\#how-to-break-out-a-service-from-a-monolith) As a (slightly silly) example, let's imagine we have a monolith `hello` with two API endpoints `H1` and `H2`. It looks like this: ```go package hello import ( "context" ) //encore:api public path=/hello/:name func H1(ctx context.Context, name string) (*Response, error) { msg := "Hello, " + name + "!" return &Response{Message: msg}, nil } //encore:api public path=/yo/:name func H2(ctx context.Context, name string) (*Response, error) { msg := "Yo, " + name + "!" return &Response{Message: msg}, nil } type Response struct { Message string } ``` Now we're going to break out `H2` into its own separate service. Happily, all we need to do is create a new package, let's call it `yo`, and move the `H2` endpoint into it. Like so: ```go package yo import ( "context" ) //encore:api public path=/yo/:name func H2(ctx context.Context, name string) (*Response, error) { msg := "Yo, " + name + "!" return &Response{Message: msg}, nil } type Response struct { Message string } ``` On disk we now have: ```go /my-app β”œβ”€β”€ encore.app // ... and other top-level project files β”‚ β”œβ”€β”€ hello // hello service (a Go package) β”‚Β Β  └── hello.go // hello service code β”‚ └── yo // yo service (a Go package) └── yo.go // yo service code ``` Encore now understands these are separate services, and when you run your app you'll see that the [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) has been automatically updated accordingly. ![](https://encore.dev/assets/docs/microservices-service-catalog.png) As well as the [Flow architecture diagram](https://encore.dev/docs/go/observability/encore-flow). ![](https://encore.dev/assets/docs/microservices-flow.png) ## [Sharing databases between services (or not)](https://encore.dev/docs/go/how-to/break-up-monolith\#sharing-databases-between-services-or-not) Deciding whether to share a database between multiple services depends on your specific situation. Encore supports both options. Learn more in the [database documentation](https://encore.dev/docs/go/primitives/share-db-between-services). [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/break-up-monolith.md) ## Frontend Integration Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore is not opinionated about where you host your frontend, pick the platform that suits your situation best. If your frontend and backend use different domains, often the case when using PR preview environments for your frontend, you may need to [configure CORS](https://encore.dev/docs/go/how-to/integrate-frontend#handling-cors). Take a look at our [React starter template](https://encore.dev/templates/react) for an example of deploying a frontend to [Vercel](https://vercel.com/) or the [Meeting Notes tutorial](https://encore.dev/docs/go/tutorials/meeting-notes) deployed to [GitHub Pages](https://pages.github.com/). ## [Generating a request client](https://encore.dev/docs/go/how-to/integrate-frontend\#generating-a-request-client) Encore is able to generate frontend request clients (TypeScript or JavaScript). This lets you to keep the request/response types in sync without manual work and assists you in calling the APIs. Generate a client by running: ```bash $ encore gen client --output=./src/client.ts --env= ``` Adding this as a script to your `package.json` is often a good idea to be able to run it whenever a change is made to your Encore API: ```json { ... "scripts": { ... "generate-client:staging": "encore gen client --output=./src/client.ts --env=staging", "generate-client:local": "encore gen client --output=./src/client.ts --env=local" } } ``` After that you are ready to use the request client in your code. Here is an example from the [Meeting Notes tutorial](https://encore.dev/docs/tutorials/meeting-notes) for calling the `GetNote` endpoint on the `note` service in order to retrieve a specific meeting note (which has the properties `id`, `cover_url` & `text`): ```ts import Client, { Environment, Local } from "src/client.ts"; // Making request to locally running backend... const client = new Client(Local); // or to a specific deployed environment const client = new Client(Environment("staging")); // Calling APIs as typesafe functions 🌟 const response = await client.note.GetNote("note-uuid"); console.log(response.id); console.log(response.cover_url); console.log(response.text); ``` See more in the [client generation docs](https://encore.dev/docs/develop/client-generation). ### [Asynchronous state management](https://encore.dev/docs/go/how-to/integrate-frontend\#asynchronous-state-management) When building something a bit more complex, you will likely need to deal with caching, refetching, and data going stale. [TanStack Query](https://tanstack.com/query/latest) is a popular library that was built to solve exactly these problems and works well with the Encore request client. Here is a simple example of using an Encore request client together with TanStack Query: ```ts import { useQuery, useMutation, useQueryClient, QueryClient, QueryClientProvider, } from '@tanstack/react-query' import Client, { todo } from '../encore-client' // Create a Encore client const encoreClient = new Client(window.location.origin); // Create a react-query client const queryClient = new QueryClient() function App() { return ( // Provide the client to your App ) } function Todos() { // Access the client const queryClient = useQueryClient() // Queries const query = useQuery({ queryKey: ['todos'], queryFn: () => encoreClient.todo.List() }) // Mutations const mutation = useMutation({ mutationFn: (params: todo.AddParams) => encoreClient.todo.Add(params), onSuccess: () => { // Invalidate and refetch queryClient.invalidateQueries({ queryKey: ['todos'] }) }, }) return (
    {query.data?.map((todo) => (
  • {todo.title}
  • ))}
) } render(, document.getElementById('root')) ``` This example assumes that we have a `todo` service with a `List` and `Add` endpoint. When adding the new todo, TanStack Query will automatically invalidate the `todos` query and refetch it. For a real-world example, take a look at the [Uptime Monitoring](https://github.com/encoredev/examples/tree/main/uptime) app which also makes use of TanStack Query's `refetchInterval` option for polling the backend. ### [Testing](https://encore.dev/docs/go/how-to/integrate-frontend\#testing) When unit testing a component that interacts with your Encore API you can mock methods on the request client to return a value suitable for the test. This makes your test URL agnostic because you are not intercepting specific requests on the fetch layer. You also get type errors in your tests if the request client gets updated. Here is an example from the [Uptime Monitoring Starter](https://github.com/encoredev/examples/tree/main/uptime) where we are mocking a GET request method and spying on a POST request method: ```ts import { render, waitForElementToBeRemoved } from "@testing-library/react"; import App from "./App"; import { site } from "./client"; import { userEvent } from "@testing-library/user-event"; describe("App", () => { beforeEach(() => { // Return mocked data from the List (GET) endpoint jest .spyOn(site.ServiceClient.prototype, "List") .mockReturnValue(Promise.resolve({ sites: [{\ id: 1,\ url: "test.dev"\ }] })); // Spy on the Add (POST) endpoint jest.spyOn(site.ServiceClient.prototype, "Add"); }); it("render sites", async () => { render(); await waitForElementToBeRemoved(() => screen.queryByText("Loading...")); // Verify that the List endpoint has been called expect(site.ServiceClient.prototype.List).toBeCalledTimes(1); // Verify that the sites are rendered with our mocked data screen.getAllByText("test.dev"); }); it("add site", async () => { render(); await waitForElementToBeRemoved(() => screen.queryByText("Loading...")); // Interact with the page and add 'another.com' await userEvent.click(screen.getByText("Add website")); await userEvent.type( screen.getByPlaceholderText("google.com"), "another.com", ); await userEvent.click(screen.getByText("Save")); // Verify that the Add endpoint has been called with the correct parameters expect(site.ServiceClient.prototype.Add).toHaveBeenCalledWith({ url: "another.com", }); }); }) ``` ##### Please note In the example above we need to mock the `List` method on `site.ServiceClient.prototype` because the request client has not yet been initialized when we're creating the mock. If you have access to the instance of the request client in your test (which could be the case if you are passing the client around in your components) you can instead do `jest.spyOn(client.site, "List")` and `expect(client.site.List).toHaveBeenCalled()` which would give you the same result. More examples of tests can be found in the [Uptime Monitoring Starter repo](https://github.com/encoredev/examples/tree/main/uptime). ## [Monorepo or Multi repo](https://encore.dev/docs/go/how-to/integrate-frontend\#monorepo-or-multi-repo) Encore is not opinionated about where your frontend lives, pick the approach that fits your application best. If you use a monorepo then it is often a good idea to place your backend and frontend in separate folders. There are two approaches to moving your Encore backend to a subfolder: 1. Place your microservices together with the `encore.app` file in a subfolder. When moving `encore.app` to a subfolder you will need to configure the "Root Directory" in app settings in the [Encore Cloud dashboard](https://app.encore.cloud/). 2. Place your microservices in a subfolder and keep the `encore.app` in the repo root directory. No configuration change is needed, but you will need to update the import paths if your services are calling each other. ## [REST vs. GraphQL](https://encore.dev/docs/go/how-to/integrate-frontend\#rest-vs-graphql) Encore allows for building backends using both REST and GraphQL, you should pick the approach that suits your use case best. Take a look at the [GraphQL tutorial](https://encore.dev/docs/go/tutorials/graphql) for an example of building a GraphQL backend with Encore. ## [Hosting a frontend on Encore for development](https://encore.dev/docs/go/how-to/integrate-frontend\#hosting-a-frontend-on-encore-for-development) Encore is primarily designed for backend development and does not (at the moment) support building or testing frontends in the deploy pipeline. For production use, we recommend that you deploy your frontend using Vercel, Netlify, or a similar service. For development purposes, you can create a `raw` endpoint that serves static frontend assets. It would look something like the example below (taken from the [Uptime Monitoring tutorial](https://encore.dev/docs/go/tutorials/uptime)), but keep in mind that you need to have the compiled frontend assets under version control (`dist` folder in the example below). ```go package frontend import ( "embed" "io/fs" "net/http" ) var ( //go:embed dist dist embed.FS assets, _ = fs.Sub(dist, "dist") handler = http.StripPrefix("/frontend/", http.FileServer(http.FS(assets))) ) //encore:api public raw path=/frontend/*path func Serve(w http.ResponseWriter, req *http.Request) { handler.ServeHTTP(w, req) } ``` ## [Handling CORS](https://encore.dev/docs/go/how-to/integrate-frontend\#handling-cors) If you are running into CORS issues when calling your Encore API from your frontend you may need to specify which origins are allowed to access your API (via browsers). Do this by specifying the `global_cors` key in the `encore.app` file, which has the following structure: ```json global_cors: { // allow_origins_without_credentials specifies the allowed origins for requests // that don't include credentials. If nil it defaults to allowing all domains // (equivalent to ["*"]). "allow_origins_without_credentials": [\ ""\ ], // allow_origins_with_credentials specifies the allowed origins for requests // that include credentials. If a request is made from an Origin in this list // Encore responds with Access-Control-Allow-Origin: . // // The URLs in this list may include wildcards (e.g. "https://*.example.com" // or "https://*-myapp.example.com"). "allow_origins_with_credentials": [\ ""\ ] } ``` See more in the [CORS docs](https://encore.dev/docs/go/develop/cors). [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/integrate-frontend.mdx) ## Integrate Temporal Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) [Temporal](https://temporal.io/) is a workflow orchestration system for building highly reliable systems. Encore works great with Temporal, and this guide shows you how to integrate Temporal into your Encore application. ## [Set up Temporal clusters](https://encore.dev/docs/go/how-to/temporal\#set-up-temporal-clusters) You'll need at least two Temporal clusters: one for local development and one for cloud environments. We recommend using [Temporalite](https://github.com/temporalio/temporalite) for local development, and [Temporal Cloud](https://temporal.io/cloud) for cloud environments. ## [Set up Temporal Workflow](https://encore.dev/docs/go/how-to/temporal\#set-up-temporal-workflow) Next it's time to create a Temporal Workflow. We'll base this on the Temporal [Hello World](https://learn.temporal.io/getting_started/go/hello_world_in_go/) example. Create a new Encore service named `greeting`: ```go greeting/greeting.go package greeting import ( "context" "fmt" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" "encore.dev" ) // Use an environment-specific task queue so we can use the same // Temporal Cluster for all cloud environments. var ( envName = encore.Meta().Environment.Name greetingTaskQueue = envName + "-greeting" ) //encore:service type Service struct { client client.Client worker worker.Worker } func initService() (*Service, error) { c, err := client.Dial(client.Options{}) if err != nil { return nil, fmt.Errorf("create temporal client: %v", err) } w := worker.New(c, greetingTaskQueue, worker.Options{}) err = w.Start() if err != nil { c.Close() return nil, fmt.Errorf("start temporal worker: %v", err) } return &Service{client: c, worker: w}, nil } func (s *Service) Shutdown(force context.Context) { s.client.Close() s.worker.Stop() } ``` Next it's time to define some workflows. These need to be in the same service, so add a new `workflow` package inside the `greeting` service, containing a workflow and activity definition in separate files: ```go greeting/workflow/workflow.go greeting/workflow/activity.go package workflow import ( "time" "go.temporal.io/sdk/workflow" ) func Greeting(ctx workflow.Context, name string) (string, error) { options := workflow.ActivityOptions{ StartToCloseTimeout: time.Second * 5, } ctx = workflow.WithActivityOptions(ctx, options) var result string err := workflow.ExecuteActivity(ctx, ComposeGreeting, name).Get(ctx, &result) return result, err } ``` Then, go back to the `greeting` service and register the workflow and activity: ```go greeting/greeting.go // Import the package at the top: import "encore.app/greeting/workflow" // Add these lines to `initService`, below the call to `worker.New`: w.RegisterWorkflow(workflow.Greeting) w.RegisterActivity(workflow.ComposeGreeting) ``` Now let's create an Encore API that triggers this workflow. Add a new file `greeting/greet.go`: ```go greeting/greet.go package greeting import ( "context" "encore.app/greeting/workflow" "encore.dev/rlog" "go.temporal.io/sdk/client" ) type GreetResponse struct { Greeting string } //encore:api public path=/greet/:name func (s *Service) Greet(ctx context.Context, name string) (*GreetResponse, error) { options := client.StartWorkflowOptions{ ID: "greeting-workflow", TaskQueue: greetingTaskQueue, } we, err := s.client.ExecuteWorkflow(ctx, options, workflow.Greeting, name) if err != nil { return nil, err } rlog.Info("started workflow", "id", we.GetID(), "run_id", we.GetRunID()) // Get the results var greeting string err = we.Get(ctx, &greeting) if err != nil { return nil, err } return &GreetResponse{Greeting: greeting}, nil } ``` ## [Run it locally](https://encore.dev/docs/go/how-to/temporal\#run-it-locally) Now we're ready to test it out. Start up `temporalite` and your Encore application (in separate terminals): ```bash $ temporalite start --namespace default $ encore run ``` Now try calling it, either from the [Local Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) or using cURL: ```bash $ curl 'http://localhost:4000/greeting/Temporal' {"Greeting": "Hello Temporal!"} ``` If you see this, it works! ## [Run in the cloud](https://encore.dev/docs/go/how-to/temporal\#run-in-the-cloud) To run it in the cloud, you will need to use Temporal Cloud or your own, self-hosted Temporal cluster. The easiest way to automatically pick up the correct cluster address is to use Encore's [config functionality](https://encore.dev/docs/go/develop/config). Add two new files: ```go greeting/config.go greeting/config.cue package greeting import "encore.dev/config" type Config struct { TemporalServer string } var cfg = config.Load[*Config]() ``` Finally go back to `greeting/greeting.go` and update the `client.Dial` call to look like: ```go greeting/greeting.go client.Dial(client.Options{HostPort: cfg.TemporalServer}) ``` With that, Encore will automatically connect to the correct Temporal cluster, using a local cluster for local development and your cloud-hosted cluster for everything else. [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) [Build with cgo](https://encore.dev/docs/go/how-to/cgo) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/temporal.md) ## Enable Cgo for Encore Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Cgo is a feature of the Go compiler that enables Go programs to interface with libraries written in other languages using C bindings. By default, for improved portability Encore builds applications with cgo support disabled. To enable cgo for your application, add `"build": {"cgo_enabled": true}` to your `encore.app` file. For example: ```json encore.app { "id": "my-app-id", "build": { "cgo_enabled": true } } ``` With this setting Encore's build system will compile the application using an Ubuntu builder image with gcc pre-installed. ## [Static linking](https://encore.dev/docs/go/how-to/cgo\#static-linking) To keep the resulting Docker images as minimal as possible, Encore compiles applications with static linking. This happens even with cgo enabled. As a result the cgo libraries you use must support static linking. In some cases, you may need to add additional linker flags to properly work with static linking of cgo libraries. See the [official cgo docs](https://pkg.go.dev/cmd/cgo) for more information on how to do this. [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) [Debug with Delve](https://encore.dev/docs/go/how-to/debug) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/cgo.md) ## Debugging with Delve Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore makes it easy to debug your application using [Delve](https://github.com/go-delve/delve "Delve"). First, make sure you have `dlv` installed by running (Go 1.16 and later): ```bash $ go install github.com/go-delve/delve/cmd/dlv@latest ``` You have two debugger options, you can either debug by attaching to a running process or by starting the process in debug mode. ## [Debug by starting the process in debug mode](https://encore.dev/docs/go/how-to/debug\#debug-by-starting-the-process-in-debug-mode) Run your Encore application with `encore run --debug=break`. This will launch your encore application with a headless Delve server, which will pause your application until a debugger is attached. ```bash $ encore run --debug=break API Base URL: http://localhost:4000 Dev Dashboard URL: http://localhost:9400/hello-world-cgu2 API server listening at: 127.0.0.1:2345 ``` Now it's time to attach the debugger. The instructions differ depending on how you would like to debug (in your terminal or in your editor). If instructions for your editor aren’t listed below, consult your editor for information on how to attach to a Delve server. ### [Terminal debugging](https://encore.dev/docs/go/how-to/debug\#terminal-debugging) To debug in your terminal, run `dlv attach :2345`. You should see: ```bash $ dlv connect :2345 Type 'help' for list of commands. (dlv) ``` How to use Delve’s terminal interface for debugging is out of scope for this guide, but there are great resources available. For a good introduction, see [Debugging with Delve](https://golang.cafe/blog/golang-debugging-with-delve.html "Debugging with Delve"). ### [Visual Studio Code](https://encore.dev/docs/go/how-to/debug\#visual-studio-code) To debug with VS Code you must first add a debug configuration. Press `Run -> Add Configuration`, choose `Go -> Connect to server`. Input `127.0.0.1` as host and `2345` as port. The resulting configuration should look something like this: ```json { "version": "0.2.0", "configurations": [\ {\ "name": "Connect to server",\ "type": "go",\ "request": "attach",\ "mode": "remote",\ "remotePath": "${workspaceFolder}",\ "port": 2345,\ "host": "127.0.0.1"\ }\ ] } ``` Next, open the **Run and Debug** menu in the toolbar on the left, select `Connect to server` (the configuration you just created), and then press the green arrow. That’s it! You should be able to set breakpoints and have the Encore application pause when they’re hit like you would expect. ### [Goland](https://encore.dev/docs/go/how-to/debug\#goland) To debug with Goland, you must create a new Go Remote configuration. Press `Run | Edit Configurations`, click the `+` button, and choose `Go Remote`. Give it a name and hit `OK`. Now select the configuration you just created and press the green bug. That's it. You should be able to set breakpoints and have the Encore application pause when they’re hit like you would expect. ## [Debug by attaching to a running process](https://encore.dev/docs/go/how-to/debug\#debug-by-attaching-to-a-running-process) Run your Encore application with `encore run --debug`. This will cause Encore to print the Process ID to the terminal, which you will use to attach your debugger: ```bash $ encore run --debug API Base URL: http://localhost:4000 Dev Dashboard URL: http://localhost:9400/hello-world-cgu2 Process ID: 51894 1:48PM TRC registered endpoint path=/hello/:name service=hello endpoint=Hello ``` (Your process id will differ). When your Encore application is running, it’s time to attach the debugger. The instructions differ depending on how you would like to debug (in your terminal or in your editor). If instructions for your editor aren’t listed below, consult your editor for information on how to attach a debugger to a running process. ### [Terminal debugging](https://encore.dev/docs/go/how-to/debug\#terminal-debugging-1) To debug in your terminal, run `dlv attach $PID` (replace `$PID` with your Process ID from the previous step). You should see: ```bash $ dlv attach 51894 Type 'help' for list of commands. (dlv) ``` How to use Delve’s terminal interface for debugging is out of scope for this guide, but there are great resources available. For a good introduction, see [Debugging with Delve](https://golang.cafe/blog/golang-debugging-with-delve.html "Debugging with Delve"). ### [Visual Studio Code](https://encore.dev/docs/go/how-to/debug\#visual-studio-code-1) To debug with VS Code you must first add a debug configuration. Press `Run -> Add Configuration`, choose `Go -> Attach to local process`. In the generated configuration, you should see `"processId": 0` as a field. Replace `0` with the process id from above. Next, open the **Run and Debug** menu in the toolbar on the left, select Attach to Process (the configuration you just created), and then press the green arrow. That’s it! You should be able to set breakpoints and have the Encore application pause when they’re hit like you would expect. ### [Goland](https://encore.dev/docs/go/how-to/debug\#goland-1) To debug with Goland, you must first install the `gops` package. Open a terminal and run the following command ```bash go get -t github.com/google/gops/ ``` Then click `Run | Attach to Process`. If a notification window appears, click the `Invoke 'go get gops'` link. Once it has completed, click `Run | Attach to Process` again. In the dialog that appears, select the process with the process ID from above. That's it. You should be able to set breakpoints and have the Encore application pause when they’re hit like you would expect. [Build with cgo](https://encore.dev/docs/go/how-to/cgo) [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/debug.md) ## HTTP Requests Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore makes it easy to define APIs and expose them, but it works best when you are in charge of the API schema. Sometimes you need more control over the underlying HTTP request, such as to accept incoming webhooks from other services, or to use WebSockets to stream data to/from the client. For these use cases Encore lets you define **raw endpoints**. Raw endpoints operate at a lower abstraction level, giving you access to the underlying HTTP request. ## [Defining raw endpoints](https://encore.dev/docs/go/how-to/http-requests\#defining-raw-endpoints) To define a raw endpoint, change the `//encore:api` annotation and function signature like so: ```go package service import "net/http" // Webhook receives incoming webhooks from Some Service That Sends Webhooks. //encore:api public raw method=POST path=/webhook func Webhook(w http.ResponseWriter, req *http.Request) { // ... operate on the raw HTTP request ... } ``` If you're an experienced Go developer, this is just a regular Go HTTP handler. See the [net/http documentation](https://pkg.go.dev/net/http#Handler) for more information on how Go HTTP handlers work. ## [Reading path parameters](https://encore.dev/docs/go/how-to/http-requests\#reading-path-parameters) Sometimes webhooks have information in the path that you may be interested in retrieving or validating. To do so, define the path with a path parameter, and then use [`encore.CurrentRequest`](https://pkg.go.dev/encore.dev#CurrentRequest) to access the path parameters. For example: ```go package service import ( "net/http" "encore.dev" ) //encore:api public raw method=POST path=/webhook/:id func Webhook(w http.ResponseWriter, req *http.Request) { id := encore.CurrentRequest().PathParams.Get("id") // ... Do something with id } ``` [Debug with Delve](https://encore.dev/docs/go/how-to/debug) [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/http-requests.md) ## Atlas and GORM Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) [Atlas](https://atlasgo.io/) is a popular tool for managing database migrations. [GORM](https://gorm.io/) is a popular ORM for Go. Encore provides excellent support for using them together to easily manage database schemas and migrations. Encore executes database migrations using [golang-migrate](https://github.com/golang-migrate/migrate), which Atlas supports out-of-the-box. This means that you can use Atlas to manage your Encore database migrations. The easiest way to use Atlas + GORM together is with Atlas's support for [external schemas](https://atlasgo.io/blog/2023/06/28/external-schemas-and-gorm-support). ## [Setting up GORM](https://encore.dev/docs/go/how-to/atlas-gorm\#setting-up-gorm) To set up your Encore application with GORM, start by installing the GORM package and associated Postgres driver: ```bash go get -u gorm.io/gorm gorm.io/driver/postgres ``` Then, in the service that you want to use GORM for, add the `*gorm.DB` as a dependency in your service struct (create a service struct if you don't already have one). For example, if you had a service called `blog`: ```go blog/blog.go package blog import ( "encore.dev/storage/sqldb" "gorm.io/driver/postgres" "gorm.io/gorm" ) //encore:service type Service struct { db *gorm.DB } var blogDB = sqldb.NewDatabase("blog", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // initService initializes the site service. // It is automatically called by Encore on service startup. func initService() (*Service, error) { db, err := gorm.Open(postgres.New(postgres.Config{ Conn: blogDB.Stdlib(), })) if err != nil { return nil, err } return &Service{db: db}, nil } ``` Finally, create the `migrations` directory inside the `blog` directory if it doesn't already exist. This is where Atlas will put your database migrations. ## [Setting up Atlas](https://encore.dev/docs/go/how-to/atlas-gorm\#setting-up-atlas) First [install Atlas](https://atlasgo.io/getting-started). Then, add an `atlas.hcl` file inside the `blog` directory: ```ini blog/atlas.hcl data "external_schema" "gorm" { program = ["env", "ENCORERUNTIME_NOPANIC=1", "go", "run", "./scripts/atlas-gorm-loader.go"] } env "local" { src = data.external_schema.gorm.url migration { dir = "file://migrations" format = golang-migrate } format { migrate { diff = "{{ sql . \" \" }}" } } } ``` Next, we need to create the `atlas-gorm-loader` script referenced above. It will use the [atlas-provider-gorm](https://github.com/ariga/atlas-provider-gorm) library provided by Atlas. Create the file as follows: ```go blog/scripts/atlas-gorm-loader.go package main import ( "fmt" "io" "os" _ "ariga.io/atlas-go-sdk/recordriver" "ariga.io/atlas-provider-gorm/gormschema" "encore.app/blog" ) // Define the models to generate migrations for. var models = []any{ &blog.Post{}, &blog.Comment{}, } func main() { stmts, err := gormschema.New("postgres").Load(models...) if err != nil { fmt.Fprintf(os.Stderr, "failed to load gorm schema: %v\n", err) os.Exit(1) } io.WriteString(os.Stdout, stmts) } ``` ## [Creating migrations](https://encore.dev/docs/go/how-to/atlas-gorm\#creating-migrations) To wrap things up, let's create a script to automate the process of generating migrations: ```bash blog/scripts/generate-migration #!/bin/bash set -eu DB_NAME=blog MIGRATION_NAME=${1:-} SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) # Reset the shadow database encore db reset --shadow $DB_NAME # GORM executes Go code without initializing Encore when generating migrations, # so configure the Encore runtime to be aware that this is expected. export ENCORERUNTIME_NOPANIC=1 # Generate the migration atlas migrate diff $MIGRATION_NAME --env local --dev-url "$(encore db conn-uri --shadow $DB_NAME)&search_path=public" ``` Finally let's make the script executable, and generate our first migration: ```bash $ chmod +x blog/scripts/generate-migration $ cd blog && ./scripts/generate-migration init ``` This will generate a new migration file in the `blog/migrations` directory, which will be automatically applied when running `encore run`. [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/atlas-gorm.md) ## Ent ORM Integration Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore has all the tools needed to support ORMs and migration frameworks out-of-the-box through [named databases](https://encore.dev/docs/go/primitives/share-db-between-services) and [migration files](https://encore.dev/docs/go/primitives/databases#defining-a-database-schema). Writing plain SQL might not work for your use case, or you may not want to use SQL in the first place. ORMs like [ent](https://entgo.io/) and migration frameworks like [Atlas](https://atlasgo.io/) can be used with Encore by integrating their logic with a system's database. Encore is not restrictive, it uses plain SQL migration files for its migrations. - If your ORM of choice can connect to any database using a [standard SQL driver](https://github.com/lib/pq), then it can be used with Encore. - If your migration framework can generate SQL migration files without any modifications, then it can be used with Encore. Let's take a look at how you can integrate ent with Encore, using Atlas for generating the migration files. ## [Add ent schemas to a service](https://encore.dev/docs/go/how-to/entgo-orm\#add-ent-schemas-to-a-service) [Install ent](https://entgo.io/docs/tutorial-setup#installation), then initialize your first schema in the service where you want to use it. For example, if you had the following app structure: ```sql /my-app β”œβ”€β”€ encore.app └── user // user service ``` You can then use this command to generate a user schema along with the ent directory that will contain that schema and all future generated files: ```bash $ go run entgo.io/ent/cmd/ent@latest new --target user/ent/schema User ``` The `--target` option sets the schema directory within your Encore system. Each system should contain its own models and schemas, and its own migration files. Like you would when using plain SQL. Add the fields and edges for your new model in the generated file under `user/ent/schema/user.go`. Now, run the following command: ```bash $ go run entgo.io/ent/cmd/ent@latest generate ./user/ent/schema ``` This generates the ent client files. Run this command again whenever you change the schemas. ## [Integrating ent with an Encore database](https://encore.dev/docs/go/how-to/entgo-orm\#integrating-ent-with-an-encore-database) Encore automates database provisioning, and automatically runs migrations in all environments. To integrate ent with Encore, we need to do three things: 1. Create the Encore database 2. Set up the ent client to use that database. 3. Generate migration files for the ent schema, using Atlas. ### [Create the Encore database](https://encore.dev/docs/go/how-to/entgo-orm\#create-the-encore-database) Create the database using [`sqldb.NewDatabase`](https://encore.dev/docs/go/primitives/databases) in `user/user.go`: ```go user/user.go package user import "encore.dev/storage/sqldb" var userDB = sqldb.NewDatabase("user", sqldb.DatabaseConfig{ Migrations: "./migrations", }) ``` Now, create the `migrations` directory, and leave it empty for now: ```bash $ mkdir user/migrations ``` ### [Connect ent to the database](https://encore.dev/docs/go/how-to/entgo-orm\#connect-ent-to-the-database) Next, extend the user service with a [Service Struct](https://encore.dev/docs/go/primitives/service-structs) that creates an ent client connected to the database. Replace the contents of the `user/user.go` file with: ```go user/user.go package user import ( "encore.dev/storage/sqldb" "entgo.io/ent/dialect" entsql "entgo.io/ent/dialect/sql" "encore.app/user/ent" ) var userDB = sqldb.NewDatabase("user", sqldb.DatabaseConfig{ Migrations: "./migrations", }) //encore:service type Service struct{ ent *ent.Client } func initService() (*Service, error) { driver := entsql.OpenDB(dialect.Postgres, userDB.Stdlib()) entClient := ent.NewClient(ent.Driver(driver)) return &Service{ent: entClient}, nil } ``` Now ent is fully wired up to the Encore database, and can be used from the service struct in any API endpoint. ## [Using Atlas for database migrations](https://encore.dev/docs/go/how-to/entgo-orm\#using-atlas-for-database-migrations) Finally, we'll set up Atlas to generate database migrations for the ent schema. First, make sure you [have Atlas installed](https://atlasgo.io/getting-started). Then, create the file `user/atlas.hcl` containing the following: ```ini user/atlas.hcl env "local" { src = "ent://ent/schema" migration { dir = "file://migrations" format = golang-migrate } format { migrate { diff = "{{ sql . \" \" }}" } } } ``` This tells Atlas to generate migrations for the ent schema, and to output them to the `migrations` directory. Atlas works by comparing the desired ent schema with the current database schema, and generating a migration to bring the database schema in line with the ent schema. This relies on a so-called "shadow database", which is an empty database that Atlas uses to compare the ent schema against. Fortunately for us, Encore has built-in support for shadow databases. Create the file `user/scripts/generate-migration` containing the following: ```bash user/scripts/generate-migration #!/bin/bash set -eu DB_NAME=user MIGRATION_NAME=${1:-} # Reset the shadow database encore db reset --shadow $DB_NAME # ent executes Go code without initializing Encore when generating migrations, # so configure the Encore runtime to be aware that this is expected. export ENCORERUNTIME_NOPANIC=1 # Generate the migration atlas migrate diff $MIGRATION_NAME --env local --dev-url "$(encore db conn-uri --shadow $DB_NAME)&search_path=public" ``` Finally, make the script executable, and generate our first migration: ```bash $ chmod +x user/scripts/generate-migration $ cd user && ./scripts/generate-migration init ``` You should see a new migration file being added to the `user/migrations` directory, containing the schema changes to create the ent models. You can now run the service with `encore run`, and everything should be ready to go! [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/entgo-orm.md) ## gRPC Integration Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) The [Connect protocol](https://connectrpc.com/) is an HTTP/2-based protocol for RPC communication. It's conceptually similar to gRPC, but with better support for using from browsers and JavaScript clients. This guide shows how to use Encore for setting up a Connect service for external clients to use: 1. First, we'll define a simple gRPC service using Protobuf and Connect. 2. Then, we'll implement the service in Go, using [connect-go](https://connectrpc.com/docs/go/getting-started). 3. Then, we'll mount the Connect service into Encore with a raw endpoint. 4. Finally, we'll call the Connect service from cURL using its JSON mapping. ## [Define a Connect service](https://encore.dev/docs/go/how-to/grpc-connect\#define-a-connect-service) We'll largely follow the connect-go [getting started guide](https://connectrpc.com/docs/go/getting-started) with some small tweaks. Start by installing the necessary tools: ```bash $ go install github.com/bufbuild/buf/cmd/buf@latest $ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install connectrpc.com/connect/cmd/protoc-gen-connect-go@latest ``` Next, inside your Encore application ( [create one if you haven't already](https://encore.dev/docs/go/quick-start)) create a new file at `greet/v1/greet.proto` with the following contents: ```ini greet/v1/greet.proto syntax = "proto3"; package greet.v1; option go_package = "encore.app/gen/greet/v1;greetv1"; message GreetRequest { string name = 1; } message GreetResponse { string greeting = 1; } service GreetService { rpc Greet(GreetRequest) returns (GreetResponse) {} } ``` Next, add a `buf.gen.yaml` in the repository root, containing: ```yaml buf.gen.yaml version: v2 plugins: - local: protoc-gen-go out: gen opt: paths=source_relative - local: protoc-gen-connect-go out: gen opt: paths=source_relative ``` Now it's time to generate the connect-go service code. Run: ```bash $ buf lint $ buf generate ``` If all went well, you should see a new `gen` directory in the repository root containing some generated Go code: ```go gen └── greet └── v1 β”œβ”€β”€ greet.pb.go └── greetv1connect └── greet.connect.go ``` ## [Implement the service](https://encore.dev/docs/go/how-to/grpc-connect\#implement-the-service) Now that we have the service definition, we can implement the Connect service in Go. Add the file `greet/greet.go` with the following contents: ```go greet/greet.go package greet import ( "context" "fmt" "log" "connectrpc.com/connect" greetv1 "encore.app/gen/greet/v1" // generated by protoc-gen-go ) type GreetServer struct{} func (s *GreetServer) Greet( ctx context.Context, req *connect.Request[greetv1.GreetRequest], ) (*connect.Response[greetv1.GreetResponse], error) { log.Println("Request headers: ", req.Header()) res := connect.NewResponse(&greetv1.GreetResponse{ Greeting: fmt.Sprintf("Hello, %s!", req.Msg.Name), }) res.Header().Set("Greet-Version", "v1") return res, nil } ``` ##### Please note The sample code is straight from the [getting started guide](https://connectrpc.com/docs/go/getting-started); there are no Encore specific changes required here. ## [Mount the service in Encore](https://encore.dev/docs/go/how-to/grpc-connect\#mount-the-service-in-encore) Now we'll create an Encore [service struct](https://encore.dev/docs/go/primitives/service-structs) that initializes the Connect service, and a [raw endpoint](https://encore.dev/docs/go/primitives/raw-endpoints) that forwards incoming requests to the Connect service. Add the file `greet/service.go` with the following contents: ```go greet/service.go package greet import ( "net/http" "encore.app/gen/greet/v1/greetv1connect" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) //encore:service type Service struct { routes http.Handler } //encore:api public raw path=/greet.v1.GreetService/*endpoint func (s *Service) GreetService(w http.ResponseWriter, req *http.Request) { s.routes.ServeHTTP(w, req) } func initService() (*Service, error) { greeter := &GreetServer{} mux := http.NewServeMux() path, handler := greetv1connect.NewGreetServiceHandler(greeter) mux.Handle(path, handler) return &Service{routes: mux}, nil } ``` That's it! We're ready to run the service and check that everything works. ## [Run the service](https://encore.dev/docs/go/how-to/grpc-connect\#run-the-service) Run the service with `encore run`: ```bash $ encore run ``` Once it starts up, open a separate terminal and use `grpcurl` to call the service: ```bash # Install grpcurl if you haven't already $ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest # Call the service with grpcurl grpcurl \ -protoset <(buf build -o -) -plaintext \ -d '{"name": "Jane"}' \ localhost:4000 greet.v1.GreetService/Greet {"greeting": "Hello, Jane!"} # Or call the service with curl $ curl -H "Content-Type: application/json" -d '{"name": "Jane"}' http://localhost:4000/greet.v1.GreetService/Greet {"greeting":"Hello, Jane!"} # Expected response ``` If you see `{"greeting":"Hello, Jane!"}`, everything is working! What's more, Encore automatically traces the incoming requests, and adds request logging and captures request metrics. [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/grpc-connect.md) ## Transactional Outbox Pattern Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) One of the hardest parts of building an event-driven application is ensuring consistency between services. A common pattern is for each service to have its own database and use Pub/Sub to notify other systems of business events. Inevitably this leads to inconsistencies since the Pub/Sub publishing is not transactional with the database writes. While there are several approaches to solving this, it's important the solution doesn't add too much complexity to what is often an already complex architecture. Perhaps the best solution in this regard is the [transactional outbox pattern](https://softwaremill.com/microservices-101/). Encore provides support for the transactional outbox pattern in the [x.encore.dev/infra/pubsub/outbox](https://pkg.go.dev/x.encore.dev/infra/pubsub/outbox) package. The transactional outbox works by binding a Pub/Sub topic to a database transaction, translating all calls to `topic.Publish` into inserting a database row in an `outbox` table. If/when the transaction later commits, the messages are picked up by a [Relay](https://pkg.go.dev/x.encore.dev/infra/pubsub/outbox#Relay) that polls the `outbox` table and publishes the messages to the actual Pub/Sub topic. ## [Publishing messages to the outbox](https://encore.dev/docs/go/how-to/pubsub-outbox\#publishing-messages-to-the-outbox) To publish messages to the outbox, a topic must first be bound to the outbox. This is done using [Pub/Sub topic references](https://encore.dev/docs/go/primitives/pubsub#using-topic-references) which allows you to retain complete type safety and the same interface as regular Pub/Sub topics, allowing existing code to continue to work without changes. ##### Please note In regular (non-outbox) usage the message id returned by `topic.Publish` is the same as the message id the subscriber receives when processing the message. With the outbox, this message id is not available until the transaction commits, so `topic.Publish` returns an id referencing the outbox row instead. The topic binding supports pluggable storage backends, enabling use of the outbox pattern with any transactional storage backend. Implementation are provided out-of-the-box for use with Encore's `encore.dev/storage/sqldb` package, as well as the standard library `database/sql` and `github.com/jackc/pgx/v5` drivers, but it's easy to write your own for other use cases. See the [Go package reference](https://pkg.go.dev/x.encore.dev/infra/pubsub/outbox#PersistFunc) for more information. For example, to use a transactional outbox to notify subscribers when a user is created: ```go outbox.go db_migration.sql // Create a SignupsTopic somehow. var SignupsTopic = pubsub.NewTopic[*SignupEvent](/* ... */) // Create a topic ref with publisher permissions. ref := pubsub.TopicRef[pubsub.Publisher[*SignupEvent]](SignupsTopic) // Bind it to the transactional outbox import "x.encore.dev/infra/pubsub/outbox" var tx *sqldb.Tx // somehow get a transaction ref = outbox.Bind(ref, outbox.TxPersister(tx)) // Calls to ref.Publish() will now insert a row in the outbox table. ``` Once the transaction commits any published messages via `ref` above will be stored in the `outbox` table. ## [Consuming messages from the outbox](https://encore.dev/docs/go/how-to/pubsub-outbox\#consuming-messages-from-the-outbox) Once committed, the messages are ready to be picked up and published to the actual Pub/Sub topic. That is done via the [Relay](https://pkg.go.dev/x.encore.dev/infra/pubsub/outbox#Relay). The relay continuously polls the `outbox` table and publishes any new messages to the actual Pub/Sub topic. The relay supports pluggable storage backends, enabling use of the outbox pattern with any transactional storage backend. An implementation is provided out-of-the-box that uses Encore's built-in [SQL database support](https://pkg.go.dev/x.encore.dev/infra/pubsub/outbox#SQLDBStore), but it's easy to write your own for other databases. The topics to poll must be registered with the relay, typically during service initialization. For example: ```go user/service.go package user import ( "context" "encore.dev/pubsub" "encore.dev/storage/sqldb" "x.encore.dev/infra/pubsub/outbox" ) type Service struct { signupsRef pubsub.Publisher[*SignupEvent] } // db is the database the outbox table is stored in var db = sqldb.NewDatabase(...) // Create the SignupsTopic somehow. var SignupsTopic = pubsub.NewTopic[*SignupEvent](/* ... */) func initService() (*Service, error) { // Initialize the relay to poll from our database. relay := outbox.NewRelay(outbox.SQLDBStore(db)) // Register the SignupsTopic to be polled. signupsRef := pubsub.TopicRef[pubsub.Publisher[*SignupEvent]](SignupsTopic) outbox.RegisterTopic(relay, signupsRef) // Start polling. go relay.PollForMessage(context.Background(), -1) return &Service{signupsRef: signupsRef}, nil } ``` [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/pubsub-outbox.md) ## Dependency Injection Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Dependency Injection is a fancy name for a simple concept: when you depend on some functionality, add that dependency as a field on your struct and refer to it that way instead of directly calling it. By doing so it becomes easier to test your services by swapping out certain dependencies for other implementations (often with the use of interfaces). Encore provides built-in support for dependency injection in services through the use of the `//encore:service` directive and a **service struct**. See the [service structs docs](https://encore.dev/docs/go/primitives/service-structs) more information on how to define service structs. As an example, consider an email service that has a SendGrid API client that is dependency injected. It might look like this: ```go package email //encore:service type Service struct { sendgridClient *sendgrid.Client } func initService() (*Service, error) { client, err := sendgrid.NewClient() if err != nil { return nil, err } return &Service{sendgridClient: client}, nil } ``` You can then define APIs as methods on this struct: ```go //encore:api private func (s *Service) Send(ctx context.Context, p *SendParams) error { // ... use s.sendgridClient to send emails ... } ``` ### [Mocking dependencies](https://encore.dev/docs/go/how-to/dependency-injection\#mocking-dependencies) If you wish to mock out the SendGrid client for testing purposes you can change the field to an interface: ```go type sendgridClient interface { SendEmail(...) // a hypothetical signature, for illustration purposes } //encore:service type Service struct { sendgridClient sendgridClient } ``` Then during your tests you can instantiate the service object by hand: ```go func TestFoo(t *testing.T) { svc := &Service{sendgridClient: &myMockClient{}} // ... } ``` [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/dependency-injection.md) ## Auth0 Authentication Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this guide you will learn how to set up an Encore [auth handler](https://encore.dev/docs/go/develop/auth#the-auth-handler) that makes use of [Auth0](https://auth0.com/) in order to add a seamless signup and login experience to your web app. For all the code and instructions of how to clone and run this example locally, see the [Auth0 Example](https://github.com/encoredev/examples/tree/main/auth0) in our examples repo. ## [Communicate with Auth0](https://encore.dev/docs/go/how-to/auth0-auth\#communicate-with-auth0) In your Encore app, install two modules: ```bash $ go get github.com/coreos/go-oidc/v3/oidc golang.org/x/oauth2 ``` Create a folder and naming it `auth`, this is where our authentication related backend code will live. Next, let's set up the Auth0 `Authenticator` that will be used by our auth handler. The `Authenticator` has a method to configure and return [OAuth2](https://pkg.go.dev/golang.org/x/oauth2?utm_source=godoc) and [oidc](https://pkg.go.dev/github.com/coreos/go-oidc?utm_source=godoc) clients, and another one to verify an ID Token. Create `auth/authenticator.go` and paste the following: ```go package auth import ( "context" "crypto/rand" "encoding/base64" "encore.dev/config" "errors" "github.com/coreos/go-oidc/v3/oidc" "golang.org/x/oauth2" ) type Auth0Config struct { ClientID config.String Domain config.String CallbackURL config.String LogoutURL config.String } var cfg = config.Load[*Auth0Config]() var secrets struct { Auth0ClientSecret string } // Authenticator is used to authenticate our users. type Authenticator struct { *oidc.Provider oauth2.Config } // New instantiates the *Authenticator. func New() (*Authenticator, error) { provider, err := oidc.NewProvider( context.Background(), "https://"+cfg.Domain()+"/", ) if err != nil { return nil, err } conf := oauth2.Config{ ClientID: cfg.ClientID(), ClientSecret: secrets.Auth0ClientSecret, RedirectURL: cfg.CallbackURL(), Endpoint: provider.Endpoint(), Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, } return &Authenticator{ Provider: provider, Config: conf, }, nil } // VerifyIDToken verifies that an *oauth2.Token is a valid *oidc.IDToken. func (a *Authenticator) VerifyIDToken(ctx context.Context, token *oauth2.Token) (*oidc.IDToken, error) { rawIDToken, ok := token.Extra("id_token").(string) if !ok { return nil, errors.New("no id_token field in oauth2 token") } oidcConfig := &oidc.Config{ ClientID: a.ClientID, } return a.Verifier(oidcConfig).Verify(ctx, rawIDToken) } func generateRandomState() (string, error) { b := make([]byte, 32) _, err := rand.Read(b) if err != nil { return "", err } state := base64.StdEncoding.EncodeToString(b) return state, nil } ``` ## [Set up the auth handler](https://encore.dev/docs/go/how-to/auth0-auth\#set-up-the-auth-handler) It's time to define your [auth handler](https://encore.dev/docs/go/develop/auth) and the endpoints needed for the login and logout flow. Create the `auth/auth.go` file and paste the following: ```go package auth import ( "context" "net/url" "encore.dev/beta/auth" "encore.dev/beta/errs" "github.com/coreos/go-oidc/v3/oidc" ) // Service struct definition. // Learn more: encore.dev/docs/primitives/services-and-apis/service-structs // //encore:service type Service struct { auth *Authenticator } // initService is automatically called by Encore when the service starts up. func initService() (*Service, error) { authenticator, err := New() if err != nil { return nil, err } return &Service{auth: authenticator}, nil } type LoginResponse struct { State string `json:"state"` AuthCodeURL string `json:"auth_code_url"` } //encore:api public method=POST path=/auth/login func (s *Service) Login(ctx context.Context) (*LoginResponse, error) { state, err := generateRandomState() if err != nil { return nil, &errs.Error{ Code: errs.Internal, Message: err.Error(), } } return &LoginResponse{ State: state, // add the audience to the auth code url AuthCodeURL: s.auth.AuthCodeURL(state), }, nil } type CallbackRequest struct { Code string `json:"code"` } type CallbackResponse struct { Token string `json:"token"` } //encore:api public method=POST path=/auth/callback func (s *Service) Callback( ctx context.Context, req *CallbackRequest, ) (*CallbackResponse, error) { // Exchange an authorization code for a token. token, err := s.auth.Exchange(ctx, req.Code) if err != nil { return nil, &errs.Error{ Code: errs.PermissionDenied, Message: "Failed to convert an authorization code into a token.", } } idToken, err := s.auth.VerifyIDToken(ctx, token) if err != nil { return nil, &errs.Error{ Code: errs.Internal, Message: "Failed to verify ID Token.", } } var profile map[string]interface{} if err := idToken.Claims(&profile); err != nil { return nil, &errs.Error{ Code: errs.Internal, Message: err.Error(), } } return &CallbackResponse{ Token: token.Extra("id_token").(string), }, nil } type LogoutResponse struct { RedirectURL string `json:"redirect_url"` } //encore:api public method=GET path=/auth/logout func (s *Service) Logout(ctx context.Context) (*LogoutResponse, error) { logoutUrl, err := url.Parse("https://" + cfg.Domain() + "/v2/logout") if err != nil { return nil, &errs.Error{ Code: errs.Internal, Message: err.Error(), } } returnTo, err := url.Parse(cfg.LogoutURL()) if err != nil { return nil, &errs.Error{ Code: errs.Internal, Message: err.Error(), } } parameters := url.Values{} parameters.Add("returnTo", returnTo.String()) parameters.Add("client_id", cfg.ClientID()) logoutUrl.RawQuery = parameters.Encode() return &LogoutResponse{ RedirectURL: logoutUrl.String(), }, nil } type ProfileData struct { Email string `json:"email"` Picture string `json:"picture"` } // The `encore:authhandler` annotation tells Encore to run this function for all // incoming API call that requires authentication. // Learn more: encore.dev/docs/develop/auth#the-auth-handler // //encore:authhandler func (s *Service) AuthHandler( ctx context.Context, token string, ) (auth.UID, *ProfileData, error) { oidcConfig := &oidc.Config{ ClientID: s.auth.ClientID, } t, err := s.auth.Verifier(oidcConfig).Verify(ctx, token) if err != nil { return "", nil, &errs.Error{ Code: errs.Unauthenticated, Message: "invalid token", } } var profile map[string]interface{} if err := t.Claims(&profile); err != nil { return "", nil, &errs.Error{ Code: errs.Internal, Message: err.Error(), } } // Extract profile data returned from the identity provider. // auth0.com/docs/manage-users/user-accounts/user-profiles/user-profile-structure profileData := &ProfileData{ Email: profile["email"].(string), Picture: profile["picture"].(string), } return auth.UID(profile["sub"].(string)), profileData, nil } // Endpoints annotated with `auth` are public and requires authentication // Learn more: encore.dev/docs/primitives/apis#access-controls // //encore:api auth method=GET path=/profile func GetProfile(ctx context.Context) (*ProfileData, error) { return auth.Data().(*ProfileData), nil } ``` ## [Auth0 settings](https://encore.dev/docs/go/how-to/auth0-auth\#auth0-settings) The `Authenticator` class requires some values that are specific your Auth0 application, namely the `ClientID`, `ClientSecret`, `Domain`, `CallbackURL` and `LogoutURL`. Create an Auth0 account if you haven't already. Then, in the Auth0 dashboard, create a new _Single Page Web Applications_. ![](https://encore.dev/assets/docs/auth0-create-app.png) Next, go to the _Application Settings_ section. There you will find the `Domain`, `Client ID`, and `Client Secret` that you need to communicate with Auth0. Copy these values, we will need them shortly. ![](https://encore.dev/assets/docs/auth0-basic-info.png) A callback URL is where Auth0 redirects the user after they have been authenticated. Add `http://localhost:3000/callback` to the _Allowed Callback URLs_. You will need to add more URLs to this list when you have a production or staging environments. The same goes for the logout URL (were the user will get redirected after logout). Add `http://localhost:3000/` to the _Allowed Logout URLs_. ![](https://encore.dev/assets/docs/auth0-app-uris.png) ## [Config and secrets](https://encore.dev/docs/go/how-to/auth0-auth\#config-and-secrets) Create a [configuration file](https://encore.dev/docs/go/develop/config) in the `auth` service and name it `auth-config.cue`. Add the following: ```cue ClientID: "" Domain: "" // An application running locally if #Meta.Environment.Type == "development" && #Meta.Environment.Cloud == "local" { CallbackURL: "http://localhost:3000/callback" LogoutURL: "http://localhost:3000/" } ``` Replace the values for the `ClientID` and `Domain` that you got from the Auth0 dashboard. The `ClientSecret` is especially sensitive and should not be hardcoded in your code/config. Instead, you should store that as an [Encore secret](https://encore.dev/docs/go/primitives/secrets). From your terminal (inside your Encore app directory), run: ```bash $ encore secret set --prod Auth0ClientSecret ``` Now you should do the same for the development secret. The most secure way is to set up a different Auth0 application and use that for development. Depending on your security requirements you could also use the same secret for development and production. Once you have a client secret for development, set it similarly to before: ```bash $ encore secret set --dev Auth0ClientSecret ``` That's it! Encore will run your auth handler and validate the token against Auth0. ## [Frontend](https://encore.dev/docs/go/how-to/auth0-auth\#frontend) Now that the backend is set up, we can create a frontend application that uses the login flow. Here's an example using [React](https://react.dev/) together with [React Router](https://reactrouter.com/). This example also makes use of a Encores ability to [generate request clients](https://encore.dev/docs/go/cli/client-generation) to make the communication with our backend simple and typesafe. ```javascript App.tsx lib/auth.ts components/LoginStatus.tsx lib/getRequestClient.ts import { PropsWithChildren } from "react"; import { createBrowserRouter, Link, Outlet, redirect, RouterProvider, useRouteError, } from "react-router-dom"; import { Auth0Provider } from "./lib/auth"; import AdminDashboard from "./components/AdminDashboard.tsx"; import IndexPage from "./components/IndexPage.tsx"; import "./App.css"; import LoginStatus from "./components/LoginStatus.tsx"; // Application routes const router = createBrowserRouter([\ {\ id: "root",\ path: "/",\ Component: Layout,\ errorElement: (\ \ \ \ ),\ children: [\ {\ Component: Outlet,\ children: [\ {\ index: true,\ Component: IndexPage,\ },\ {\ // Login route\ path: "login",\ loader: async ({ request }) => {\ const url = new URL(request.url);\ const searchParams = new URLSearchParams(url.search);\ const returnToURL = searchParams.get("returnTo") ?? "/";\ \ if (Auth0Provider.isAuthenticated()) return redirect(returnToURL);\ \ try {\ const returnURL = await Auth0Provider.login(returnToURL);\ return redirect(returnURL);\ } catch (error) {\ throw new Error("Login failed");\ }\ },\ },\ {\ // Callback route, redirected to from Auth0 after login\ path: "callback",\ loader: async ({ request }) => {\ const url = new URL(request.url);\ const searchParams = new URLSearchParams(url.search);\ const state = searchParams.get("state");\ const code = searchParams.get("code");\ \ if (!state || !code) throw new Error("Login failed");\ \ try {\ const redirectURL = await Auth0Provider.validate(state, code);\ return redirect(redirectURL);\ } catch (error) {\ throw new Error("Login failed");\ }\ },\ },\ {\ // Logout route\ path: "logout",\ loader: async () => {\ try {\ const redirectURL = await Auth0Provider.logout();\ return redirect(redirectURL);\ } catch (error) {\ throw new Error("Logout failed");\ }\ },\ },\ {\ element: ,\ // Redirect to /login if not authenticated\ loader: async ({ request }) => {\ if (!Auth0Provider.isAuthenticated()) {\ const params = new URLSearchParams();\ params.set("returnTo", new URL(request.url).pathname);\ return redirect("/login?" + params.toString());\ }\ return null;\ },\ // Protected routes\ children: [\ {\ path: "admin-dashboard",\ Component: AdminDashboard,\ },\ ],\ },\ ],\ },\ ],\ },\ ]); export default function App() { return Loading...

} />; } function Layout({ children }: PropsWithChildren) { return (
{children ?? }
); } function ErrorBoundary() { const error = useRouteError() as Error; return (

Something went wrong

{error.message || JSON.stringify(error)}

); } ``` ## [Auth0 Social Identity Providers](https://encore.dev/docs/go/how-to/auth0-auth\#auth0-social-identity-providers) Auth0 supports multiple [social identity providers](https://auth0.com/docs/authenticate/identity-providers/social-identity-providers) (like Google and GitHub) for web applications out of the box. [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/auth0-auth.md) ## Clerk Authentication Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) In this guide you will learn how to set up an Encore [auth handler](https://encore.dev/docs/go/develop/auth#the-auth-handler) that makes use of [Clerk](https://clerk.com/) in order to add an integrated signup and login experience to your web app. For all the code and instructions of how to clone and run this example locally, see the [Clerk Example](https://github.com/encoredev/examples/tree/main/clerk) in our examples repo. ## [Set up the auth handler](https://encore.dev/docs/go/how-to/clerk-auth\#set-up-the-auth-handler) In your Encore app, install the following module: ```bash $ go get github.com/clerkinc/clerk-sdk-go/clerk ``` Create a folder and naming it `auth`, this is where our authentication related backend code will live. It's time to define your [auth handler](https://encore.dev/docs/go/develop/auth). Create `auth/auth.go` and paste the following: ```go package auth import ( "context" "encore.dev/beta/auth" "encore.dev/beta/errs" "github.com/clerkinc/clerk-sdk-go/clerk" ) var secrets struct { ClientSecretKey string } // Service struct definition. // Learn more: encore.dev/docs/primitives/services-and-apis/service-structs // //encore:service type Service struct { client clerk.Client } // initService is automatically called by Encore when the service starts up. func initService() (*Service, error) { client, err := clerk.NewClient(secrets.ClientSecretKey) if err != nil { return nil, err } return &Service{client: client}, nil } type UserData struct { ID string `json:"id"` Username *string `json:"username"` FirstName *string `json:"first_name"` LastName *string `json:"last_name"` ProfileImageURL string `json:"profile_image_url"` PrimaryEmailAddressID *string `json:"primary_email_address_id"` EmailAddresses []clerk.EmailAddress `json:"email_addresses"` } // The `encore:authhandler` annotation tells Encore to run this function for all // incoming API call that requires authentication. // Learn more: encore.dev/docs/develop/auth#the-auth-handler // //encore:authhandler func (s *Service) AuthHandler(ctx context.Context, token string) (auth.UID, *UserData, error) { // verify the session sessClaims, err := s.client.VerifyToken(token) if err != nil { return "", nil, &errs.Error{ Code: errs.Unauthenticated, Message: "invalid token", } } user, err := s.client.Users().Read(sessClaims.Claims.Subject) if err != nil { return "", nil, &errs.Error{ Code: errs.Internal, Message: err.Error(), } } userData := &UserData{ ID: user.ID, Username: user.Username, FirstName: user.FirstName, LastName: user.LastName, ProfileImageURL: user.ProfileImageURL, PrimaryEmailAddressID: user.PrimaryEmailAddressID, EmailAddresses: user.EmailAddresses, } return auth.UID(user.ID), userData, nil } ``` ## [Clerk credentials](https://encore.dev/docs/go/how-to/clerk-auth\#clerk-credentials) Create a Clerk account if you haven't already. Then, in the Clerk dashboard, create a new applications. Next, go to the _API Keys_ page for your app. Copy one of the "Secret keys" (the "Publishable Key" will be used by your frontend). The `Secret key` is sensitive and should not be hardcoded in your code/config. Instead, you should store that as an [Encore secret](https://encore.dev/docs/go/primitives/secrets). From your terminal (inside your Encore app directory), run: ```bash $ encore secret set --prod ClientSecretKey ``` Now you should do the same for the development secret. The most secure way is to create another secret key (Clerk allows you to have multiple). Once you have a client secret for development, set it similarly to before: ```bash $ encore secret set --dev ClientSecretKey ``` ## [Frontend](https://encore.dev/docs/go/how-to/clerk-auth\#frontend) Clerk offers a [React SDK](https://clerk.com/docs/references/react/overview) for the frontend which makes it really simple to integrate a login/signup flow inside your web app as well as getting the token required to communicate with your Encore backend. You can use the `useAuth` hook from `@clerk/clerk-react` to get the token and send it to your backend. ```tsx import { useAuth } from '@clerk/clerk-react'; export default function ExternalDataPage() { const { getToken, isLoaded, isSignedIn } = useAuth(); if (!isLoaded) { // Handle loading state however you like return
Loading...
; } if (!isSignedIn) { // Handle signed out state however you like return
Sign in to view this page
; } const fetchDataFromExternalResource = async () => { const token = await getToken(); // Use token to send to Encore backend when fetching data return data; } return
...
; } ``` For a fully working backend + frontend example see the [Clerk Example](https://github.com/encoredev/examples/tree/main/clerk) in our examples repo. [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/clerk-auth.md) ## Firebase Auth Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Encore's [authentication support](https://encore.dev/docs/go/develop/auth) provides a simple yet powerful way of dealing with various authentication scenarios. [Firebase Authentication](https://firebase.google.com/docs/auth) is a common solution for quickly setting up a user store and simplifying social logins. Encore makes it really easy to integrate with Firebase Authentication on the backend. For all the code and instructions of how to clone and run this example locally, see the [Firebase Auth Example](https://github.com/encoredev/examples/tree/main/firebase-auth) in our examples repo. ## [Set up auth handler](https://encore.dev/docs/go/how-to/firebase-auth\#set-up-auth-handler) First, install two modules: ```bash $ go get firebase.google.com/go/v4 go4.org/syncutil ``` Next it's time to define your [authentication handler](https://encore.dev/docs/go/develop/auth). It can live in whatever service you'd like, but it's usually easiest to create a designated `user` service. Create the `user/user.go` file and add the following skeleton code: ```go package user import ( "context" "strings" "encore.dev/beta/auth" firebase "firebase.google.com/go/v4" fbauth "firebase.google.com/go/v4/auth" "go4.org/syncutil" "google.golang.org/api/option" ) // Data represents the user's data stored in Firebase Auth. type Data struct { // Email is the user's email. Email string // Name is the user's name. Name string // Picture is the user's picture URL. Picture string } // ValidateToken validates an auth token against Firebase Auth. //encore:authhandler func ValidateToken(ctx context.Context, token string) (auth.UID, *Data, error) { panic("Not Yet Implemented") } ``` ## [Initialize Firebase SDK](https://encore.dev/docs/go/how-to/firebase-auth\#initialize-firebase-sdk) Next, let's set up the Firebase Auth client. We'll use [`syncutil.Once`](https://pkg.go.dev/go4.org/syncutil#Once) to do it lazily the first time we need it. Add to the bottom of our file: ```go var ( fbAuth *fbauth.Client setupOnce syncutil.Once ) // setupFB ensures Firebase Auth is setup. func setupFB() error { return setupOnce.Do(func() error { opt := option.WithCredentialsJSON([]byte(secrets.FirebasePrivateKey)) app, err := firebase.NewApp(context.Background(), nil, opt) if err == nil { fbAuth, err = app.Auth(context.Background()) } return err }) } var secrets struct { // FirebasePrivateKey is the JSON credentials for calling Firebase. FirebasePrivateKey string } ``` ## [Validate token against Firebase](https://encore.dev/docs/go/how-to/firebase-auth\#validate-token-against-firebase) Now that we have the code to initialize Firebase Auth, we can use it from our `ValidateToken` auth handler. Update the function to look like the following: ```go func ValidateToken(ctx context.Context, token string) (auth.UID, *Data, error) { if err := setupFB(); err != nil { return "", nil, err } tok, err := fbAuth.VerifyIDToken(ctx, token) if err != nil { return "", nil, err } email, _ := tok.Claims["email"].(string) name, _ := tok.Claims["name"].(string) picture, _ := tok.Claims["picture"].(string) uid := auth.UID(tok.UID) usr := &Data{ Email: email, Name: name, Picture: picture, } return uid, usr, nil } ``` Great! We're done with the code. Now we just need to set up the secret. ## [Set Firebase secret credentials](https://encore.dev/docs/go/how-to/firebase-auth\#set-firebase-secret-credentials) If you haven't already, set up a [Firebase](https://firebase.google.com/) project. Then, go to **Project settings** and navigate to **Service accounts**. Select `Go` as the language of choice and click `Generate new private key`. Download the generated key and take note where it is stored. Next, store the private key as your firebase secret. From your terminal (inside your Encore app directory), run: ```bash $ encore secret set --type prod FirebasePrivateKey < /path/to/firebase-private-key.json Successfully updated production secret FirebasePrivateKey ``` Now you should do the same for the development secret. The most secure way is to set up a different Firebase project and use that for development. Depending on your security requirements you could also use the same Firebase project, but we recommend generating a new private key for development in that case. Once you have a private key for development, set it similarly to before: ```bash $ encore secret set --type dev,local,pr FirebasePrivateKey < /path/to/firebase-private-key.json Successfully updated development secret FirebasePrivateKey ``` That's it! You can now call your Encore application and pass in Firebase tokens. Encore will run your auth handler and validate the token against Firebase Auth. ## [Frontend](https://encore.dev/docs/go/how-to/firebase-auth\#frontend) Firebase offers a [npm package](https://www.npmjs.com/package/firebase) for your web frontend which makes it really simple to create a login/signup flow inside your web app as well as getting the token required to communicate with your Encore backend. For a fully working backend + frontend example see the [Firebase Auth Example](https://github.com/encoredev/examples/tree/main/firebase-auth) in our examples repo. [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/firebase-auth.md) ## Logto Authentication Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) [Logto](https://logto.io/) is a modern Auth0 alternative that helps you build the sign-in experience and user identity within minutes. It's particularly well-suited for protecting API services built with Encore. This guide will show you how to integrate Logto with your Encore application to add authentication and authorization capabilities. You can find the complete [Logto example](https://github.com/encoredev/examples/tree/main/logto-react-sdk) in our examples repo. ## [Logto settings](https://encore.dev/docs/go/how-to/logto-auth\#logto-settings) Before we begin integrating with Encore, you'll need to set up a few things in Logto: 1. Create an account at [Logto Cloud](https://cloud.logto.io/) if you don't have one yet. 2. Create an API Resource in Logto Console, this represents your Encore API service - Go to "API Resources" in Logto Console and create a new API - Set a name and API identifier (e.g., `https://api.encoreapp.com`) - Note down the API identifier on the API resource details page as we'll need it later ![](https://encore.dev/assets/docs/logto-api-resource.png) 3. Create an application for your frontend application - Go to "Applications" in Logto Console - Create a new application according to your frontend framework (We use React as an example, but you can create any Single-Page Application (SPA) or native app) - (Optional, we'll cover this later) Integrate Logto with your frontend application according to the guide in the Logto Console. - Note down the application ID and issuer URL on the Application details page as we'll need them later ![](https://encore.dev/assets/docs/logto-application-endpoints.png) ## [Setup the auth handler](https://encore.dev/docs/go/how-to/logto-auth\#setup-the-auth-handler) Now let's implement the authentication in your Encore application. We'll use Encore's built-in [auth handler](https://encore.dev/docs/go/develop/auth) to validate Logto's JWT tokens. Add these two modules in your Encore application: ```bash $ go get github.com/golang-jwt/jwt/v5 $ go get github.com/MicahParks/keyfunc/v3 ``` Create `auth/auth.go` and add the following code: ```go package auth import ( "context" "time" "encore.dev/beta/auth" "encore.dev/beta/errs" "encore.dev/config" "github.com/MicahParks/keyfunc/v3" "github.com/golang-jwt/jwt/v5" ) // Configuration variables for authentication type LogtoAuthConfig struct { // The issuer URL Issuer config.String // URL to fetch JSON Web Key Set (JWKS) JwksUri config.String // Expected audience for the JWT ApiResourceIndicator config.String // Expected client ID in the token claims ClientId config.String } var authConfig *LogtoAuthConfig = config.Load[*LogtoAuthConfig]() // RequiredClaims defines the expected structure of JWT claims // Extends the standard JWT claims with a custom ClientID field type RequiredClaims struct { ClientID string `json:"client_id"` jwt.RegisteredClaims } // AuthHandler validates JWT tokens and extracts the user ID // Implements Encore's authentication handler interface // //encore:authhandler func AuthHandler(ctx context.Context, token string) (auth.UID, error) { // Fetch and parse the JWKS (JSON Web Key Set) from the identity provider jwks, err := keyfunc.NewDefaultCtx(ctx, []string{authConfig.JwksUri()}) if err != nil { return "", &errs.Error{ Code: errs.Internal, Message: "failed to fetch JWKS", } } // Parse and validate the JWT token with required claims and validation options parsedToken, err := jwt.ParseWithClaims( token, &RequiredClaims{}, jwks.Keyfunc, // Expect the token to be intended for this API resource jwt.WithAudience(authConfig.ApiResourceIndicator()), // Expect the token to be issued by this issuer jwt.WithIssuer(authConfig.Issuer()), // Allow some leeway for clock skew jwt.WithLeeway(time.Minute*10), ) // Check if there were any errors during token parsing if err != nil { return "", &errs.Error{ Code: errs.Unauthenticated, Message: "invalid token", } } // Verify that the client ID in the token matches the expected client ID if parsedToken.Claims.(*RequiredClaims).ClientID != authConfig.ClientId() { return "", &errs.Error{ Code: errs.Unauthenticated, Message: "invalid token", } } // Extract the user ID (subject) from the token claims userId, err := parsedToken.Claims.GetSubject() if err != nil { return "", &errs.Error{ Code: errs.Unauthenticated, Message: "invalid token", } } // Return the user ID as an Encore auth.UID return auth.UID(userId), nil } ``` Create a [configuration file](https://encore.dev/docs/go/develop/config) in the auth service and name it `auth-config.cue`. Add the following: ```cue Issuer: "" JwksUri: "/jwks" ApiResourceIndicator: "" ClientId: "" ``` Replace the values with the ones you noted down from your Logto settings: - ``: The issuer URL from your Logto application endpoints (e.g., `https://your-tenant.logto.app`) - ``: The API identifier you set when creating the API resource (e.g., `https://api.encoreapp.com`) - ``: The application ID from your Logto application details page For example, your `auth-config.cue` might look like: ```cue Issuer: "https://your-tenant.logto.app" JwksUri: "https://your-tenant.logto.app/jwks" ApiResourceIndicator: "https://api.encoreapp.com" ClientId: "2gadf3mp0zotlq8j1k5x" ``` And then, you can use this auth handler to protect your API endpoints: ```go package api import ( "context" "fmt" "encore.dev/beta/auth" "encore.dev/beta/errs" ) //encore:api auth path=/api/hello func Api(ctx context.Context) (*Response, error) { userId, hasUserId := auth.UserID() if !hasUserId { return nil, &errs.Error{ Code: errs.Internal, Message: "User ID not found", } } msg := fmt.Sprintf("Hello, %s!", userId) return &Response{Message: msg}, nil } type Response struct { Message string } ``` ## [Frontend](https://encore.dev/docs/go/how-to/logto-auth\#frontend) We've completed our work in the Encore API service. Now we need to integrate Logto with our frontend application. You can choose the framework you are using in the [Logto Quick start](https://docs.logto.io/quick-starts) page to integrate Logto with your frontend application. In this guide we use React as an example. Check out the [Add authentication to your React application](https://docs.logto.io/quick-starts/react) guide to learn how to integrate Logto with your React application. In this example, you only need to complete up to the Integration section. After that, we'll demonstrate how the frontend application can obtain an access token from Logto to access the Encore API. First, update your `LogtoConfig` by adding the API resource used in your Encore app to the `resources` field. This tells Logto that we will be requesting access tokens for this API resource (Encore API). ```ts import { LogtoConfig } from '@logto/react'; const config: LogtoConfig = { // ...other configs resources: [''], }; ``` After updating the `LogtoConfig`, if a user is already signed in, they need to sign out and sign in again for the new `LogtoConfig` settings to take effect. Once the user is logged in, you can use the `getAccessToken` method provided by the Logto React SDK to obtain an access token for accessing specific API resources. For example, to access the Encore API, we use `https://api.encoreapp.com` as the API resource identifier. Then, add this access token to the request headers as the `Authorization` field in subsequent requests. ```ts const { getAccessToken } = useLogto(); const accessToken = await getAccessToken(''); // Add this access token to the request headers as the 'Authorization' field in subsequent requests fetch('/hello', { headers: { Authorization: `Bearer ${accessToken}`, }, }); ``` Here's the key frontend code: ```arduino config/logto.tsx pages/ProtectedResource.tsx import { LogtoConfig } from '@logto/react' export const config: LogtoConfig = { endpoint: '', appId: '', resources: [''], } export const appConfig = { apiResourceIndicator: '', signInRedirectUri: '', signOutRedirectUri: '', } export const encoreApiEndpoint = '' ``` That's it, you've successfully integrated Logto with your Encore application. You can find the complete example code [here](https://github.com/encoredev/examples/tree/main/logto-react-sdk). ## [Explore more](https://encore.dev/docs/go/how-to/logto-auth\#explore-more) If you want to use more Logto features, you can refer to the following links for more information: - Combine Logto's [Custom token claims](https://docs.logto.io/developers/custom-token-claims) to set [custom user data](https://encore.dev/docs/go/develop/auth#with-custom-user-data) in the auth handler - Use [Logto RBAC features](https://docs.logto.io/authorization/role-based-access-control) to add authorization support to your application. The React integration tutorial also demonstrates how to add `scope` information to your Access token (note that you need to sign in again after updating Logto config) [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/how-to/logto-auth.md) ## Encore Migration Guide Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) _We realize most people read this page before even trying Encore, so we start with a perspective on how you might reason about adopting Encore. Read on to see what tools are available for migrating away._ Picking technologies for your project is an important decision. It's tricky because you don't know what the requirements are going to look like in the future. This uncertainty makes many teams opt for maximum flexibility, often without acknowledging this has a significant negative effect on productivity. When designing Encore, we've leaned on standardization to provide a well-integrated and highly productive development workflow. The design is based on the core team's experience building scalable distributed systems at Spotify and Google, complemented with loads of invaluable input from the developer community. In practise Encore is opinionated only in certain areas which are critical for enabling the static analysis used to create Encore's application model. This is fundamental to how Encore can provide its powerful features, like automatically instrumenting distributed tracing, and provisioning and managing cloud infrastructure. ## [Accommodating for your unique requirements](https://encore.dev/docs/go/migration/migrate-away\#accommodating-for-your-unique-requirements) Many software projects end up having a few novel requirements, which are highly specific to the problem domain. To accommodate for this, Encore is designed to let you go outside of the standardized Backend Framework when you need to, for example: - You can drop down in abstraction level in the API framework using [raw endpoints](https://encore.dev/docs/go/primitives/raw-endpoints). - You can use tools like the [Terraform provider](https://encore.dev/docs/platform/integrations/terraform) to integrate infrastructure that is not managed by Encore ## [Mitigating risk through Open Source and efficiency](https://encore.dev/docs/go/migration/migrate-away\#mitigating-risk-through-open-source-and-efficiency) We believe that adopting Encore is a low-risk decision for several reasons: - There's no upfront investment needed to get the benefits - Encore apps are normal programs where less than 1% of the code is Encore-specific - All infrastructure and data is in your own cloud - It's simple to integrate with cloud services and systems not natively supported by Encore - Everything you need to develop your application is Open Source, including the [parser](https://github.com/encoredev/encore/tree/main/v2/parser), [compiler](https://github.com/encoredev/encore/tree/main/v2/compiler), [runtime](https://github.com/encoredev/encore/tree/main/runtimes) - Everything you need to self-host your application is [Open Source and documented](https://encore.dev/docs/go/self-host/docker-build) ## [What to expect when migrating away](https://encore.dev/docs/go/migration/migrate-away\#what-to-expect-when-migrating-away) If you want to migrate away, we want to ensure this is as smooth as possible! Here are some of the ways Encore is designed to keep your app portable, with minimized lock-in, and the tools provided to aid in migrating away. ### [Code changes](https://encore.dev/docs/go/migration/migrate-away\#code-changes) Building with Encore doesn't require writing your entire application in an Encore-specific way. Encore applications are normal programs where only 1% of the code is specific to Encore's Open Source Backend Framework. This means that the changes required to stop using the Backend Framework is almost exactly the same work you would have needed to do if you hadn't used Encore in the first place, e.g. writing infrastructure boilerplate. There is no added migration cost. ### [Deployment](https://encore.dev/docs/go/migration/migrate-away\#deployment) If you are self-hosting your application, then you're already done. If you are using Encore Cloud Platform to manage deployments and want to migrate to your own solution, you can use the `encore build docker` command to produce a Docker image, containing the compiled application, using exactly the same code path as Encore's CI system to ensure compatibility. Learn more in the [self-hosting docs](https://encore.dev/docs/go/self-host/docker-build). ### [Tell us what you need](https://encore.dev/docs/go/migration/migrate-away\#tell-us-what-you-need) We're engineers ourselves and we understand the importance of not being constrained by a single technology. We're working every single day on making it even easier to start, _and stop_, using Encore. If you have specific concerns, questions, or requirements, we'd love to hear from you! Please reach out on [Discord](https://encore.dev/discord) or [send an email](mailto:hello@encore.dev) with your thoughts. [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) [Get Involved](https://encore.dev/docs/go/community/get-involved) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/migration/migrate-away.md) ## Get Involved with Encore Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Developers building with Encore are forward-thinkers, who are working on exciting and innovative applications. We rely on this group's feedback, and contributions to the Open Source project, to improve Encore for developers everywhere. Getting involved is a fantastic way of finding support and inspiration among peers. Everyone is welcome in the Encore community, and we hope you to get involved too! ## [Get involved](https://encore.dev/docs/go/community/get-involved\#get-involved) There are many ways to get involved. Here's where you can start straight away. [Contribute on GitHub](https://github.com/encoredev/encore) Use GitHub to report bugs, feedback on proposals, or contribute your ideas. [Join Discord](https://encore.dev/discord) Connect with fellow Encore developers, ask questions, or just hang out! [Follow on Twitter](https://twitter.com/encoredotdev) Follow Encore on Twitter to keep up with the latest. Share what you've built to help spread the word about the project. ### [Contribute to the project](https://encore.dev/docs/go/community/get-involved\#contribute-to-the-project) Want to make a contribution to Encore? Great, start by reading about the different [ways to contribute](https://encore.dev/docs/go/community/contribute). ### [Feedback on the Roadmap](https://encore.dev/docs/go/community/get-involved\#feedback-on-the-roadmap) [The Encore Roadmap](https://encore.dev/roadmap) is public. It's open to your comments, feature requests, and you can vote on existing entries. ## [Community Governance](https://encore.dev/docs/go/community/get-involved\#community-governance) We recommend everyone read the [Community Principles](https://encore.dev/docs/go/community/principles). If you need assistance, have concerns, or have questions for the Community team, please email us at [support@encore.dev](mailto:support@encore.dev). [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) [Contribute](https://encore.dev/docs/go/community/contribute) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/community/get-involved.md) ## Contribute to Encore Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) We’re so excited that you are interested in contributing to Encore! All contributions are welcome, and there are several valuable ways to contribute. ### [Open Source Project](https://encore.dev/docs/go/community/contribute\#open-source-project) If you want to contribute to the Encore Open Source project, you can submit a pull request on [GitHub](https://github.com/encoredev/encore/pulls). ### [Report issues](https://encore.dev/docs/go/community/contribute\#report-issues) If you have run into an issue or think you’ve found a bug, please report it via the [issue tracker](https://github.com/encoredev/encore/issues). ### [Add or update docs](https://encore.dev/docs/go/community/contribute\#add-or-update-docs) If there’s something you think would be helpful to add to the docs or if there’s something that seems out of date, we appreciate your input. You can view the docs and contribute fixes or improvements directly in [GitHub](https://github.com/encoredev/encore/tree/main/docs). You can also email your feedback to us at [hello@encore.dev](mailto:hello@encore.dev). ### [Blog posts](https://encore.dev/docs/go/community/contribute\#blog-posts) If you’ve built something cool using Encore, we’d really like you to talk about it! We love it when developers share their projects on blogs and on Twitter. Use the hashtag **#builtwithencore** and we’ll have an easier time finding your work. – We might also showcase it on the [Encore Twitter account](https://twitter.com/encoredotdev)! ### [Meetups & Workshops](https://encore.dev/docs/go/community/contribute\#meetups--workshops) Organizing a meetup or workshop is a great way to connect with other developers using Encore. It can also be a great first step in trying out Encore for development in your company or other professional organization. If you want help with organizing or planning an event, please don’t hesitate to reach out to us via email at [hello@encore.dev](mailto:hello@encore.dev). [Get Involved](https://encore.dev/docs/go/community/get-involved) [Open Source](https://encore.dev/docs/go/community/open-source) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/community/contribute.md) ## Open Source Community Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) We believe Open Source is key to a long-term sustainable and prosperous technology community. Encore builds on Open Source software, and is largely Open Source itself. ## [License](https://encore.dev/docs/go/community/open-source\#license) Encore's Backend Framework, parser, and compiler are Open Source under Mozilla Public License 2.0. > The MPL is a simple copyleft license. The MPL's "file-level" copyleft is designed to encourage contributors to share modifications they make to your code, while still allowing them to combine your code with code under other licenses (open or proprietary) with minimal restrictions. You can learn more about MPL 2.0 onΒ [the official website](https://www.mozilla.org/en-US/MPL/2.0/FAQ/). ## [Contribute](https://encore.dev/docs/go/community/open-source\#contribute) Contributions to improve Encore are very welcome. Contribute to Encore onΒ [GitHub](https://github.com/encoredev/encore). [Contribute](https://encore.dev/docs/go/community/contribute) [Principles](https://encore.dev/docs/go/community/principles) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/community/open-source.md) ## Encore Community Principles Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) Everyone is welcome in the Encore community, and it is of utmost importance to us that everyone is able to feel at home and contribute. Therefore we as maintainers, and you as a contributor, must pledge to make participation in our community a harassment-free experience for everyone, regardless of: age, body size, disability, ethnicity, gender identity, level of experience, nationality, personal appearance, race, religion, or sexual identity. ### [Code of Conduct](https://encore.dev/docs/go/community/principles\#code-of-conduct) To this end, the Encore community is guided by the [Contributor Covenant 2.0 Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/) to ensure everyone is welcome and able to participate. [Open Source](https://encore.dev/docs/go/community/open-source) [Submit Template](https://encore.dev/docs/go/community/submit-template) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/community/principles.md) ## Submit Encore Templates Get Started - [Installation](https://encore.dev/docs/go/install) - [Quick Start](https://encore.dev/docs/go/quick-start) - [AI Integration](https://encore.dev/docs/go/ai-integration) - [FAQ](https://encore.dev/docs/go/faq) Concepts - [Benefits](https://encore.dev/docs/go/concepts/benefits) - [Application Model](https://encore.dev/docs/go/concepts/application-model) Tutorials - [Building a REST API](https://encore.dev/docs/go/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/go/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/go/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/go/tutorials/slack-bot) - [Building a Meeting Notes app](https://encore.dev/docs/go/tutorials/meeting-notes) - [Building a Booking System](https://encore.dev/docs/go/tutorials/booking-system) Primitives - [App Structure](https://encore.dev/docs/go/primitives/app-structure) - [Services](https://encore.dev/docs/go/primitives/services) - APIs - Databases - [Object Storage](https://encore.dev/docs/go/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/go/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/go/primitives/pubsub) - [Caching](https://encore.dev/docs/go/primitives/caching) - [Secrets](https://encore.dev/docs/go/primitives/secrets) - [Code Snippets](https://encore.dev/docs/go/primitives/code-snippets) Development - [Authentication](https://encore.dev/docs/go/develop/auth) - [Configuration](https://encore.dev/docs/go/develop/config) - [CORS](https://encore.dev/docs/go/develop/cors) - [Metadata](https://encore.dev/docs/go/develop/metadata) - [Middleware](https://encore.dev/docs/go/develop/middleware) - [Testing](https://encore.dev/docs/go/develop/testing) - [Mocking](https://encore.dev/docs/go/develop/testing/mocking) - [Validation](https://encore.dev/docs/go/develop/validation) - [Environment Variables](https://encore.dev/docs/go/develop/env-vars) CLI - [CLI Reference](https://encore.dev/docs/go/cli/cli-reference) - [Client Generation](https://encore.dev/docs/go/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/go/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/go/cli/config-reference) - [Telemetry](https://encore.dev/docs/go/cli/telemetry) - [MCP](https://encore.dev/docs/go/cli/mcp) Observability - [Development Dashboard](https://encore.dev/docs/go/observability/dev-dash) - [Distributed Tracing](https://encore.dev/docs/go/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/go/observability/encore-flow) - [Service Catalog](https://encore.dev/docs/go/observability/service-catalog) - [Logging](https://encore.dev/docs/go/observability/logging) - [Metrics](https://encore.dev/docs/go/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/go/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/go/self-host/docker-build) - [Configure Infrastructure](https://encore.dev/docs/go/self-host/configure-infra) How to guides - [Break a monolith into microservices](https://encore.dev/docs/go/how-to/break-up-monolith) - [Integrate with a web frontend](https://encore.dev/docs/go/how-to/integrate-frontend) - [Use Temporal with Encore](https://encore.dev/docs/go/how-to/temporal) - [Build with cgo](https://encore.dev/docs/go/how-to/cgo) - [Debug with Delve](https://encore.dev/docs/go/how-to/debug) - [Receive regular HTTP requests & Use websockets](https://encore.dev/docs/go/how-to/http-requests) - [Use Atlas + GORM for database migrations](https://encore.dev/docs/go/how-to/atlas-gorm) - [Use the ent ORM for migrations](https://encore.dev/docs/go/how-to/entgo-orm) - [Use Connect for gRPC communication](https://encore.dev/docs/go/how-to/grpc-connect) - [Use a Pub/Sub Transactional Outbox](https://encore.dev/docs/go/how-to/pubsub-outbox) - [Use Dependency Injection](https://encore.dev/docs/go/how-to/dependency-injection) - [Use Auth0 Authentication](https://encore.dev/docs/go/how-to/auth0-auth) - [Use Clerk Authentication](https://encore.dev/docs/go/how-to/clerk-auth) - [Use Firebase Authentication](https://encore.dev/docs/go/how-to/firebase-auth) - [Use Logto Authentication](https://encore.dev/docs/go/how-to/logto-auth) Migration guides - [Migrate away from Encore](https://encore.dev/docs/go/migration/migrate-away) Community - [Get Involved](https://encore.dev/docs/go/community/get-involved) - [Contribute](https://encore.dev/docs/go/community/contribute) - [Open Source](https://encore.dev/docs/go/community/open-source) - [Principles](https://encore.dev/docs/go/community/principles) - [Submit Template](https://encore.dev/docs/go/community/submit-template) [Templates](https://encore.dev/templates) help and inspire developers to build applications using Encore. You are welcome to contribute your own templates! Two types of templates that are especially useful: - **Starters:** Runnable Encore applications for others to use as is, or take inspiration from. - **Bits:** Re-usable code samples to solve common development patterns or integrate Encore applications with third-party APIs and services. ## [Submit your contribution](https://encore.dev/docs/go/community/submit-template\#submit-your-contribution) Contribute a template by submitting a Pull Request to the [Open Source Examples Repo](https://github.com/encoredev/examples): `https://github.com/encoredev/examples` ### [Submitting Starters](https://encore.dev/docs/go/community/submit-template\#submitting-starters) Follow these steps to submit a **Starter**: 1. Fork the repo. 2. Create a new folder in the root directory of the repo, this is where you will place your template. β€” Use a short folder name as your template will be installable via the CLI, like so: `encore app create APP-NAME --example=` 3. Include a `README.md` with instructions for how to use the template. We recommend following [this format](https://github.com/encoredev/examples/blob/8c7e33243f6bfb1b2654839e996e9a924dcd309e/uptime/README.md). Once your Pull Request has been approved, it may be featured on the [Templates page](https://encore.dev/templates) on the Encore website. ### [Submitting Bits](https://encore.dev/docs/go/community/submit-template\#submitting-bits) Follow these steps to submit your **Bits**: 1. Fork the repo. 2. Create a new folder inside the `bits` folder in the repo and place your template inside it. Use a short folder name as your template will soon be installable via the CLI. 3. Include a `README.md` with instructions for how to use the template. Once your Pull Request has been approved, it may be featured on the [Templates page](https://encore.dev/templates) on the Encore website. ## [Contribute from your own repo](https://encore.dev/docs/go/community/submit-template\#contribute-from-your-own-repo) If you don't want to contribute code to the examples repo, but still want to be featured on the [Templates page](https://encore.dev/templates), please contact us at [hello@encore.dev](mailto:hello@encore.dev). ## [Dynamic Encore AppID](https://encore.dev/docs/go/community/submit-template\#dynamic-encore-appid) In most cases, you should avoid hardcoding an `AppID` in your template's source code. Instead, use the notation `{{ENCORE_APP_ID}}`. When a developer creates an app using the template, `{{ENCORE_APP_ID}}` will be dymically replaced with their new and unique `AppID`, meaning they will not need to make any manual code adjustments. [Principles](https://encore.dev/docs/go/community/principles) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/go/community/submit-template.md) ## Encore CLI Installation Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) If you are new to Encore, we recommend following the [quick start guide](https://encore.dev/docs/ts/quick-start). ## [Install the Encore CLI](https://encore.dev/docs/ts/install\#install-the-encore-cli) To develop locally with Encore, you first need to install the Encore CLI. This is what provisions your local development environment, and runs your Local Development Dashboard complete with logs, tracing, and API documentation. macOS Windows Linux Brew `$ curl -L https://encore.dev/install.sh | bash` ### [Prerequisites](https://encore.dev/docs/ts/install\#prerequisites) - [Node.js](https://nodejs.org/en/download/) is required to run Encore.ts apps. - [Docker](https://www.docker.com/) is required for Encore to set up local databases. ### [Optional: Add AI/LLM instructions](https://encore.dev/docs/ts/install\#optional-add-aillm-instructions) To help AI coding assistants (Cursor, Claude Code, GitHub Copilot, etc.) understand how to use Encore, run this from your app directory: ```bash encore llm-rules init ``` This prompts you to select your tool and generates the appropriate config (e.g. `.cursorrules`, `CLAUDE.md`) and MCP setup where supported. For full details and other options, see [AI Tools Integration](https://encore.dev/docs/ts/ai-integration). ### [Build from source](https://encore.dev/docs/ts/install\#build-from-source) If you prefer to build from source, [follow these instructions](https://github.com/encoredev/encore/blob/main/CONTRIBUTING.md). ## [Update to the latest version](https://encore.dev/docs/ts/install\#update-to-the-latest-version) Check which version of Encore you have installed by running `encore version` in your terminal. It should print something like: ```bash encore version v1.28.0 ``` If you think you're on an older version of Encore, you can easily update to the latest version by running `encore version update` from your terminal. [Quick Start](https://encore.dev/docs/ts/quick-start) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/install.md) ## Encore Quick Start Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Follow the steps below or use [Leap](https://leap.new/) (our AI builder) to get started. ### Try Encore.ts with Leap Let Leap generate a complete application that demonstrates Encore's capabilities. Generate Examples: database pub/sub storage In this short guide, you'll learn key concepts and experience the Encore workflow. It should only take about 5 minutes to complete and by the end you'll have an API running in Encore's free development Cloud (Encore Cloud). To make it easy to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Install the Encore CLI](https://encore.dev/docs/ts/quick-start\#1-install-the-encore-cli) To develop with Encore, you need the Encore CLI. It provisions your local environment, and runs your local development dashboard complete with tracing and API documentation. πŸ₯ Install Encore by running the appropriate command for your system: macOS Windows Linux Brew `$ curl -L https://encore.dev/install.sh | bash` ### [Prerequisites](https://encore.dev/docs/ts/quick-start\#prerequisites) - [Node.js](https://nodejs.org/en/download/) is required to run Encore.ts apps. - [Docker](https://www.docker.com/) is required for Encore to set up local databases. ## [2\. Create your app](https://encore.dev/docs/ts/quick-start\#2-create-your-app) πŸ₯ Create your app by running: ```bash $ encore app create ``` If this is your first time using Encore, you’ll be prompted to create a free Encore Cloud account. This enables Encore to manage things like secrets and fully automate cloud deployments (which you’ll use later in the tutorial). πŸ₯ Select `TypeScript` as your app’s language. πŸ₯ Choose a starter template. Pick `Hello World` and continue. Optional: Install AI instructions to improve how tools like Cursor and Claude Code work with Encore. After selecting your template, choose the AI instructions for the tool you plan to use. πŸ₯ Pick a name for your app. Encore will now create your app in a folder named after your app. ### [Let's take a look at the code](https://encore.dev/docs/ts/quick-start\#lets-take-a-look-at-the-code) Part of what makes Encore different is the simple developer experience when building distributed systems. Let's look at the code to better understand how to build applications with Encore. πŸ₯ Open the `hello.ts` file in your code editor. It's located in the folder: `your-app-name/hello/`. You should see this: ```ts hello/hello.ts import { api } from "encore.dev/api"; export const world = api( { method: "GET", path: "/hello/:name", expose: true }, async ({ name }: { name: string }): Promise => { return { message: `Hello ${name}!` }; } ); interface Response { message: string; } ``` As you can see, it's all standard TypeScript. You define an API endpoint by wrapping a regular async function in a call to `api`. Doing this makes Encore identify the `world` function as a public API endpoint. Encore automatically handles authentication, HTTP routing, request validation, error handling, observability, API documentation, and more. The `world` endpoint is part of the `hello` service because in the same folder you will also find a file named `encore.service.ts` which looks like this: ```ts hello/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("hello"); ``` This is how you define services with Encore. Encore will now consider files in the `hello` directory and all its subdirectories as part of the `hello` service. If you want to create more services, simply create a new folders, add a `encore.service.ts` file that is exporting a new `Service`. _If you're curious, you can read more about defining [services](https://encore.dev/docs/ts/primitives/services) and [APIs](https://encore.dev/docs/ts/primitives/apis)._ The Encore.ts [Backend Framework](https://encore.dev/docs/ts) provides several declarative ways of using backend primitives like databases, Pub/Sub, and scheduled tasks by simply writing code. ## [3\. Start your app & Explore Local Development Dashboard](https://encore.dev/docs/ts/quick-start\#3-start-your-app--explore-local-development-dashboard) πŸ₯ Now let's run your app locally: ```bash $ cd your-app-name # replace with the app name you picked $ encore run ``` You should see this: That means your local development environment is up and running! Encore takes care of setting up all the necessary infrastructure for your applications, even including databases and Pub/Sub. ### [Open the Local Development Dashboard](https://encore.dev/docs/ts/quick-start\#open-the-local-development-dashboard) You can now start using your [Local Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash). πŸ₯ Open [http://localhost:9400](http://localhost:9400/) in your browser to access it. The Local Development Dashboard is a powerful tool to help you move faster when you're developing new features. It comes with an API explorer, a Service Catalog with automatically generated documentation, and powerful observability features like [distributed tracing](https://encore.dev/docs/ts/observability/tracing). Through the Local Development Dashboard you also have access to [Encore Flow](https://encore.dev/docs/ts/observability/encore-flow), a visual representation of your microservice architecture that updates in real-time as you develop your application. ### [Call your API](https://encore.dev/docs/ts/quick-start\#call-your-api) πŸ₯ While you keep the app running, call your API from the API Explorer: ![](https://encore.dev/assets/docs/qs_call.png) You can also open a separate terminal to call your API endpoint: ```bash $ curl http://localhost:4000/hello/world {"Message": "Hello, world!"} ``` If you see this JSON response, you've successfully made an API call to your very first Encore application. Well done, you're on your way! ### [Review a trace of the request](https://encore.dev/docs/ts/quick-start\#review-a-trace-of-the-request) You can now take a look at the trace for the request you just made by clicking on it in the right column in the local dashboard. ![](https://encore.dev/assets/docs/qs_trace.png) With such a simple API, there's not much to it, just a simple request and response. However, just imagine how powerful it is to have tracing when you're developing a more complex system with multiple services, Pub/Sub, and databases. (Learn more about Encore's tracing capabilities in the [tracing docs](https://encore.dev/docs/ts/observability/tracing).) ## [4\. Make a code change](https://encore.dev/docs/ts/quick-start\#4-make-a-code-change) Let's put our mark on this API and make our first code change. πŸ₯ Head back to your code editor and look at the `hello.ts` file again. If you can't come up a creative change yourself, why not simply change the "Hello" message to a more sassy "Howdy"? πŸ₯ Once you've made your change, save the file. When you save, the daemon run by the Encore CLI instantly detects the change and automatically recompiles your application and reloads your local development environment. The output where you're running your app will look something like this: ```output Changes detected, recompiling... Reloaded successfully. TRC registered endpoint endpoint=World path=/hello/:name service=hello TRC listening for incoming HTTP requests ``` πŸ₯ Test your change by calling your API again. ```bash $ curl http://localhost:4000/hello/world {"Message": "Howdy, world!"} ``` Great job, you made a change and your app was reloaded automatically. Now you're ready to head to the cloud! ## [5\. Deploy your app](https://encore.dev/docs/ts/quick-start\#5-deploy-your-app) ### [Generating Docker image](https://encore.dev/docs/ts/quick-start\#generating-docker-image) You can either deploy by generating a Docker image for your app using: ```bash $ encore build docker MY-IMAGE:TAG ``` This will compile your application using the host machine and then produce a Docker image containing the compiled application. You can now deploy this anywhere you like. Learn more in the [self-host docs](https://encore.dev/docs/ts/self-host/build). ### [Deploy using Encore Cloud](https://encore.dev/docs/ts/quick-start\#deploy-using-encore-cloud) Optionally, you can use [Encore Cloud](https://encore.dev/use-cases/devops-automation) to automatically deploy your application. It comes with built-in free development hosting, and for production offers fully automated deployment to your own cloud on AWS or GCP. πŸ₯ To deploy, simply push your changes to Encore: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore Cloud will now build and test your app, provision the needed infrastructure, and deploy your application to a staging environment. After triggering the deployment, you will see a URL where you can view its progress in the Encore Cloud dashboard. It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` πŸ₯ Open the URL to access the Cloud Dashboard and check the progress of your deployment. You can now use the Cloud Dashboard to view production [traces](https://encore.dev/docs/ts/observability/tracing), [connect your cloud account](https://encore.dev/docs/platform/deploy/own-cloud), [integrate with GitHub](https://encore.dev/docs/platform/integrations/github), and much more. ## [What's next?](https://encore.dev/docs/ts/quick-start\#whats-next) - Check out the [REST API tutorial](https://encore.dev/docs/ts/tutorials/rest-api) to learn how to create endpoints, use databases, and more. - Join the friendly community on [Discord](https://encore.dev/discord) to ask questions and meet other Encore developers. [Installation](https://encore.dev/docs/ts/install) [AI Integration](https://encore.dev/docs/ts/ai-integration) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/quick-start.mdx) ## AI Integration Overview Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore is built for AI-assisted development. Encore-specific rules and [MCP](https://encore.dev/docs/ts/ai-integration#mcp-server) integration let AI understand your architecture and generate type-safe code that follows your patterns. Run `encore run` to start your app; Encore provisions local infrastructure automatically. For production, [self-host](https://encore.dev/docs/ts/self-host/build) or use [Encore Cloud](https://encore.cloud/) to provision infrastructure in your own AWS or GCP account. ## [What AI Enables](https://encore.dev/docs/ts/ai-integration\#what-ai-enables) Encore's declarative APIs and infrastructure primitives give AI a clear model to work with. AI can add databases, pub/sub topics, and other resources with built-in guardrails, and use MCP to introspect your appβ€”services, APIs, databases, and tracesβ€”so it can suggest accurate, pattern-consistent code. ## [Enabling AI for Your Project](https://encore.dev/docs/ts/ai-integration\#enabling-ai-for-your-project) There are two ways to set up AI support: - [Method 1: Using the CLI](https://encore.dev/docs/ts/ai-integration#method-1-using-the-cli) (recommended) - [Method 2: Using Encore Skills](https://encore.dev/docs/ts/ai-integration#method-2-using-encore-skills) ### [Method 1: Using the CLI](https://encore.dev/docs/ts/ai-integration\#method-1-using-the-cli) **New projects:** When you run `encore app create`, you'll be prompted to select an AI tool. Encore generates the appropriate configuration files for your chosen tool. ![](https://encore.dev/assets/docs/initllm.png) **Existing projects:** Run `encore llm-rules init` to add AI support: ```bash encore llm-rules init ``` This prompts you to select a tool and generates the appropriate configuration file (`.cursorrules`, `CLAUDE.md`, etc.). Both commands also set up MCP server configuration for tools that support it (Cursor, Claude Code). If you want to set up MCP manually, see [MCP Server](https://encore.dev/docs/ts/ai-integration#mcp-server) below. Supported tools: Cursor, Claude Code, VS Code, AGENTS.md, and Zed. ### [Method 2: Using Encore Skills](https://encore.dev/docs/ts/ai-integration\#method-2-using-encore-skills) Use the [Encore skills package](https://github.com/encoredev/skills) which works with Cursor, Claude Code, GitHub Copilot, and 10+ other AI agents: ```bash npx add-skill encoredev/skills ``` You can also install specific skills or target specific agents: ```bash # List available skills npx add-skill encoredev/skills --list # Install to specific agents npx add-skill encoredev/skills -a cursor -a claude-code ``` ## [MCP Server](https://encore.dev/docs/ts/ai-integration\#mcp-server) Encore's [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server gives AI agents deep introspection into your application: querying databases, calling APIs, inspecting services, and analyzing traces. ### [Start the Server](https://encore.dev/docs/ts/ai-integration\#start-the-server) From your Encore app directory: ```bash encore mcp start ``` This displays connection information. Keep it running while using your AI tools. ### [Connect Cursor](https://encore.dev/docs/ts/ai-integration\#connect-cursor) **Quick setup:** Use this button (update `your-app-id` to your actual app ID): [![Add encore-mcp MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=encore-mcp&config=eyJjb21tYW5kIjoiZW5jb3JlIG1jcCBydW4gLS1hcHA9eW91ci1hcHAtaWQifQ%3D%3D) **Manual setup:** Create `.cursor/mcp.json`: ```json { "mcpServers": { "encore-mcp": { "command": "encore", "args": ["mcp", "run", "--app=your-app-id"] } } } ``` Find your app ID with `encore app info` or in the [Encore dashboard](https://app.encore.dev/). ### [Connect Claude Code](https://encore.dev/docs/ts/ai-integration\#connect-claude-code) From your Encore app directory: ```bash claude mcp add --transport stdio encore-mcp -- encore mcp run --app=your-app-id ``` Verify with `claude mcp list`. You should see `encore-mcp` in the list. ## [What AI Can Do](https://encore.dev/docs/ts/ai-integration\#what-ai-can-do) With Encore skills and MCP connected, AI can: - **Define infrastructure in code** \- AI declares databases, pub/sub, cron jobs, buckets, and other [primitives](https://encore.dev/docs/ts/primitives) - **Generate type-safe APIs** \- code that follows your patterns and passes validation - **Understand architecture** \- inspect services and how they connect via MCP - **Query databases** \- introspect schema and data to generate accurate queries - **Debug with tracing** \- view request traces, timing, and span details to pinpoint issues - **Test instantly** \- run `encore run` to test with real infrastructure, not mocks ### [In Practice](https://encore.dev/docs/ts/ai-integration\#in-practice) #### [Smarter Debugging with Tracing](https://encore.dev/docs/ts/ai-integration\#smarter-debugging-with-tracing) AI can access Encore's distributed tracing via MCP to debug issues intelligently. Instead of guessing, AI can view actual request traces, analyze timing across services, and inspect span details to pinpoint exactly where things went wrong. This creates a powerful feedback loop: generate code, test it, analyze the traces, and iterate. #### [Database Introspection](https://encore.dev/docs/ts/ai-integration\#database-introspection) AI can query your actual database schema and data via MCP. This means AI understands your real data model and can generate accurate queries, suggest schema changes, and debug data issues by inspecting actual records. #### [Instant Validation with Real Infrastructure](https://encore.dev/docs/ts/ai-integration\#instant-validation-with-real-infrastructure) When you run `encore run`, Encore provisions real local infrastructure (databases, pub/sub, etc.). AI can generate code and immediately test it against real services, catching issues early and ensuring the code works before you deploy. Example prompts: - "Add an endpoint that publishes to a pub/sub topic, call it and verify in traces" - "Query the users database and show accounts created in the last week" - "Create a new service with CRUD endpoints connected to PostgreSQL" ## [Learn More](https://encore.dev/docs/ts/ai-integration\#learn-more) - [MCP Server Documentation](https://encore.dev/docs/ts/cli/mcp) \- Complete MCP reference - [Encore Skills Repository](https://github.com/encoredev/skills) \- Available skills and installation - [Quick Start Guide](https://encore.dev/docs/ts/quick-start) \- Build your first Encore app [Quick Start](https://encore.dev/docs/ts/quick-start) [FAQ](https://encore.dev/docs/ts/faq) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/ai-integration.md) ## Encore FAQ Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) ## [About the project](https://encore.dev/docs/ts/faq\#about-the-project) **Is Encore Open Source?** Yes, check out the project on [GitHub](https://github.com/encoredev/encore). **Is there a community?** Yes, you're welcome to join the developer community on [Discord](https://encore.dev/discord). ## [Can I use X with Encore?](https://encore.dev/docs/ts/faq\#can-i-use-x-with-encore) **Can I use Python with Encore?** Encore currently supports Go and TypeScript. Python support in on the [roadmap](https://encore.dev/roadmap) and will be available in 2026. **Can mix TypeScript and Go in one application?** Support for mixing languages in coming. Currently, if you want to use both TypeScript and Go, you need to create a separate application per language and integrate using APIs. **Can I use Azure / Digital Ocean?** Encore Cloud currently supports automating deployments to AWS and GCP. Azure support in on the [roadmap](https://encore.dev/roadmap) and will be available in 2026. If you want to use other cloud providers like Azure or Digital Ocean, you can follow the [self-hosting instructions](https://encore.dev/docs/how-to/self-host). **Can I use MongoDB / MySQL with Encore?** Encore currently has built-in support for PostgreSQL. To use another type of database, like MongoDB and MySQL, you will need to set it up and integrate as you normally would when not using Encore. **Can I use AWS lambda with Encore?** Not right now. Encore currently supports AWS Fargate and EKS (along with CloudRun and GKE on Google Cloud Platform). **Can I use Bun / Deno with Encore.ts?** Right now Encore.ts officially supports Node and has experimental support for Bun. Deno support is on the way. Note that Encore.ts already provides performance improvements thanks to its Rust-based runtime. [Learn more](https://encore.dev/blog/event-loops). To enable the Bun experiment, add `"experiments": ["bun-runtime"]` to your `encore.app` file. ## [IDE Integrations](https://encore.dev/docs/ts/faq\#ide-integrations) **Is there an Encore plugin for Goland / IntelliJ?** Yes, Encore's official Goland plugin is available in the [JetBrains marketplace](https://plugins.jetbrains.com/plugin/20010-encore). **Is there an Encore plugin for VS Code?** Not yet, it's coming soon. ## [Troubleshooting](https://encore.dev/docs/ts/faq\#troubleshooting) **symlink creation error on Windows** Encore currently relies on symbolic links, which may be disabled by default. A common fix for this issue is to enable "developer mode" in the Windows settings (Settings > System > For developers > Developer mode). **`node` errors** You might need to restart the Encore daemon, e.g. if your PATH has changed since installing nvm. Restart the daemon by running `encore daemon`. [AI Integration](https://encore.dev/docs/ts/ai-integration) [Benefits](https://encore.dev/docs/ts/concepts/benefits) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/faq.md) ## Encore Benefits Overview Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) ## [Integrated developer experience for enhanced productivity](https://encore.dev/docs/ts/concepts/benefits\#integrated-developer-experience-for-enhanced-productivity) - **Local development with instant infrastructure**: Encore automatically sets up necessary infrastructure as you develop. - **Rapid feedback**: Catch issues early with type-safe infrastructure, avoiding slow deployment cycles. - **No manual configuration required**: No need for Infrastructure-as-Code. Your code is the single source of truth. - **Unified codebase**: One codebase for all environments; local, preview, and cloud. - **Cloud-agnostic by default**: Encore.ts provides an abstraction layer on top of the cloud provider's APIs, so you avoid becoming locked in to a single cloud. - **Evolve infrastructure without code changes**: As requirements evolve, you can change the provisioned infrastructure without needing application code changes. Either using the Open Source [self-hosting tools](https://encore.dev/docs/ts/self-host/build) or with the optional [Cloud Platform](https://encore.dev/use-cases/devops-automation), which fully-automates infrastructure management in your own AWS/GCP account. - **AI-assisted development**: Encore is built for AI coding assistants. With [Encore-specific rules and MCP integration](https://encore.dev/docs/ts/ai-integration), AI understands your architecture and can generate type-safe, pattern-consistent code and introspect your appβ€”services, APIs, databases, and traces. ## [High-performance Rust runtime](https://encore.dev/docs/ts/concepts/benefits\#high-performance-rust-runtime) To enable Encore's functionality in TypeScript, we’ve created a high-performance distributed systems runtime in Rust. It integrates with the standard Node.js runtime for executing JavaScript code, ensuring **100% compatibility with the Node.js ecosystem**. It provides a number of benefits over standard Node.js: - **Handles requests validation, provides API type-safety, has built-in observability, and integrates with databases, Pub/Sub, and more** - **9x increased throughput and 85% reduced latency** compared to standard Node.js/Express.js [See benchmarks](https://encore.dev/blog/event-loops) - **Zero NPM dependencies** for improved security and faster builds ### [How it works](https://encore.dev/docs/ts/concepts/benefits\#how-it-works) Encore.ts is designed to let the Node.js event loop β€” which is single-threaded β€” focus on executing your business logic, while everything else happens in Encore’s multi-threaded Rust runtime. Here's a high-level overview of how this works: **1\. Node.js starts up and initializes the Encore Rust runtime. The Rust runtime then:** - Begins accepting incoming requests - Parses and validates these requests against the API schema **2\. For each request, the Encore Runtime:** - Passes the request to your application code - Waits for your code to process the request - Sends the response back to the client **3\. When your application needs to interact with infrastructure (like databases or PubSub):** - It delegates these tasks to the Rust runtime - The Rust runtime handles these operations more efficiently than Node.js would, providing faster execution and lower latency ## [Enhanced type-safety for distributed systems](https://encore.dev/docs/ts/concepts/benefits\#enhanced-type-safety-for-distributed-systems) Encore leverages static code analysis to parse the API schema and TypeScript types you define. This enables a number of features: - Built-in [local development dashboard](https://encore.dev/docs/ts/observability/dev-dash) - API Explorer, automatic documentation, and local tracing - Runtime type-safety, automatically validating incoming requests against the API schema - Eliminating runtime errors due to missing required fields ## [No DevOps experience required](https://encore.dev/docs/ts/concepts/benefits\#no-devops-experience-required) Encore provides open source tools to help you integrate with your cloud infrastructure, enabling you to self-host your application anywhere that supports Docker containers. Learn more in the [self-host documentation](https://encore.dev/docs/ts/self-host/build). You can also use [Encore Cloud](https://encore.dev/use-cases/devops-automation), which fully automates provisioning and managing infrastructure in your own cloud on AWS and GCP. This approach dramatically reduces the level of DevOps expertise required to use scalable, production-ready, cloud services like Kubernetes and Pub/Sub. And because your application code is the source of truth for infrastructure requirements, it ensures the infrastructure in all environments is always in sync with the application's current requirements. ## [Simplicity without giving up flexibility](https://encore.dev/docs/ts/concepts/benefits\#simplicity-without-giving-up-flexibility) Encore.ts provides integrations for common infrastructure primitives, but also allows for flexibility. For example, you can always use any cloud infrastructure, even if it's not built into the Encore.ts framework. You can use any database, message broker, or other service that your application needs, just set up the infrastructure and then reference it in your code as you would do traditionally. If you use [Encore Cloud](https://encore.dev/use-cases/devops-automation), it will [automate infrastructure](https://encore.dev/docs/platform/infrastructure/infra) using your own cloud account, so you always have full access to your services from the cloud provider's console. [FAQ](https://encore.dev/docs/ts/faq) [Application Model](https://encore.dev/docs/ts/concepts/application-model) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/concepts/benefits.md) ## Encore Application Model Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore works by using static analysis to understand your application. This is a fancy term for parsing and analyzing the code you write and creating a graph of how your application works. This graph closely represents your own mental model of the system: boxes and arrows that represent systems and services that communicate with other systems, pass data and connect to infrastructure. We call it the Encore Application Model. Because the Open Source framework, parser, and compiler, are all designed together, Encore can ensure 100% accuracy when creating the application model. Any deviation is caught as a compilation error. Using this model, Encore can provide tools to solve problems that normally would be up to the developer to do manually. From creating architecture diagrams and API documentation to provisioning cloud infrastructure. We're continuously expanding on Encore's capabilities and are building a new generation of developer tools that are enabled by Encore's understanding of your application. The framework, parser, and compiler that enable this are all [Open Source](https://github.com/encoredev/encore). ![](https://encore.dev/assets/docs/flow-diagram.png) ## [Standardization brings clarity](https://encore.dev/docs/ts/concepts/application-model\#standardization-brings-clarity) Developers make dozens of decisions when creating a backend application. Deciding how to structure the codebase, defining API schemas, picking underlying infrastructure, etc. The decisions often come down to personal preferences, not technical rationale. This creates a huge problem in the form of fragmentation! When every stack looks different, all tools have to be general purpose. When you adopt Encore, many of these stylistic decisions are already made for you. The Encore framework ensures your application follows modern best practices. And when you run your application, Encore's Open Source parser and compiler check that you're sticking to the standard. This means you're free to focus your energy on what matters: writing your application's business logic. [Benefits](https://encore.dev/docs/ts/concepts/benefits) [Hello World](https://encore.dev/docs/ts/concepts/hello-world) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/concepts/application-model.md) ## TypeScript API Guide Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore lets you easily define type-safe, idiomatic TypeScript API endpoints. It's done in a fully declarative way, enabling Encore to automatically parse and validate the incoming request and ensure it matches the schema, with zero boilerplate. To define an API, use the `api` function from the `encore.dev/api` module to wrap a regular TypeScript async function that receives the request data as input and returns response data. This tells Encore that the function is an API endpoint. Encore will then automatically generate the necessary boilerplate at compile-time. This means you need less than 10 lines of code to define a production-ready deployable service and API endpoint: ```typescript import { api } from "encore.dev/api"; export const get = api( { expose: true, method: "GET", path: "/hello/:name" }, async ({ name }: { name: string }): Promise => { const msg = `Hello ${name}!`; return { message: msg }; } ); interface Response { message: string; } ``` ##### Related example [encoredev/examples/hello-world](https://github.com/encoredev/examples/tree/main/ts/hello-world) Hello World REST API example application. `$ encore app create --example=ts/hello-world` ## [Getting started video](https://encore.dev/docs/ts/concepts/hello-world\#getting-started-video) Get to know the basics of Encore.ts in this getting started video. Getting started with Encore for TypeScript - YouTube [Photo image of Encore](https://www.youtube.com/channel/UCvqeAqMPotfuA6SPXa4VhNQ?embeds_referring_euri=https%3A%2F%2Fencore.dev%2Fdocs%2Fts%2Fconcepts%2Fhello-world) Encore 5.16K subscribers [Getting started with Encore for TypeScript](https://www.youtube.com/watch?v=wiLDz-JUuqY) Encore Search Info Shopping Tap to unmute If playback doesn't begin shortly, try restarting your device. Full screen is unavailable. [Learn More](https://support.google.com/youtube/answer/6276924) You're signed out Videos you watch may be added to the TV's watch history and influence TV recommendations. To avoid this, cancel and sign in to YouTube on your computer. CancelConfirm Share Include playlist An error occurred while retrieving sharing information. Please try again later. Watch later Share Copy link Watch on 0:00 0:00 / 6:24 β€’Live β€’ ## [Using databases, Pub/Sub, and other primitives](https://encore.dev/docs/ts/concepts/hello-world\#using-databases-pubsub-and-other-primitives) Encore's Backend Framework makes it simple to add more primitives, such as additional microservices, databases, Pub/Sub, etc. See how to use each primitive: - [Services](https://encore.dev/docs/ts/primitives/services) - [APIs](https://encore.dev/docs/ts/primitives/defining-apis) - [Databases](https://encore.dev/docs/ts/primitives/databases) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub & Queues](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) [Application Model](https://encore.dev/docs/ts/concepts/application-model) [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/concepts/hello-world.md) ## REST API Tutorial Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) In this tutorial you will create a REST API for a URL Shortener service. In a few short minutes, you'll learn how to: - Create REST APIs with Encore - Use PostgreSQL databases - Use the local development dashboard to test your app - Create and run tests This is the end result: ```ts 1 import { api, APIError } from "encore.dev/api"; 2 import { SQLDatabase } from "encore.dev/storage/sqldb"; 3 import { randomBytes } from "node:crypto"; 4 5 // 'url' database is used to store the URLs that are being shortened. 6 const db = new SQLDatabase("url", { migrations: "./migrations" }); 7 8 interface URL { 9 id: string; // short-form URL id 10 url: string; // complete URL, in long form 11 } 12 13 interface ShortenParams { 14 url: string; // the URL to shorten 15 } 16 17 // shorten shortens a URL. 18 export const shorten = api( 19 { expose: true, auth: false, method: "POST", path: "/url" }, 20 async ({ url }: ShortenParams): Promise => { 21 const id = randomBytes(6).toString("base64url"); 22 await db.exec` 23 INSERT INTO url (id, original_url) 24 VALUES (${id}, ${url}) 25 `; 26 return { id, url }; 27 } 28 ); 29 30 // Get retrieves the original URL for the id. 31 export const get = api( 32 { expose: true, auth: false, method: "GET", path: "/url/:id" }, 33 async ({ id }: { id: string }): Promise => { 34 const row = await db.queryRow` 35 SELECT original_url FROM url WHERE id = ${id} 36 `; 37 if (!row) throw APIError.notFound("url not found"); 38 return { id, url: row.original_url }; 39 } 40 ); 41 42 interface ListResponse { 43 urls: URL[]; 44 } 45 46 // List retrieves all URLs. 47 export const list = api( 48 { expose: false, method: "GET", path: "/url" }, 49 async (): Promise => { 50 const rows = db.query` 51 SELECT id, original_url 52 FROM url 53 `; 54 const urls: URL[] = []; 55 for await (const row of rows) { 56 urls.push({ id: row.id, url: row.original_url }); 57 } 58 return { urls }; 59 } 60 ); 61 ``` Project url migrations 1\_create\_tables.up.sql encore.service.ts url.test.ts url.ts Encore encore.app package.json tsconfig.json vite.config.ts Open on ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Create a service and endpoint](https://encore.dev/docs/ts/tutorials/rest-api\#1-create-a-service-and-endpoint) Create a new application by running `encore app create` and select `Empty app` as the template. If this is the first time you're using Encore, you'll be asked if you wish to create a free account. This is needed when you want Encore to manage functionality like secrets and handle cloud deployments (which we'll use later on in the tutorial). Now let's create a new `url` service. πŸ₯ In your application's root folder, create a directory named `url` containing a file named `encore.service.ts`. ```bash $ mkdir url $ touch url/encore.service.ts ``` πŸ₯ Add the following code to `url/encore.service.ts`: ```ts url/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("url"); ``` This is how you define a service with Encore. Encore will now consider files in the `url` directory and all its subdirectories as part of the `url` service. πŸ₯ Create a new file `url.ts` in the `url` directory: ```bash $ touch url/url.ts ``` πŸ₯ Add the following code to `url/url.ts`: ```ts url/url.ts import { api } from "encore.dev/api"; import { randomBytes } from "node:crypto"; interface URL { id: string; // short-form URL id url: string; // complete URL, in long form } interface ShortenParams { url: string; // the URL to shorten } // Shortens a URL. export const shorten = api( { method: "POST", path: "/url", expose: true }, async ({ url }: ShortenParams): Promise => { const id = randomBytes(6).toString("base64url"); return { id, url }; }, ); ``` This sets up the `POST /url` endpoint. πŸ₯ Let’s see if it works! Start your app by running the following command from your app's root directory: ```bash $ encore run ``` You should see this: ```output Encore development server running! Your API is running at: http://127.0.0.1:4000 Development Dashboard URL: http://localhost:9400/5g288 3:50PM INF registered API endpoint endpoint=shorten path=/url service=url ``` πŸ₯ Next, call your endpoint from the Local Development Dashboard at [http://localhost:9400](http://localhost:9400/) and view a trace of the response. It should look like this: You can also call it from the terminal: ```bash $ curl http://localhost:4000/url -d '{"url": "https://encore.dev"}' ``` You should see this: ```output { "id": "5cJpBVRp", "url": "https://encore.dev" } ``` It works! There’s just one problem... Right now, we’re not actually storing the URL anywhere. That means we can generate shortened IDs but there’s no way to get back to the original URL! We need to store a mapping from the short ID to the complete URL. ## [2\. Save URLs in a database](https://encore.dev/docs/ts/tutorials/rest-api\#2-save-urls-in-a-database) Fortunately, Encore makes it really easy to set up a PostgreSQL database to store our data. To do so, we first define a **database schema**, in the form of a migration file. πŸ₯ Create a new folder named `migrations` inside the `url` folder. Then, inside the `migrations` folder, create an initial database migration file named `001_create_tables.up.sql`. The file name format is important (it must start with `001_` and end in `.up.sql`). ```bash $ mkdir url/migrations $ touch url/migrations/001_create_tables.up.sql ``` πŸ₯ Add the following contents to the file: ```sql url/migrations/001_create_tables.up.sql CREATE TABLE url ( id TEXT PRIMARY KEY, original_url TEXT NOT NULL ); ``` πŸ₯ Next, go back to the `url/url.ts` file and import the `SQLDatabase` class from `encore.dev/storage/sqldb` module by modifying the imports to look like this: ```ts url/url.ts import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { randomBytes } from "node:crypto"; ``` πŸ₯ Now, to define the database, create an instance of the `SQLDatabase` class in the `url` service: ```ts url/url.ts import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { randomBytes } from "node:crypto"; // 'url' database is used to store the URLs that are being shortened. const db = new SQLDatabase("url", { migrations: "./migrations" }); interface URL { id: string; // short-form URL id url: string; // complete URL, in long form } interface ShortenParams { url: string; // the URL to shorten } // Shortens a URL. export const shorten = api( { method: "POST", path: "/url", expose: true }, async ({ url }: ShortenParams): Promise => { const id = randomBytes(6).toString("base64url"); return { id, url }; }, ); ``` πŸ₯ Lastly, update the `shorten` function to insert data into the database: ```ts url/url.ts import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { randomBytes } from "node:crypto"; // 'url' database is used to store the URLs that are being shortened. const db = new SQLDatabase("url", { migrations: "./migrations" }); interface URL { id: string; // short-form URL id url: string; // complete URL, in long form } interface ShortenParams { url: string; // the URL to shorten } // Shortens a URL. export const shorten = api( { method: "POST", path: "/url", expose: true }, async ({ url }: ShortenParams): Promise => { const id = randomBytes(6).toString("base64url"); await db.exec` INSERT INTO url (id, original_url) VALUES (${id}, ${url}) `; return { id, url }; }, ); ``` ##### Please note Before running your application, make sure you have [Docker](https://www.docker.com/) installed and running. It's required to locally run Encore applications with databases. πŸ₯ Next, start the application again with `encore run` and Encore automatically sets up your database. (In case your application won't run, check the [databases troubleshooting guide](https://encore.dev/docs/ts/primitives/databases#troubleshooting).) You can verify that the database was created by looking at the **Infra** tab in the local development dashboard at [localhost:9400](http://localhost:9400/), which should look like this: ![Infra tab in local development dashboard](https://encore.dev/assets/docs/infra_tab.png) πŸ₯ Now let's call the API again from the local development dashboard, or from the terminal: ```bash $ curl http://localhost:4000/url -d '{"url": "https://encore.dev"}' ``` πŸ₯ Finally, let's verify that it was saved in the database. You can do this by checking the trace in the local development dashboard, or you can run `encore db shell url` from the app root directory and inputting `select * from url;`: ```bash $ encore db shell url psql (13.1, server 11.12) Type "help" for help. url=# select * from url; id | original_url ----------+-------------------- zr6RmZc4 | https://encore.dev (1 row) ``` That was easy! ## [3\. Add endpoint to retrieve URLs](https://encore.dev/docs/ts/tutorials/rest-api\#3-add-endpoint-to-retrieve-urls) To complete our URL shortener API, let’s add the endpoint to retrieve a URL given its short id. πŸ₯ Add this endpoint to `url/url.ts`: ```ts url/url.ts import { api, APIError } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { randomBytes } from "node:crypto"; // 'url' database is used to store the URLs that are being shortened. const db = new SQLDatabase("url", { migrations: "./migrations" }); interface URL { id: string; // short-form URL id url: string; // complete URL, in long form } interface ShortenParams { url: string; // the URL to shorten } // Shortens a URL. export const shorten = api( { method: "POST", path: "/url", expose: true }, async ({ url }: ShortenParams): Promise => { const id = randomBytes(6).toString("base64url"); await db.exec` INSERT INTO url (id, original_url) VALUES (${id}, ${url}) `; return { id, url }; }, ); // Get retrieves the original URL for the id. export const get = api( { expose: true, auth: false, method: "GET", path: "/url/:id" }, async ({ id }: { id: string }): Promise => { const row = await db.queryRow` SELECT original_url FROM url WHERE id = ${id} `; if (!row) throw APIError.notFound("url not found"); return { id, url: row.original_url }; } ); ``` Encore uses the `/url/:id` syntax to represent a path with a parameter. The `id` name corresponds to the parameter name in the function signature. In this case it is of type `string`, but you can also use other built-in types like `number` or `boolean` if you want to restrict the values. πŸ₯ We can make sure it works by reviewing the endpoint in the [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) in the local development dashboard, where we can call it using the `id` you got in the previous step: You can also call it directly from the terminal: ```bash $ curl http://localhost:4000/url/your-id-from-the-previous-step ``` You should now see this: ```json { "id": "your-id-from-the-previous-step", "url": "https://encore.dev" } ``` It works! That's how you build REST APIs and use PostgreSQL databases in Encore. ## [4\. Add a test](https://encore.dev/docs/ts/tutorials/rest-api\#4-add-a-test) Before deployment, it is good practice to have tests to assure that the service works properly. Such tests including database access are easy to write. πŸ₯ Let's start by adding the `vitest` package to your project: ```bash $ npm i --save-dev vitest ``` [Vitest](https://vitest.dev/) is a testing framework that works great with Encore but you can use another TypeScript testing framework if you like. πŸ₯ Next we need to add a test script to our `package.json`: ```json package.json "scripts": { "test": "vitest" }, ``` We've prepared a test to check that the whole cycle of shortening the URL, storing and then retrieving the original URL works. πŸ₯ Save this in a separate file `url/url.test.ts`. ```ts url/url.test.ts import { describe, expect, test } from "vitest"; import { get, shorten } from "./url"; describe("shorten", () => { test("getting a shortened url should give back the original", async () => { const resp = await shorten({ url: "https://example.com" }); const url = await get({ id: resp.id }); expect(url.url).toBe("https://example.com"); }); }); ``` πŸ₯ Now run `encore test` to verify that it's working. If you use the local development dashboard ( [localhost:9400](http://localhost:9400/)), you can even see traces for tests. ## [5\. Deploy](https://encore.dev/docs/ts/tutorials/rest-api\#5-deploy) ### [Self-hosting](https://encore.dev/docs/ts/tutorials/rest-api\#self-hosting) Encore supports building Docker images directly from the CLI, which can then be self-hosted on your own infrastructure of choice. If your app is using infrastructure resources, such as SQL databases, Pub/Sub, or metrics, you will need to configure your Docker image with the necessary configuration. Our URL shortener makes use of a PostgreSQL database, so we'll need to supply a runtime configuration so that our app knows how to connect to the database in the cloud. πŸ₯ Create a new file `infra-config.json` in the root of your project with the following contents: ```json { "$schema": "https://encore.dev/schemas/infra.schema.json", "sql_servers": [\ {\ "host": "my-db-host:5432",\ "databases": {\ "url": {\ "username": "my-db-owner",\ "password": {"$env": "DB_PASSWORD"}\ }\ }\ }\ ] } ``` The values in this configuration are just examples, you will need to replace them with the correct values for your database. Take a look at our guide for [deploying an Encore app with a PostgreSQL database to Digital Ocean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) for more information. πŸ₯ Build a Docker image by running `encore build docker url-shortener:v1.0`. This will compile your application using the host machine and then produce a Docker image containing the compiled application. πŸ₯ Upload the Docker image to the cloud provider of your choice and run it. ### [Encore Cloud (free)](https://encore.dev/docs/ts/tutorials/rest-api\#encore-cloud-free) Encore Cloud provides automated infrastructure and DevOps. Deploy to a free development environment or to your own cloud account on AWS or GCP. ### [Create account](https://encore.dev/docs/ts/tutorials/rest-api\#create-account) Before deploying with Encore Cloud, you need to have a free Encore Cloud account and link your app to the platform. If you already have an account, you can move on to the next step. If you don’t have an account, the simplest way to get set up is by running `encore app create` and selecting **Y** when prompted to create a new account. Once your account is set up, continue creating a new app, selecting the `empty app` template. After creating the app, copy your project files into the new app directory, ensuring that you do not replace the `encore.app` file (this file holds a unique id which links your app to the platform). ### [Commit changes](https://encore.dev/docs/ts/tutorials/rest-api\#commit-changes) The final step before you deploy is to commit all changes to the project repo. πŸ₯ 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 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From there you can also see metrics, traces, and connect your own AWS or GCP account to use for production deployment. _Now you have a fully fledged backend running in the cloud, well done!_ ### [Celebrate with fireworks](https://encore.dev/docs/ts/tutorials/rest-api\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) πŸ₯ A great next step is to [integrate with GitHub](https://encore.dev/docs/platform/integrations/github). Once you've linked with GitHub, Encore will automatically start building and running tests against your Pull Requests. ## [What's next](https://encore.dev/docs/ts/tutorials/rest-api\#whats-next) Now that you know how to build a backend with a database, you're ready to let your creativity flow and begin building your next great idea! We're excited to hear what you're going to build with Encore, join the pioneering developer community on [Discord](https://encore.dev/discord) and share your story. [Hello World](https://encore.dev/docs/ts/concepts/hello-world) [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/tutorials/rest-api.mdx) ## Uptime Monitoring Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Want to be notified when your website goes down so you can fix it before your users notice? You need an uptime monitoring system. Sounds daunting? Don't worry, we'll build it with Encore in 30 minutes! The app will use an event-driven architecture and the final result will look like this: ![](https://encore.dev/assets/tutorials/uptime/frontend.png) ```ts 1 import { api } from "encore.dev/api"; 2 import { Subscription, Topic } from "encore.dev/pubsub"; 3 import { SQLDatabase } from "encore.dev/storage/sqldb"; 4 import { Site, SiteAddedTopic } from "../site/site"; 5 import { ping } from "./ping"; 6 import { site } from "~encore/clients"; 7 import { CronJob } from "encore.dev/cron"; 8 9 // Check checks a single site. 10 export const check = api( 11 { expose: true, method: "POST", path: "/check/:siteID" }, 12 async (p: { siteID: number }): Promise<{ up: boolean }> => { 13 const s = await site.get({ id: p.siteID }); 14 return doCheck(s); 15 }, 16 ); 17 18 // CheckAll checks all sites. 19 export const checkAll = api( 20 { expose: true, method: "POST", path: "/check-all" }, 21 async (): Promise => { 22 const sites = await site.list(); 23 await Promise.all(sites.sites.map(doCheck)); 24 }, 25 ); 26 27 // Defines a Cron Job to check all tracked sites every hour. 28 // Learn more: https://encore.dev/docs/ts/primitives/cron-jobs 29 30 // 'check-all' is used to check all tracked sites every hour. 31 const cronJob = new CronJob("check-all", { 32 title: "Check all sites", 33 every: "1h", 34 endpoint: checkAll, 35 }); 36 37 async function doCheck(site: Site): Promise<{ up: boolean }> { 38 const { up } = await ping({ url: site.url }); 39 40 // Publish a Pub/Sub message if the site transitions 41 // from up->down or from down->up. 42 const wasUp = await getPreviousMeasurement(site.id); 43 if (up !== wasUp) { 44 await TransitionTopic.publish({ site, up }); 45 } 46 47 await MonitorDB.exec` 48 INSERT INTO checks (site_id, up, checked_at) 49 VALUES (${site.id}, ${up}, NOW()) 50 `; 51 return { up }; 52 } 53 54 // getPreviousMeasurement reports whether the given site was 55 // up or down in the previous measurement. 56 async function getPreviousMeasurement(siteID: number): Promise { 57 const row = await MonitorDB.queryRow` 58 SELECT up 59 FROM checks 60 WHERE site_id = ${siteID} 61 ORDER BY checked_at DESC 62 LIMIT 1 63 `; 64 return row?.up ?? true; 65 } 66 67 // Define a database named 'monitor', using the database migrations 68 // in the "./migrations" folder. Encore automatically provisions, 69 // migrates, and connects to the database. 70 71 // The 'monitor' database is used to store the uptime checks 72 export const MonitorDB = new SQLDatabase("monitor", { 73 migrations: "./migrations", 74 }); 75 76 const _ = new Subscription(SiteAddedTopic, "check-site", { 77 handler: doCheck, 78 }); 79 80 // TransitionEvent describes a transition of a monitored site 81 // from up->down or from down->up. 82 export interface TransitionEvent { 83 site: Site; // Site is the monitored site in question. 84 up: boolean; // Up specifies whether the site is now up or down (the new value). 85 } 86 87 // 'uptime-transition' is a pubsub topic with transition events for when a monitored site 88 // transitions from up->down or from down->up. 89 export const TransitionTopic = new Topic("uptime-transition", { 90 deliveryGuarantee: "at-least-once", 91 }); 92 ``` Project frontend monitor migrations 1\_create\_tables.up.sql check.test.ts check.ts encore.service.ts ping.test.ts ping.ts status.ts site migrations 1\_create\_tables.up.sql encore.service.ts site.ts slack encore.service.ts slack.ts Encore encore.app package.json tsconfig.json vite.config.ts Open on ## [1\. Create your Encore application](https://encore.dev/docs/ts/tutorials/uptime\#1-create-your-encore-application) ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. πŸ₯ Create a new Encore application, using this tutorial project's starting-point branch. This gives you a ready-to-go frontend to use. ```bash $ encore app create uptime --example=github.com/encoredev/example-app-uptime/tree/starting-point-ts ``` If this is the first time you're using Encore, you'll be asked if you wish to create a free account. This is needed when you want Encore to manage functionality like secrets and handle cloud deployments (which we'll use later on in the tutorial). When we're done we'll have a backend with an event-driven architecture, as seen below in the [automatically generated diagram](https://encore.dev/docs/ts/observability/encore-flow) where white boxes are services and black boxes are Pub/Sub topics: ![](https://encore.dev/assets/tutorials/uptime/encore-flow.png) ## [2\. Create monitor service](https://encore.dev/docs/ts/tutorials/uptime\#2-create-monitor-service) Let's start by creating the functionality to check if a website is currently up or down. Later we'll store this result in a database so we can detect when the status changes and send alerts. πŸ₯ Create a directory named `monitor` containing a file named `encore.service.ts`. ```bash $ mkdir monitor $ touch monitor/encore.service.ts ``` πŸ₯ Add the following code to `monitor/encore.service.ts`: ```ts monitor/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("monitor"); ``` This is how you create define services with Encore. Encore will now consider files in the `monitor` directory and all its subdirectories as part of the `monitor` service. πŸ₯ In the `monitor` directory, create a file named `ping.ts`. πŸ₯ Add an Encore API endpoint named `ping` that takes a URL as input and returns a response indicating whether the site is up or down. ```ts monitor/ping.ts // Service monitor checks if a website is up or down. import { api } from "encore.dev/api"; export interface PingParams { url: string; } export interface PingResponse { up: boolean; } // Ping pings a specific site and determines whether it's up or down right now. export const ping = api( { expose: true, path: "/ping/:url", method: "GET" }, async ({ url }) => { // If the url does not start with "http:" or "https:", default to "https:". if (!url.startsWith("http:") && !url.startsWith("https:")) { url = "https://" + url; } try { // Make an HTTP request to check if it's up. const resp = await fetch(url, { method: "GET" }); // 2xx and 3xx status codes are considered up const up = resp.status >= 200 && resp.status < 300; return { up }; } catch (err) { return { up: false }; } } ); ``` πŸ₯ Let's try it! Run `encore run` in your terminal and you should see the service start up. Then open up the Local Development Dashboard at [http://localhost:9400](http://localhost:9400/) and try calling the `monitor.ping` endpoint from the API Explorer, passing in `google.com` as the URL. You can then see the response, logs, and view a trace of the request. It will look like this: If you prefer to use the terminal instead run `curl http://localhost:4000/ping/google.com` in a new terminal instead. Either way you should see the response: ```json {"up": true} ``` You can also try with `httpstat.us/400` and `some-non-existing-url.com` and it should respond with `{"up": false}`. (It's always a good idea to test the negative case as well.) ### [Add a test](https://encore.dev/docs/ts/tutorials/uptime\#add-a-test) πŸ₯ Let's write an automated test so we don't break this endpoint over time. Create the file `monitor/ping.test.ts` with the content: ```ts monitor/ping.test.ts import { describe, expect, test } from "vitest"; import { ping } from "./ping"; describe("ping", () => { test.each([\ // Test both with and without "https://"\ { site: "google.com", expected: true },\ { site: "https://encore.dev", expected: true },\ \ // 4xx and 5xx should considered down.\ { site: "https://not-a-real-site.xyz", expected: false },\ // Invalid URLs should be considered down.\ { site: "invalid://scheme", expected: false },\ ])( `should verify that $site is ${"$expected" ? "up" : "down"}`, async ({ site, expected }) => { const resp = await ping({ url: site }); expect(resp.up).toBe(expected); }, ); }); ``` πŸ₯ Run `encore test` to check that it all works as expected. You should see something like: ```bash $ encore test DEV v1.3.0 βœ“ monitor/ping.test.ts (4) βœ“ ping (4) βœ“ should verify that 'google.com' is up βœ“ should verify that 'https://encore.dev' is up βœ“ should verify that 'https://not-a-real-site.xyz' is down βœ“ should verify that 'invalid://scheme' is down Test Files 1 passed (1) Tests 4 passed (4) Start at 12:31:03 Duration 460ms (transform 43ms, setup 0ms, collect 59ms, tests 272ms, environment 0ms, prepare 47ms) PASS Waiting for file changes... ``` ## [3\. Create site service](https://encore.dev/docs/ts/tutorials/uptime\#3-create-site-service) Next, we want to keep track of a list of websites to monitor. Since most of these APIs will be simple "CRUD" (Create/Read/Update/Delete) endpoints, let's build this service using [Knex.js](https://knexjs.org/), an ORM library that makes building CRUD endpoints really simple. πŸ₯ Let's start with creating a new service named `site`: ```bash $ mkdir site # Create a new directory in the application root $ touch site/encore.service.ts ``` ```ts site/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("site"); ``` πŸ₯ Now we want to add a SQL database to the `site` service. To do so, create a new directory named `migrations` folder inside the `site` folder: ```bash $ mkdir site/migrations ``` πŸ₯ Add a database migration file inside that folder, named `1_create_tables.up.sql`. The file name is important (it must look something like `1_.up.sql`). Add the following contents: ```sql site/migrations/1_create_tables.up.sql CREATE TABLE site ( id SERIAL PRIMARY KEY, url TEXT NOT NULL UNIQUE ); ``` πŸ₯ Next, install the Knex.js library and PostgreSQL client: ```bash $ npm i knex pg ``` Now let's create the `site` service itself with our CRUD endpoints. πŸ₯ Create `site/site.ts` with the contents: ```ts site/site.ts import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import knex from "knex"; // Site describes a monitored site. export interface Site { id: number; // ID is a unique ID for the site. url: string; // URL is the site's URL. } // AddParams are the parameters for adding a site to be monitored. export interface AddParams { // URL is the URL of the site. If it doesn't contain a scheme // (like "http:" or "https:") it defaults to "https:". url: string; } // Add a new site to the list of monitored websites. export const add = api( { expose: true, method: "POST", path: "/site" }, async (params: AddParams): Promise => { const site = (await Sites().insert({ url: params.url }, "*"))[0]; return site; }, ); // Get a site by id. export const get = api( { expose: true, method: "GET", path: "/site/:id", auth: false }, async ({ id }: { id: number }): Promise => { const site = await Sites().where("id", id).first(); return site ?? Promise.reject(new Error("site not found")); }, ); // Delete a site by id. export const del = api( { expose: true, method: "DELETE", path: "/site/:id" }, async ({ id }: { id: number }): Promise => { await Sites().where("id", id).delete(); }, ); export interface ListResponse { sites: Site[]; // Sites is the list of monitored sites } // Lists the monitored websites. export const list = api( { expose: true, method: "GET", path: "/site" }, async (): Promise => { const sites = await Sites().select(); return { sites }; }, ); // Define a database named 'site', using the database migrations // in the "./migrations" folder. Encore automatically provisions, // migrates, and connects to the database. const SiteDB = new SQLDatabase("site", { migrations: "./migrations", }); const orm = knex({ client: "pg", connection: SiteDB.connectionString, }); const Sites = () => orm("site"); ``` πŸ₯ Now make sure you have [Docker](https://docker.com/) installed and running, and then restart `encore run` to cause the `site` database to be created by Encore. You can verify that the database was created by looking at your application's Flow architecture diagram in the local development dashboard at [localhost:9400](http://localhost:9400/), and then use the Service Catalog to call the `site.add` endpoint. You can also call the `site.add` endpoint from the terminal: ```bash $ curl -X POST 'http://localhost:4000/site' -d '{"url": "https://encore.dev"}' { "id": 1, "url": "https://encore.dev" } ``` ## [4\. Record uptime checks](https://encore.dev/docs/ts/tutorials/uptime\#4-record-uptime-checks) In order to notify when a website goes down or comes back up, we need to track the previous state it was in. πŸ₯ To do so, let's add a database to the `monitor` service as well. Create the directory `monitor/migrations` and the file `monitor/migrations/1_create_tables.up.sql`: ```sql monitor/migrations/1_create_tables.up.sql CREATE TABLE checks ( id BIGSERIAL PRIMARY KEY, site_id BIGINT NOT NULL, up BOOLEAN NOT NULL, checked_at TIMESTAMP WITH TIME ZONE NOT NULL ); ``` We'll insert a database row every time we check if a site is up. πŸ₯ Add a new endpoint `check` to the `monitor` service, that takes in a Site ID, pings the site, and inserts a database row in the `checks` table. For this service we'll use Encore's [`SQLDatabase` class](https://encore.dev/docs/ts/primitives/databases#querying-data) instead of Knex (in order to showcase both approaches). Add the following to `monitor/check.ts`: ```ts monitor/check.ts import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { ping } from "./ping"; import { site } from "~encore/clients"; // Check checks a single site. export const check = api( { expose: true, method: "POST", path: "/check/:siteID" }, async (p: { siteID: number }): Promise<{ up: boolean }> => { const s = await site.get({ id: p.siteID }); const { up } = await ping({ url: s.url }); await MonitorDB.exec` INSERT INTO checks (site_id, up, checked_at) VALUES (${s.id}, ${up}, NOW()) `; return { up }; }, ); // Define a database named 'monitor', using the database migrations // in the "./migrations" folder. Encore automatically provisions, // migrates, and connects to the database. export const MonitorDB = new SQLDatabase("monitor", { migrations: "./migrations", }); ``` πŸ₯ Restart `encore run` to cause the `monitor` database to be created. We can again verify that the database was created in the Flow diagram, and also see the dependency between the `monitor` service and the `site` service that we just added. We can then call the `monitor.check` endpoint using the id `1` that we got in the last step, and view the trace where we see the database interactions. It will look something like this: You can also also inspect the database using `encore db shell `: ```bash $ encore db shell monitor psql (14.4, server 14.2) Type "help" for help. monitor=> SELECT * FROM checks; id | site_id | up | checked_at ----+---------+----+------------------------------- 1 | 1 | t | 2022-10-21 09:58:30.674265+00 ``` If that's what you see, everything's working perfectly! ### [Add a cron job to check all sites](https://encore.dev/docs/ts/tutorials/uptime\#add-a-cron-job-to-check-all-sites) We now want to regularly check all the tracked sites so we can respond in case any of them go down. We'll create a new `checkAll` API endpoint in the `monitor` service that will list all the tracked sites and check all of them. πŸ₯ Let's extract some of the functionality we wrote for the `check` endpoint into a separate function, like so: ```ts monitor/check.ts import {Site} from "../site/site"; // Check checks a single site. export const check = api( { expose: true, method: "POST", path: "/check/:siteID" }, async (p: { siteID: number }): Promise<{ up: boolean }> => { const s = await site.get({ id: p.siteID }); return doCheck(s); }, ); async function doCheck(site: Site): Promise<{ up: boolean }> { const { up } = await ping({ url: site.url }); await MonitorDB.exec` INSERT INTO checks (site_id, up, checked_at) VALUES (${site.id}, ${up}, NOW()) `; return { up }; } ``` Now we're ready to create our new `checkAll` endpoint. πŸ₯ Create the new `checkAll` endpoint inside `monitor/check.ts`: ```ts monitor/check.ts // CheckAll checks all sites. export const checkAll = api( { expose: true, method: "POST", path: "/check-all" }, async (): Promise => { const sites = await site.list(); await Promise.all(sites.sites.map(doCheck)); }, ); ``` πŸ₯ Now that we have a `checkAll` endpoint, define a [cron job](https://encore.dev/docs/ts/primitives/cron-jobs) to automatically call it every 1 hour (since this is an example, we don't need to go too crazy and check every minute): ```ts monitor/check.ts import { CronJob } from "encore.dev/cron"; // Check all tracked sites every 1 hour. const cronJob = new CronJob("check-all", { title: "Check all sites", every: "1h", endpoint: checkAll, }); ``` ##### Please note To avoid confusion while developing, cron jobs are not triggered when running the application locally but work when deploying the application to a cloud environment. The frontend needs a way to list all sites and display if they are up or down. πŸ₯ Add a file `monitor/status.ts` with the following code: ```ts import { api } from "encore.dev/api"; import { MonitorDB } from "./check"; interface SiteStatus { id: number; up: boolean; checkedAt: string; } // StatusResponse is the response type from the Status endpoint. interface StatusResponse { // Sites contains the current status of all sites, // keyed by the site ID. sites: SiteStatus[]; } // status checks the current up/down status of all monitored sites. export const status = api( { expose: true, path: "/status", method: "GET" }, async (): Promise => { const rows = await MonitorDB.query` SELECT DISTINCT ON (site_id) site_id, up, checked_at FROM checks ORDER BY site_id, checked_at DESC `; const results: SiteStatus[] = []; for await (const row of rows) { results.push({ id: row.site_id, up: row.up, checkedAt: row.checked_at, }); } return { sites: results }; }, ); ``` Now that the backend is working, let's open [http://localhost:4000/](http://localhost:4000/) in the browser to see the frontend of our application. ![](https://encore.dev/assets/tutorials/uptime/frontend.png) ## [5\. Deploy](https://encore.dev/docs/ts/tutorials/uptime\#5-deploy) To try out your uptime monitor for real, let's deploy it to the cloud. ### [Self-hosting](https://encore.dev/docs/ts/tutorials/uptime\#self-hosting) Encore supports building Docker images directly from the CLI, which can then be self-hosted on your own infrastructure of choice. If your app is using infrastructure resources, such as SQL databases, Pub/Sub, or metrics, you will need to supply a [runtime configuration](https://encore.dev/docs/ts/self-host/configure-infra) your Docker image. πŸ₯ Create a new file `infra-config.json` in the root of your project with the following contents: ```json { "$schema": "https://encore.dev/schemas/infra.schema.json", "sql_servers": [\ {\ "host": "my-db-host:5432",\ "databases": {\ "monitor": {\ "username": "my-db-owner",\ "password": {"$env": "DB_PASSWORD"}\ },\ "site": {\ "username": "my-db-owner",\ "password": {"$env": "DB_PASSWORD"}\ }\ }\ }\ ] } ``` The values in this configuration are just examples, you will need to replace them with the correct values for your database. Take a look at our guide for [deploying an Encore app with a PostgreSQL database to Digital Ocean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) for more information. πŸ₯ Build a Docker image by running `encore build docker uptime:v1.0`. This will compile your application using the host machine and then produce a Docker image containing the compiled application. πŸ₯ Upload the Docker image to the cloud provider of your choice and run it. ### [Encore Cloud (free)](https://encore.dev/docs/ts/tutorials/uptime\#encore-cloud-free) Encore Cloud provides automated infrastructure and DevOps. Deploy to a free development environment or to your own cloud account on AWS or GCP. ### [Create account](https://encore.dev/docs/ts/tutorials/uptime\#create-account) Before deploying with Encore Cloud, you need to have a free Encore Cloud account and link your app to the platform. If you already have an account, you can move on to the next step. If you don’t have an account, the simplest way to get set up is by running `encore app create` and selecting **Y** when prompted to create a new account. Once your account is set up, continue creating a new app, selecting the `empty app` template. After creating the app, copy your project files into the new app directory, ensuring that you do not replace the `encore.app` file (this file holds a unique id which links your app to the platform). ### [Commit changes](https://encore.dev/docs/ts/tutorials/uptime\#commit-changes) Encore comes with built-in CI/CD, and the deployment process is as simple as a `git push`. (You can also integrate with GitHub, learn more in the [CI/CD docs](https://encore.dev/docs/platform/deploy/deploying).) πŸ₯ Let's deploy your app to Encore's free development cloud by running: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From the Cloud Dashboard you can also see metrics, trigger Cron Jobs, see traces, and later connect your own AWS or GCP account to use for deployment. πŸ₯ When the deploy has finished, you can try out your uptime monitor by going to `https://staging-$APP_ID.encr.app`. _You now have an app running in the cloud, well done!_ ## [6\. Publish Pub/Sub events when a site goes down](https://encore.dev/docs/ts/tutorials/uptime\#6-publish-pubsub-events-when-a-site-goes-down) Hold on, an uptime monitoring system isn't very useful if it doesn't actually notify you when a site goes down. To do so let's add a [Pub/Sub topic](https://encore.dev/docs/ts/primitives/pubsub) on which we'll publish a message every time a site transitions from being up to being down, or vice versa. πŸ₯ Define the topic using Encore's Pub/Sub module in `monitor/check.ts`: ```ts monitor/check.ts import { Subscription, Topic } from "encore.dev/pubsub"; // TransitionEvent describes a transition of a monitored site // from up->down or from down->up. export interface TransitionEvent { site: Site; // Site is the monitored site in question. up: boolean; // Up specifies whether the site is now up or down (the new value). } // TransitionTopic is a pubsub topic with transition events for when a monitored site // transitions from up->down or from down->up. export const TransitionTopic = new Topic("uptime-transition", { deliveryGuarantee: "at-least-once", }); ``` Now let's publish a message on the `TransitionTopic` if a site's up/down state differs from the previous measurement. πŸ₯ Create a `getPreviousMeasurement` function to report the last up/down state: ```ts monitor/check.ts // getPreviousMeasurement reports whether the given site was // up or down in the previous measurement. async function getPreviousMeasurement(siteID: number): Promise { const row = await MonitorDB.queryRow` SELECT up FROM checks WHERE site_id = ${siteID} ORDER BY checked_at DESC LIMIT 1 `; return row?.up ?? true; } ``` πŸ₯ Now add a function to conditionally publish a message if the up/down state differs by modifying the `doCheck` function: ```ts monitor/check.ts async function doCheck(site: Site): Promise<{ up: boolean }> { const { up } = await ping({ url: site.url }); // Publish a Pub/Sub message if the site transitions // from up->down or from down->up. const wasUp = await getPreviousMeasurement(site.id); if (up !== wasUp) { await TransitionTopic.publish({ site, up }); } await MonitorDB.exec` INSERT INTO checks (site_id, up, checked_at) VALUES (${site.id}, ${up}, NOW()) `; return { up }; } ``` πŸ₯ Start your app again using `encore run` and open the Flow architecture diagram in the local development dashboard. Now you'll see the Pub/Sub topic as a black box, it should look like this: ![](https://encore.dev/assets/docs/uptime_tut_flow_2.png) Now the monitoring system will publish messages on the `TransitionTopic` whenever a monitored site transitions from up->down or from down->up. It doesn't know or care who actually listens to these messages. The truth is right now nobody does. So let's fix that by adding a Pub/Sub subscriber that posts these events to Slack. ## [7\. Send Slack notifications when a site goes down](https://encore.dev/docs/ts/tutorials/uptime\#7-send-slack-notifications-when-a-site-goes-down) πŸ₯ Start by creating a new service named `slack`: ```bash $ mkdir slack # Create a new directory in the application root $ touch slack/encore.service.ts ``` ```ts slack/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("slack"); ``` πŸ₯ Add a `slack.ts` file containing the following: ```ts slack/slack.ts import { api } from "encore.dev/api"; import { secret } from "encore.dev/config"; import log from "encore.dev/log"; export interface NotifyParams { text: string; // the slack message to send } // Sends a Slack message to a pre-configured channel using a // Slack Incoming Webhook (see https://api.slack.com/messaging/webhooks). export const notify = api({}, async ({ text }) => { const url = webhookURL(); if (!url) { log.info("no slack webhook url defined, skipping slack notification"); return; } const resp = await fetch(url, { method: "POST", headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ content: text }), }); if (resp.status >= 400) { const body = await resp.text(); throw new Error(`slack notification failed: ${resp.status}: ${body}`); } }); // SlackWebhookURL defines the Slack webhook URL to send uptime notifications to. const webhookURL = secret("SlackWebhookURL"); ``` πŸ₯ Now go to a Slack community of your choice where you have the permission to create a new Incoming Webhook. πŸ₯ Once you have the Webhook URL, set it as an Encore secret: ```bash $ encore secret set --type dev,local,pr SlackWebhookURL Enter secret value: ***** Successfully updated development secret SlackWebhookURL. ``` πŸ₯ Test the `slack.notify` endpoint by calling it via cURL: ```bash $ curl 'http://localhost:4000/slack.notify' -d '{"text": "Testing Slack webhook"}' ``` You should see the _Testing Slack webhook_ message appear in the Slack channel you designated for the webhook. πŸ₯ When it works it's time to add a Pub/Sub subscriber to automatically notify Slack when a monitored site goes up or down. Add the following: ```ts slack/slack.ts import { Subscription } from "encore.dev/pubsub"; import { TransitionTopic } from "../monitor/check"; const _ = new Subscription(TransitionTopic, "slack-notification", { handler: async (event) => { const text = `*${event.site.url} is ${event.up ? "back up." : "down!"}*`; await notify({ text }); }, }); ``` ## [8\. Deploy your finished Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime\#8-deploy-your-finished-uptime-monitor) Now you're ready to deploy your finished Uptime Monitor, complete with a Slack integration. ### [Self-hosting](https://encore.dev/docs/ts/tutorials/uptime\#self-hosting-1) Because we have added more infrastructure to our app, we need to [update the configuration](https://encore.dev/docs/ts/self-host/configure-infra) in our `infra-config.json` to include the new Pub/Sub topic and subscription as well as how we should set the `SlackWebhookURL` secret. πŸ₯ Update your `Γ¬nfra-config.json` to reflect the new infrastructure. πŸ₯ Build a Docker image by running `encore build docker uptime:v2.0`. πŸ₯ Upload the Docker image to the cloud provider and run it. ### [Encore Cloud (free)](https://encore.dev/docs/ts/tutorials/uptime\#encore-cloud-free-1) πŸ₯ As before, deploying your app to the cloud is as simple as running: ```bash $ git add -A . $ git commit -m 'Add slack integration' $ git push encore ``` ### [Celebrate with fireworks](https://encore.dev/docs/ts/tutorials/uptime\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) ## [Conclusion](https://encore.dev/docs/ts/tutorials/uptime\#conclusion) We've now built a fully functioning uptime monitoring system. If we may say so ourselves (and we may; it's our documentation after all) it's pretty remarkable how much we've accomplished in such little code: - We've built three different services (`site`, `monitor`, and `slack`) - We've added two databases (to the `site` and `monitor` services) for tracking monitored sites and the monitoring results - We've added a cron job for automatically checking the sites every hour - We've set up a Pub/Sub topic to decouple the monitoring system from the Slack notifications - We've added a Slack integration, using secrets to securely store the webhook URL, listening to a Pub/Sub subscription for up/down transition events All of this in just a bit over 300 lines of code. It's time to lean back and take a sip of your favorite beverage, safe in the knowledge you'll never be caught unaware of a website going down suddenly. [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/tutorials/uptime.md) ## GraphQL API Tutorial Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore has great support for GraphQL with its type-safe approach to building APIs. Encore's automatic tracing also makes it easy to find and fix performance issues that often arise in GraphQL APIs (like the [N+1 problem](https://hygraph.com/blog/graphql-n-1-problem)). In this tutorial we will build a GraphQL API using [Apollo](https://www.apollographql.com/docs/apollo-server/) and Encore.ts. The final code will look like this: ```ts 1 import { api } from "encore.dev/api"; 2 import log from "encore.dev/log"; 3 import { ApolloServer, HeaderMap } from "@apollo/server"; 4 import { readFileSync } from "node:fs"; 5 import resolvers from "./resolvers"; 6 import { json } from "node:stream/consumers"; 7 8 const typeDefs = readFileSync("./schema.graphql", { encoding: "utf-8" }); 9 10 const server = new ApolloServer({ 11 typeDefs, 12 resolvers, 13 }); 14 15 await server.start(); 16 17 export const graphqlAPI = api.raw( 18 { expose: true, path: "/graphql", method: "*" }, 19 async (req, res) => { 20 server.assertStarted("/graphql"); 21 22 const headers = new HeaderMap(); 23 for (const [key, value] of Object.entries(req.headers)) { 24 if (value !== undefined) { 25 headers.set(key, Array.isArray(value) ? value.join(", ") : value); 26 } 27 } 28 29 let body: unknown; 30 31 try { 32 body = await json(req); 33 } catch (error) { 34 log.error(error); 35 } 36 37 // More on how to use executeHTTPGraphQLRequest: https://www.apollographql.com/docs/apollo-server/integrations/building-integrations/ 38 const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({ 39 httpGraphQLRequest: { 40 headers, 41 method: req.method!.toUpperCase(), 42 body, 43 search: new URLSearchParams(req.url ?? "").toString(), 44 }, 45 context: async () => { 46 return { req, res }; 47 }, 48 }); 49 50 for (const [key, value] of httpGraphQLResponse.headers) { 51 res.setHeader(key, value); 52 } 53 res.statusCode = httpGraphQLResponse.status || 200; 54 55 if (httpGraphQLResponse.body.kind === "complete") { 56 res.end(httpGraphQLResponse.body.string); 57 return; 58 } 59 60 for await (const chunk of httpGraphQLResponse.body.asyncIterator) { 61 res.write(chunk); 62 } 63 res.end(); 64 }, 65 ); 66 ``` Project book book.ts encore.service.ts graphql \_\_generated\_\_ resolvers-types.ts resolvers index.ts mutations.ts queries.ts encore.service.ts graphql.ts codegen.yml Encore encore.app package.json schema.graphql tsconfig.json Open on ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Create your Encore application](https://encore.dev/docs/ts/tutorials/graphql\#1-create-your-encore-application) πŸ₯ Create a new application by running `encore app create` and select `Empty app` as the template. If this is the first time you're using Encore, you'll be asked if you wish to create a free account. This is optional, but is needed when you want Encore to manage functionality like secrets and handle cloud deployments (which we'll use later on in the tutorial). ## [2\. GraphQL setup](https://encore.dev/docs/ts/tutorials/graphql\#2-graphql-setup) First, we need to install the necessary dependencies: πŸ₯ Update your `package.json` file to look like this: ```perl package.json { "name": "encore-graphql", "private": true, "version": "0.0.1", "license": "MPL-2.0", "type": "module", "scripts": { "generate": "graphql-codegen --config codegen.yml" }, "devDependencies": { "@types/node": "^20.5.7", "typescript": "^5.2.2", "@graphql-codegen/cli": "2.16.5", "@graphql-codegen/typescript": "2.8.8", "@graphql-codegen/typescript-resolvers": "2.7.13" }, "dependencies": { "@apollo/server": "^4.11.0", "encore.dev": "^1.35.3", "graphql": "^16.9.0", "graphql-tag": "^2.12.6" } } ``` πŸ₯ Run `npm install` to install the dependencies. πŸ₯ Next, create a `codegen.yml` file in the application root containing: ```yaml codegen.yml # This configuration file tells GraphQL Code Generator how to generate types based on our schema. schema: './schema.graphql' generates: # Specify where our generated types should live. ./graphql/__generated__/resolvers-types.ts: plugins: - 'typescript' - 'typescript-resolvers' config: useIndexSignature: true ``` ## [3\. Add GraphQL schema](https://encore.dev/docs/ts/tutorials/graphql\#3-add-graphql-schema) Now it's time to define the GraphQL schema. πŸ₯ Create a `schema.graphql` file in the application root containing: ```typescript schema.graphql type Query { books: [Book] } type Book { title: String! author: String! } type AddBookMutationResponse { code: String! success: Boolean! message: String! book: Book } type Mutation { addBook(title: String!, author: String!): AddBookMutationResponse } ``` πŸ₯ Run the code generation script to generate the resolver types: ```bash $ npm run generate ``` The types will be written to `graphql/__generated__/resolvers-types.ts` and will contain a bunch of types that we can use when implementing the resolvers. ## [4\. Create a Book service](https://encore.dev/docs/ts/tutorials/graphql\#4-create-a-book-service) Let's create a simple book service that we can later query using GraphQL. It's a good idea to to make the GraphQL library query Encore endpoints because that will result in traces being created for each called endpoint. Having tracing makes it easy to find and fix performance issues that often arise in GraphQL APIs. πŸ₯ In your application's root folder, create a directory named `book` containing a file named `encore.service.ts`. ```bash $ mkdir book $ touch book/encore.service.ts ``` πŸ₯ Add the following code to `book/encore.service.ts`: ```ts book/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("book"); ``` This is how you define a service with Encore. Encore will now consider files in the `book` directory and all its subdirectories as part of the `book` service. πŸ₯ Next, create a `book/book.ts` file containing: ```ts import { api, APIError } from "encore.dev/api"; import { Book } from "../graphql/__generated__/resolvers-types"; const db: Book[] = [\ {\ title: "To Kill a Mockingbird",\ author: "Harper Lee",\ },\ {\ title: "1984",\ author: "George Orwell",\ },\ {\ title: "The Great Gatsby",\ author: "F. Scott Fitzgerald",\ },\ {\ title: "Moby-Dick",\ author: "Herman Melville",\ },\ {\ title: "Pride and Prejudice",\ author: "Jane Austen",\ },\ ]; export const list = api( { expose: true, method: "GET", path: "/books" }, async (): Promise<{ books: Book[] }> => { return { books: db }; }, ); // Omit the "__typename" field from the request type AddRequest = Omit, "__typename">; export const add = api( { expose: true, method: "POST", path: "/book" }, async (book: AddRequest): Promise<{ book: Book }> => { if (db.some((b) => b.title === book.title)) { throw APIError.alreadyExists( `Book "${book.title}" is already in database`, ); } db.push(book); return { book }; }, ); ``` The `book` service contains two endpoint, one for listing all books and another to add a new book to the database. Our "database" is hardcoded just to limit the scope of this example. Take a look at the [Using SQL databases](https://encore.dev/docs/ts/primitives/databases) docs to learn how to set up and use a database. We get the `Book` type from the generated resolver types. This will make it easier later when we create the resolver functions. ## [5\. Create the GraphQL service](https://encore.dev/docs/ts/tutorials/graphql\#5-create-the-graphql-service) Now it's time to create our Encore service that will provide the GraphQL API. πŸ₯ In the `graphql` directory, add a `encore.service.ts` file with the following content: ```ts graphql/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("graphql"); ``` Now, we need to create resolvers that call the `book` service. Since the GraphQL API uses the same types as the Encore API exposes (we import types form `resolvers-types.ts` in `book.ts`), our resolver can just be thin wrapper around out API endpoints. πŸ₯ Create the directory `resolvers` in the `graphql` directory. In the resolvers directory we want to place three files: `index.ts`, `queries.ts` and `mutations.ts`: ```ts resolvers/index.ts resolvers/queries.ts resolvers/mutations.ts import { Resolvers } from "../__generated__/resolvers-types"; import Query from "./queries.js"; import Mutation from "./mutations.js"; const resolvers: Resolvers = { Query, Mutation }; export default resolvers; ``` Now we are ready can create the ApolloServer that makes use of our resolvers and to expose our GraphQL endpoint. πŸ₯ Still in the `graphql` directory, create a `graphql.ts` file containing: ```ts graphql/graphql.ts import { api } from "encore.dev/api"; import { ApolloServer, HeaderMap } from "@apollo/server"; import { readFileSync } from "node:fs"; import resolvers from "./resolvers"; import { json } from "node:stream/consumers"; const typeDefs = readFileSync("./schema.graphql", { encoding: "utf-8" }); const server = new ApolloServer({ typeDefs, resolvers, }); await server.start(); export const graphqlAPI = api.raw( { expose: true, path: "/graphql", method: "*" }, async (req, res) => { server.assertStarted("/graphql"); const headers = new HeaderMap(); for (const [key, value] of Object.entries(req.headers)) { if (value !== undefined) { headers.set(key, Array.isArray(value) ? value.join(", ") : value); } } // More on how to use executeHTTPGraphQLRequest: https://www.apollographql.com/docs/apollo-server/integrations/building-integrations/ const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({ httpGraphQLRequest: { headers, method: req.method!.toUpperCase(), body: await json(req), search: new URLSearchParams(req.url ?? "").toString(), }, context: async () => { return { req, res }; }, }); for (const [key, value] of httpGraphQLResponse.headers) { res.setHeader(key, value); } res.statusCode = httpGraphQLResponse.status || 200; if (httpGraphQLResponse.body.kind === "complete") { res.end(httpGraphQLResponse.body.string); return; } for await (const chunk of httpGraphQLResponse.body.asyncIterator) { res.write(chunk); } res.end(); }, ); ``` This creates an [Raw API endpoint](https://encore.dev/docs/ts/primitives/raw-endpoints) available on `/graphql`. In the endpoint we use ApolloServer to handle the GraphQL queries and mutations. We then return the response to the client. If we were to use another GraphQL library other than Apollo, the concept would still be the same: 1. Take client requests with a Raw endpoint. 2. Pass along the request and response objects to the GraphQL library of your choice. 3. Use the library to handle the GraphQL queries and mutations. 4. Return the GraphQL response from the Raw endpoint. ## [6\. Trying it out](https://encore.dev/docs/ts/tutorials/graphql\#6-trying-it-out) With that, the GraphQL API is done! πŸ₯Try it out by running `encore run` and opening [https://studio.apollographql.com/sandbox](https://studio.apollographql.com/sandbox) in your browser. Set http://localhost:4000/graphql as your endpoint URL. You should now be able to read the schema and execute queries. Enter the query: ```graphql mutation AddBook { addBook(author: "J.R.R. Tolkien", title: "The Hobbit") { success message code } } ``` Now try the GetBooks query: ```graphql query GetBooks { books { author title } } ``` And you should now see the "The Hobbit" in the list of books. πŸ₯ Try opening the Local Development Dashboard at [http://localhost:9400](http://localhost:9400/) and view the traces that were generated when calling your GraphQL API. ## [7\. Deploy](https://encore.dev/docs/ts/tutorials/graphql\#7-deploy) ### [Self-hosting](https://encore.dev/docs/ts/tutorials/graphql\#self-hosting) Encore supports building Docker images directly from the CLI, which can then be self-hosted on your own infrastructure of choice. If your app is using infrastructure resources, such as SQL databases, Pub/Sub, or metrics, you will need to supply a [runtime configuration](https://encore.dev/docs/ts/self-host/configure-infra) your Docker image. πŸ₯ Build a Docker image by running `encore build docker graphql:v1.0`. This will compile your application using the host machine and then produce a Docker image containing the compiled application. πŸ₯ Upload the Docker image to the cloud provider of your choice and run it. ### [Encore Cloud (free)](https://encore.dev/docs/ts/tutorials/graphql\#encore-cloud-free) Encore Cloud provides automated infrastructure and DevOps. Deploy to a free development environment or to your own cloud account on AWS or GCP. ### [Create account](https://encore.dev/docs/ts/tutorials/graphql\#create-account) Before deploying with Encore Cloud, you need to have a free Encore Cloud account and link your app to the platform. If you already have an account, you can move on to the next step. If you don’t have an account, the simplest way to get set up is by running `encore app create` and selecting **Y** when prompted to create a new account. Once your account is set up, continue creating a new app, selecting the `empty app` template. After creating the app, copy your project files into the new app directory, ensuring that you do not replace the `encore.app` file (this file holds a unique id which links your app to the platform). ### [Commit changes](https://encore.dev/docs/ts/tutorials/graphql\#commit-changes) The final step before you deploy is to commit all changes to the project repo. πŸ₯ Push your changes and deploy your application to Encore's free development cloud by running: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Encore will now build and test your app, provision the needed infrastructure, and deploy your application to the cloud. After triggering the deployment, you will see a URL where you can view its progress in the [Encore Cloud dashboard](https://app.encore.cloud/). It will look something like: `https://app.encore.cloud/$APP_ID/deploys/...` From there you can also see metrics, traces, link your app to a GitHub repo to get automatic deploys on new commits, and connect your own AWS or GCP account to use for production deployment. ### [Celebrate with fireworks](https://encore.dev/docs/ts/tutorials/graphql\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) ## [Conclusion](https://encore.dev/docs/ts/tutorials/graphql\#conclusion) We've now built a GraphQL API gateway that forwards requests to the application's underlying Encore services in a type-safe way with minimal boilerplate. Note that the concepts discussed here are general and can be easily adapted to any GraphQL schema. Whenever you make a change to the schema or configuration, re-run `npm run generate` to regenerate the resolver types. [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/tutorials/graphql.mdx) ## Slack Bot Tutorial Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) In this tutorial you will create a Slack bot that brings the greatness of the `cowsay` utility to Slack! ![Slack Cowsay](https://encore.dev/assets/docs/cowsay.png) This is the end result: ```ts 1 import { api } from "encore.dev/api"; 2 import { secret } from "encore.dev/config"; 3 import { createHmac, timingSafeEqual } from "node:crypto"; 4 import type { IncomingMessage } from "node:http"; 5 import type { IncomingHttpHeaders } from "http"; 6 7 // This uses Encore's built-in secrets manager, learn more: https://encore.dev/docs/ts/primitives/secrets 8 const slackSigningSecret = secret("SlackSigningSecret"); 9 10 // cowart is the formatting string for printing the cow art. 11 const cowart = (msg: string) => ` 12 \`\`\` 13 +-${"-".repeat(msg.length)}-+ 14 | ${msg} | 15 +-${"-".repeat(msg.length)}-+ 16 \\ __n__n__ 17 .------\`-\\00/-' 18 / ## ## (oo) 19 / \\## __ ./ 20 |//YY \\|/ 21 ||| ||| 22 \`\`\` 23 `; 24 25 export const cowsay = api.raw( 26 { expose: true, path: "/cowsay", method: "*" }, 27 async (req, resp) => { 28 const body = await getBody(req); 29 30 try { 31 await verifySignature(body, req.headers); 32 } catch (err) { 33 const e = err as Error; 34 resp.statusCode = 500; 35 resp.end(e.message); 36 return; 37 } 38 39 const text = new URLSearchParams(body).get("text"); 40 const msg = cowart(text || "Moo!"); 41 resp.setHeader("Content-Type", "application/json"); 42 resp.end(JSON.stringify({ response_type: "in_channel", text: msg })); 43 }, 44 ); 45 46 // Verifies the signature of an incoming request from Slack. 47 // https://github.com/slackapi/bolt-js/blob/main/src/receivers/verify-request.ts 48 const verifySignature = async function ( 49 body: string, 50 headers: IncomingHttpHeaders, 51 ) { 52 const requestTimestampSec = parseInt( 53 headers["x-slack-request-timestamp"] as string, 54 ); 55 const signature = headers["x-slack-signature"] as string; 56 if (Number.isNaN(requestTimestampSec)) { 57 throw new Error( 58 `Failed to verify authenticity: header x-slack-request-timestamp did not have the expected type (${requestTimestampSec})`, 59 ); 60 } 61 62 // Calculate time-dependent values 63 const nowMs = Date.now(); 64 const requestTimestampMaxDeltaMin = 5; 65 const fiveMinutesAgoSec = 66 Math.floor(nowMs / 1000) - 60 * requestTimestampMaxDeltaMin; 67 68 // Enforce verification rules 69 70 // Rule 1: Check staleness 71 if (requestTimestampSec < fiveMinutesAgoSec) { 72 throw new Error( 73 `Failed to verify authenticity: x-slack-request-timestamp must differ from system time by no more than ${requestTimestampMaxDeltaMin} minutes or request is stale`, 74 ); 75 } 76 77 // Rule 2: Check signature 78 // Separate parts of signature 79 const [signatureVersion, signatureHash] = signature.split("="); 80 // Only handle known versions 81 if (signatureVersion !== "v0") { 82 throw new Error(`Failed to verify authenticity: unknown signature version`); 83 } 84 // Compute our own signature hash 85 const hmac = createHmac("sha256", slackSigningSecret()); 86 hmac.update(`${signatureVersion}:${requestTimestampSec}:${body}`); 87 const ourSignatureHash = hmac.digest("hex"); 88 if ( 89 !signatureHash || 90 !timingSafeEqual( 91 Buffer.from(signatureHash, "utf8"), 92 Buffer.from(ourSignatureHash, "utf8"), 93 ) 94 ) { 95 throw new Error(`Failed to verify authenticity: signature mismatch`); 96 } 97 }; 98 99 // Extract the body from an incoming request. 100 function getBody(req: IncomingMessage): Promise { 101 return new Promise((resolve) => { 102 const bodyParts: any[] = []; 103 req 104 .on("data", (chunk) => { 105 bodyParts.push(chunk); 106 }) 107 .on("end", () => { 108 resolve(Buffer.concat(bodyParts).toString()); 109 }); 110 }); 111 } 112 ``` Project slack encore.service.ts slack.ts Encore encore.app package.json tsconfig.json Open on ##### Please note To make it easier to follow along, we've laid out a trail of croissants to guide your way. Whenever you see a πŸ₯ it means there's something for you to do. ## [1\. Create your Encore application](https://encore.dev/docs/ts/tutorials/slack-bot\#1-create-your-encore-application) πŸ₯ Create a new Encore application by running `encore app create` and select `Empty app` as the template. **Take a note of your app id, we'll need it in the next step.** ## [2\. Create a Slack app](https://encore.dev/docs/ts/tutorials/slack-bot\#2-create-a-slack-app) πŸ₯ The first step is to create a new Slack app: 1. Head over to [Slack's API site](https://api.slack.com/apps) and create a new app. 2. When prompted, choose to create the app **from an app manifest**. 3. Choose a workspace to install the app in. πŸ₯ Enter the following manifest (replace `$APP_ID` in the URL below with your app id from above): ```yaml _metadata: major_version: 1 display_information: name: Encore Bot description: Cowsay for the cloud age. features: slash_commands: - command: /cowsay # Replace $APP_ID below url: https://staging-$APP_ID.encr.app/cowsay description: Say things with a flair! usage_hint: your message here should_escape: false bot_user: display_name: encore-bot always_online: true oauth_config: scopes: bot: - commands - chat:write - chat:write.public settings: org_deploy_enabled: false socket_mode_enabled: false token_rotation_enabled: false ``` Once created, we're ready to move on with implementing our Encore endpoint! ## [3\. Implement the Slack endpoint](https://encore.dev/docs/ts/tutorials/slack-bot\#3-implement-the-slack-endpoint) Since Slack sends custom HTTP headers that we need to pay attention to, we're going to use a raw endpoint in Encore. For more information on this check out Slack's documentation on [Enabling interactivity with Slash Commands](https://api.slack.com/interactivity/slash-commands). πŸ₯ In your Encore app, create a directory named `slack` containing a file named `encore.service.ts`. ```bash $ mkdir slack $ touch slack/encore.service.ts ``` πŸ₯ Add the following code to `slack/encore.service.ts`: ```ts slack/encore.service.ts import { Service } from "encore.dev/service"; export default new Service("slack"); ``` This is how you create define services with Encore. Encore will now consider files in the `slack` directory and all its subdirectories as part of the `slack` service. πŸ₯ Create a file `slack/slack.ts` with the following contents: ```ts slack/slack.ts import { api } from "encore.dev/api"; import type { IncomingMessage } from "node:http"; // cowart is the formatting string for printing the cow art. const cowart = (msg: string) => `Moo! ${msg} `; export const cowsay = api.raw( { expose: true, path: "/cowsay", method: "*" }, async (req, resp) => { const body = await getBody(req); const text = new URLSearchParams(body).get("text"); const msg = cowart(text || "Moo!"); resp.setHeader("Content-Type", "application/json"); resp.end(JSON.stringify({ response_type: "in_channel", text: msg })); }, ); // Extract the body from an incoming request. function getBody(req: IncomingMessage): Promise { return new Promise((resolve) => { const bodyParts: any[] = []; req .on("data", (chunk) => { bodyParts.push(chunk); }) .on("end", () => { resolve(Buffer.concat(bodyParts).toString()); }); }); } ``` Let's try it out locally. πŸ₯ Start your app with `encore run` and then call it in another terminal: ```bash $ curl http://localhost:4000/cowsay -d 'text=Eat your greens!' {"response_type":"in_channel","text":"Moo! Eat your greens!"} ``` Looks great! πŸ₯ Next, let's deploy it to the cloud: ```bash $ git add -A . $ git commit -m 'Initial commit' $ git push encore ``` Once deployed, we're ready to try our Slack command! πŸ₯ Head over to the workspace you installed the app in and run `/cowsay Hello there`. You should see something like this: ![Cowsay](https://encore.dev/assets/docs/cowsay-wip.png) And just like that we have a fully working Slack integration. ## [4\. Secure the webhook endpoint](https://encore.dev/docs/ts/tutorials/slack-bot\#4-secure-the-webhook-endpoint) In order to get up and running quickly we ignored one important aspect for a production-ready Slack app: verifying that the webhook requests are actually coming from Slack. Let's do that now! The Slack documentation covers this really well on the [Verifying requests from Slack](https://api.slack.com/authentication/verifying-requests-from-slack) page. In short, what we need to do is: 1. Save a shared secret that Slack provides us 2. Use the secret to verify that the request comes from Slack, using HMAC (Hash-based Message Authentication Code). ### [Save the shared secret](https://encore.dev/docs/ts/tutorials/slack-bot\#save-the-shared-secret) Let's define a secret using Encore's secrets management functionality. πŸ₯ Add this to your `slack.ts` file: ```ts slack/slack.ts import { secret } from "encore.dev/config"; const slackSigningSecret = secret("SlackSigningSecret"); ``` πŸ₯ Head over to the configuration section for your Slack app (go to [Your Apps](https://api.slack.com/apps) β†’ select your app β†’ Basic Information). πŸ₯ Copy the **Signing Secret** and then run `encore secret set --type prod SlackSigningSecret` and paste the secret. πŸ₯ For development you will also want to set `encore secret set --type dev,local,pr SlackSigningSecret`. You can use the same secret value or a placeholder value. ### [Compute the HMAC](https://encore.dev/docs/ts/tutorials/slack-bot\#compute-the-hmac) TypeScript makes computing HMAC very straightforward, but it's still a fair amount of code. πŸ₯ Add a few more imports to your file, so that it reads: ```ts slack/slack.ts import { createHmac, timingSafeEqual } from "node:crypto"; import type { IncomingHttpHeaders } from "http"; ``` πŸ₯ Next, we'll add the `verifySignature` function: ```ts slack/slack.ts // Verifies the signature of an incoming request from Slack. const verifySignature = async function ( body: string, headers: IncomingHttpHeaders, ) { const requestTimestampSec = parseInt( headers["x-slack-request-timestamp"] as string, ); const signature = headers["x-slack-signature"] as string; if (Number.isNaN(requestTimestampSec)) { throw new Error( `Failed to verify authenticity: header x-slack-request-timestamp did not have the expected type (${requestTimestampSec})`, ); } // Calculate time-dependent values const nowMs = Date.now(); const requestTimestampMaxDeltaMin = 5; const fiveMinutesAgoSec = Math.floor(nowMs / 1000) - 60 * requestTimestampMaxDeltaMin; // Enforce verification rules // Rule 1: Check staleness if (requestTimestampSec < fiveMinutesAgoSec) { throw new Error( `Failed to verify authenticity: x-slack-request-timestamp must differ from system time by no more than ${requestTimestampMaxDeltaMin} minutes or request is stale`, ); } // Rule 2: Check signature // Separate parts of signature const [signatureVersion, signatureHash] = signature.split("="); // Only handle known versions if (signatureVersion !== "v0") { throw new Error(`Failed to verify authenticity: unknown signature version`); } // Compute our own signature hash const hmac = createHmac("sha256", slackSigningSecret()); hmac.update(`${signatureVersion}:${requestTimestampSec}:${body}`); const ourSignatureHash = hmac.digest("hex"); if ( !signatureHash || !timingSafeEqual( Buffer.from(signatureHash, "utf8"), Buffer.from(ourSignatureHash, "utf8"), ) ) { throw new Error(`Failed to verify authenticity: signature mismatch`); } }; ``` We're now ready to verify the signature. πŸ₯ Update the `cowsay` function to look like this: ```ts slack/slack.ts export const cowsay = api.raw( { expose: true, path: "/cowsay", method: "*" }, async (req, resp) => { const body = await getBody(req); try { await verifySignature(body, req.headers); } catch (err) { const e = err as Error; resp.statusCode = 500; resp.end(e.message); return; } const text = new URLSearchParams(body).get("text"); const msg = cowart(text || "Moo!"); resp.setHeader("Content-Type", "application/json"); resp.end(JSON.stringify({ response_type: "in_channel", text: msg })); }, ); ``` ## [5\. Put it all together and deploy](https://encore.dev/docs/ts/tutorials/slack-bot\#5-put-it-all-together-and-deploy) Finally we're ready to put it all together. πŸ₯ Add the `cowart` in `slack.ts` like so: ```ts slack/slack.ts const cowart = (msg: string) => ` \`\`\` +-${"-".repeat(msg.length)}-+ | ${msg} | +-${"-".repeat(msg.length)}-+ \\ __n__n__ .------\`-\\00/-' / ## ## (oo) / \\## __ ./ |//YY \\|/ ||| ||| \`\`\` `; ``` πŸ₯ Finally, let's commit our changes and deploy it: ```bash $ git add -A . $ git commit -m 'Verify webhook requests and improve art' $ git push encore ``` πŸ₯ Once deployed, head back to Slack and run `/cowsay Hello there`. If everything is set up correctly, you should see: ![Slack Cowsay](https://encore.dev/assets/docs/cowsay.png) And there we go, a production-ready Slack bot in less than 100 lines of code. Well done! ### [Celebrate with fireworks](https://encore.dev/docs/ts/tutorials/slack-bot\#celebrate-with-fireworks) Now that your app is running in the cloud, let's celebrate with some fireworks: πŸ₯ In the Cloud Dashboard, open the Command Menu by pressing **Cmd + K** (Mac) or **Ctrl + K** (Windows/Linux). _From here you can easily access all Cloud Dashboard features and for example jump straight to specific services in the Service Catalog or view Traces for specific endpoints._ πŸ₯ Type `fireworks` in the Command Menu and press enter. Sit back and enjoy the show! ![Fireworks](https://encore.dev/assets/docs/fireworks.jpg) [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) [App Structure](https://encore.dev/docs/ts/primitives/app-structure) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/tutorials/slack-bot.md) ## Encore App Structure Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore uses a monorepo design and it's best to use one Encore app for your entire backend application. This lets Encore build an application model that spans your entire app, necessary to get the most value out of many features like [distributed tracing](https://encore.dev/docs/ts/observability/tracing) and [Encore Flow](https://encore.dev/docs/ts/observability/flow). If you have a large application, see advice on how to [structure an app with several systems](https://encore.dev/docs/ts/primitives/app-structure#large-applications-with-several-systems). It's simple to integrate Encore applications with pre-existing systems you might have, using APIs and built-in tools like [client generation](https://encore.dev/docs/ts/cli/client-generation). See more on how to approach building new functionality incrementally with Encore in the [migrating to Encore](https://encore.dev/docs/platform/migration/migrate-to-encore) documentation. ## [Monolith or Microservices](https://encore.dev/docs/ts/primitives/app-structure\#monolith-or-microservices) Encore is not opinionated about monoliths vs. microservices. It does however let you build microservices applications with a monolith-style developer experience. For example, you automatically get IDE auto-complete when making [API calls between services](https://encore.dev/docs/ts/primitives/api-calls), along with cross-service type-safety. When creating a cloud environment on AWS/GCP, Encore enables you to configure if you want to combine multiple services into one process or keep them separate. This can be useful for improved efficiency at smaller scales, and for co-locating services for increased performance. Learn more in the [environments documentation](https://encore.dev/docs/platform/deploy/environments). ## [Defining services](https://encore.dev/docs/ts/primitives/app-structure\#defining-services) To create an Encore service, add a file named `encore.service.ts` in a directory. The file must export a service instance, by calling `new Service`, imported from `encore.dev/service`. For example: ```ts import { Service } from "encore.dev/service"; export default new Service("my-service"); ``` That's it! Encore will consider this directory and all its subdirectories as part of the service. Within the service, you can then [define APIs](https://encore.dev/docs/ts/primitives/defining-apis) and use infrastructure resources like querying databases. ##### Related docs [Services](https://encore.dev/docs/ts/primitives/services) [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) ## [Examples](https://encore.dev/docs/ts/primitives/app-structure\#examples) Let's take a look at a few different approaches to structuring your Encore application, depending on the size and complexity of your application. ### [Single-service application](https://encore.dev/docs/ts/primitives/app-structure\#single-service-application) The best place to start, especially if you're new to Encore, is by having a single service in your application. Once you've familiarized yourself with the Encore development model, it's easy to break it up into multiple services. The best way to do this is by defining the `encore.service.ts` in the root of your project, next to the `package.json` file. On disk it might look like this (but feel free to change as you see fit): ```go /my-app β”œβ”€β”€ package.json β”œβ”€β”€ encore.app β”œβ”€β”€ // ... other project files β”‚ β”œβ”€β”€ encore.service.ts // defines your service root β”œβ”€β”€ api.ts // API endpoints β”œβ”€β”€ db.ts // Database definition ``` Services can have subdirectories, so as the complexity of your service grows you can add subdirectories as you see fit, to better organize the code base. ### [Multi-service application (Distributed System)](https://encore.dev/docs/ts/primitives/app-structure\#multi-service-application-distributed-system) For larger applications it's often useful to break it apart into multiple services. This helps improve reliability, scalability, and lead to clearer code organization. Encore makes it easy to structure your application as multiple services. Just like before, you add an `encore.service.ts` file to mark a directory (and its subdirectories) as a service. ##### Please note Note that services cannot be nested: each must be defined in its own directory, and cannot live in a subdirectory of another service. If you have a single-service project with a `encore.service.ts` file at the top-level directory of your project, and you want to break it apart, start by moving that service code into a subdirectory. On disk it might look like this: ```arduino /my-app β”œβ”€β”€ encore.app // ... and other top-level project files β”‚ β”œβ”€β”€ hello // hello service (directory) β”‚Β Β  β”œβ”€β”€ migrations // hello service db migrations (directory) β”‚Β Β  β”‚ └── 1_create_table.up.sql // hello service db migration β”‚Β Β  β”œβ”€β”€ encore.service.ts // hello service definition β”‚Β Β  β”œβ”€β”€ hello.ts // hello service APIs β”‚Β Β  └── hello_test.ts // tests for hello service β”‚ └── world // world service (directory) β”‚Β Β  β”œβ”€β”€ encore.service.ts // world service definition └── world.ts // world service APIs ``` ### [Large applications with several systems](https://encore.dev/docs/ts/primitives/app-structure\#large-applications-with-several-systems) If you have a large application with several logical domains, each consisting of multiple services, it can be practical to separate these into distinct systems. Systems are not a special construct in Encore, they only help you divide your application logically around common concerns and purposes. Encore only handles services, the compiler will read your systems and extract the services of your application. As applications grow, systems help you decompose your application without requiring any complex refactoring. To create systems, simply create a sub-directory for each system and put the relevant service packages within it. As an example, a company building a Trello app might divide their application into three systems: the **Trello** system (for the end-user facing app with boards and cards), the **User** system (for user and organization management), and the **Premium** system (for handling payments and subscriptions). On disk it might look like this: ```go /my-trello-clone β”œβ”€β”€ encore.app β”œβ”€β”€ package.json // ... and other top-level project files β”‚ β”œβ”€β”€ trello // trello system (a directory) β”‚Β Β  β”œβ”€β”€ board // board service (a directory) β”‚Β Β  β”‚ β”œβ”€β”€ encore.service.ts // service definition β”‚Β Β  β”‚ └── board.ts // service code β”‚Β Β  β”‚ β”‚Β Β  └── card // card service (a directory) β”‚Β Β  β”œβ”€β”€ encore.service.ts // service definition β”‚ └── card.ts // service code β”‚ β”œβ”€β”€ premium // premium system (a directory) β”‚Β Β  β”œβ”€β”€ payment // payment service (a directory) β”‚Β Β  β”‚ β”œβ”€β”€ encore.service.ts // service definition β”‚Β Β  β”‚ └── payment.ts // service code β”‚Β Β  β”‚ β”‚Β Β  └── subscription // subscription service (a directory) β”‚Β Β  β”œβ”€β”€ encore.service.ts // service definition β”‚ └── subscription.ts // service code β”‚ └── usr // usr system (a directory) β”œβ”€β”€ org // org service (a directory) β”‚Β Β  β”œβ”€β”€ encore.service.ts // service definition β”‚Β Β  └── org.ts // service code β”‚ └── user // user service (a directory) Β Β  β”œβ”€β”€ encore.service.ts // service definition Β Β  └── user.ts // service code ``` The only refactoring needed to divide an existing Encore application into systems is to move services into their respective subfolders. This is a simple way to separate the specific concerns of each system. What matters for Encore are the packages containing services, and the division in systems or subsystems will not change the endpoints or architecture of your application. ## [Package Management](https://encore.dev/docs/ts/primitives/app-structure\#package-management) For Encore.ts projects, using a single root-level `package.json` file (monorepo approach) is the recommended practice. It has several benefits: - Ensures consistent dependency versions across your services - Simplifies TypeScript configuration management - Makes it easier to share common types and utilities - Reduces npm install overhead - Works seamlessly with TypeScript's project references Encore.ts also supports separate `package.json` files in sub-packages, with the following limitations: - The Encore.ts application must use one package with a single `package.json` file - Other separate packages must be pre-transpiled to JavaScript Further package management options are planned for the future, particularly for supporting automatically transpiling and bundling workspace packages. [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) [Services](https://encore.dev/docs/ts/primitives/services) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/primitives/app-structure.md) ## Encore Services Guide Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore.ts makes it simple to build applications with one or many services, without needing to manually handle the typical complexity of developing microservices. ## [Defining services](https://encore.dev/docs/ts/primitives/services\#defining-services) To create an Encore service, add a file named `encore.service.ts` in a directory. The file must export a service instance, by calling `new Service`, imported from `encore.dev/service`. For example: ```ts import { Service } from "encore.dev/service"; export default new Service("my-service"); ``` That's it! Encore will consider this directory and all its subdirectories as part of the service. With multiple services, each service lives in its own directory with its own `encore.service.ts`: ```go /my-app β”œβ”€β”€ package.json β”œβ”€β”€ encore.app β”‚ β”œβ”€β”€ hello // hello service β”‚ β”œβ”€β”€ encore.service.ts β”‚ └── hello.ts β”‚ └── world // world service β”œβ”€β”€ encore.service.ts └── world.ts ``` For more on how to structure your application, see the [app structure guide](https://encore.dev/docs/ts/primitives/app-structure). [App Structure](https://encore.dev/docs/ts/primitives/app-structure) [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/primitives/services.mdx) ## Defining TypeScript APIs Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) - [Validation](https://encore.dev/docs/ts/primitives/validation) - [API Calls](https://encore.dev/docs/ts/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints) - [GraphQL](https://encore.dev/docs/ts/primitives/graphql) - [Streaming APIs](https://encore.dev/docs/ts/primitives/streaming-apis) - [API Errors](https://encore.dev/docs/ts/primitives/errors) - [Static Assets](https://encore.dev/docs/ts/primitives/static-assets) - [Cookies](https://encore.dev/docs/ts/primitives/cookies) - [Types](https://encore.dev/docs/ts/primitives/types) - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore.ts simplifies creating type-safe, idiomatic TypeScript API endpoints and provides built-in validation for incoming requests. At their core, APIs in Encore.ts are normal `async` functions with request and response data types defined as TypeScript interfaces, which Encore.ts uses to encode API requests to HTTP messages. Encore.ts also parses your source code to understand the request and response schema of each endpoint, automatically handling validation of incoming requests against your schema. ## [Defining API endpoints](https://encore.dev/docs/ts/primitives/defining-apis\#defining-api-endpoints) To define an API endpoint, use the `api` function from the `encore.dev/api` module. This function wraps a regular TypeScript async function, designating it as an API endpoint. Encore.ts then generates the necessary boilerplate at compile-time. In the example below, we define the API endpoint `ping` which accepts `POST` requests and is exposed as `hello.ping` (because our service name is `hello`). ```typescript // inside the hello.ts file import { api } from "encore.dev/api"; export const ping = api( { method: "POST" }, async (p: PingParams): Promise => { return { message: `Hello ${p.name}!` }; }, ); ``` ### [Exposing API endpoints to the outside world](https://encore.dev/docs/ts/primitives/defining-apis\#exposing-api-endpoints-to-the-outside-world) When you define an API, by default it is not exposed to the outside world, and it can only be called by other APIs within the same Encore application. To expose an API to the internet, add the `expose: true` field to the options object passed in as the first argument to `api`. - `{ expose: false }` – defines a private API that is never accessible to the outside world. It can only be called from other services in your app and via cron jobs. This is default value if the `expose` field isn't set. - `{ expose: true }` – defines a public API that anybody on the internet can call ### [Requiring authentication data](https://encore.dev/docs/ts/primitives/defining-apis\#requiring-authentication-data) To require authentication for an API endpoint, add `auth: true` to the API options. With this option, Encore will first call the authentication handler you've defined to validate the authentication of incoming requests. Setting `auth: true` can also be useful for internal APIs that aren't exposed to the internet. In that case, it means that the internal caller must have valid authentication data associated with its request. Finally, even if an API endpoint does not specify `auth: true`, it will still receive any authentication data that was provided. For more information on defining APIs that require authentication, see the [authentication guide](https://encore.dev/docs/ts/develop/auth). ## [API Schemas](https://encore.dev/docs/ts/primitives/defining-apis\#api-schemas) ### [Request and response schemas](https://encore.dev/docs/ts/primitives/defining-apis\#request-and-response-schemas) In the example above we defined an API that uses request and response schemas, where the request data is of type `Params` and the response data of type `Response`. That means we need to define them like so: ```ts hello.ts import { api } from "encore.dev/api"; // PingParams is the request data for the Ping endpoint. interface PingParams { name: string; } // PingResponse is the response data for the Ping endpoint. interface PingResponse { message: string; } // hello is an API endpoint that responds with a simple response. export const hello = api( { method: "POST", path: "/hello" }, async (p: PingParams): Promise => { return { message: `Hello ${p.name}!` }; }, ); ``` Request and response schemas are both optional. There are four different ways of defining an API: **Using both request and response data:** `api({ ... }, async (params: Params): Promise => {});` **Only returning a response:** `api({ ... }, async (): Promise => {});` **With only request data:** `api({ ... }, async (params: Params): Promise => {});` **Without any request or response data:** `api({ ... }, async (): Promise => {});` Alternatively, you can express these using type parameters, since `api` is a generic function: **Using both request and response data:** `api({ ... }, async (params) => {});` **Only returning a response:** `api({ ... }, async () => {});` **With only request data:** `api({ ... }, async (params) => {});` **Without any request or response data:** `api({ ... }, async () => {});` ### [Customizing request and response encoding](https://encore.dev/docs/ts/primitives/defining-apis\#customizing-request-and-response-encoding) Encore parses the source code to understand the request and response schema of each endpoint. By default, the data is parsed as a JSON body for incoming requests, and written back as JSON responses. This can be customized on a per-field basis, allowing individual fields to be parsed from query strings and HTTP headers with ease. This is done by using the `Header` and `Query` types defined in the `encore.dev/api` module. ### [Headers](https://encore.dev/docs/ts/primitives/defining-apis\#headers) Headers are defined by setting the field type to `Header<"Name-Of-Header">`. It can be used in both request and response data types. In the example below, the `language` field will be fetched from the `Accept-Language` HTTP header. ```ts import { Header } from "encore.dev/api"; interface Params { language: Header<"Accept-Language">; // parsed from header author: string; // not a header } ``` ### [Query parameters](https://encore.dev/docs/ts/primitives/defining-apis\#query-parameters) For `GET`, `HEAD` and `DELETE` requests, parameters are read from the query string by default, since those HTTP methods do not support request bodies. For other HTTP methods (that do support request bodies), parameters are by default read from the HTTP request body as JSON. In those cases, the `Query` type can be used to specify that a field should be parsed from the query string instead. Query strings are not supported in HTTP responses, and are treated as being part of the HTTP response body in JSON. In the example below, the `limit` field will be read from the `limit` query parameter for all HTTP methods, whereas the `author` field will be parsed from the query string only if the method of the request is `GET`, `HEAD` or `DELETE` (and otherwise from the HTTP request body as JSON). ```ts interface Params { limit: Query; // always a query parameter author: string; // query if GET, HEAD or DELETE, otherwise body parameter } ``` ### [Path parameters](https://encore.dev/docs/ts/primitives/defining-apis\#path-parameters) Path parameters are specified by the `path` field in the API Options in `api` call. To specify a placeholder variable, use `:name` and add a function parameter with the same name to the function signature. Encore parses the incoming request URL and makes sure it matches the type of the parameter. The last segment of the path can be parsed as a wildcard parameter by using `*name` with a matching function parameter. Each path parameter (whether a single segment like `:name` or a wildcard parameter like `*name`) must have a matching field in the request data type. For example: ```ts // Retrieves a blog post by its id. export const getBlogPost = api( { method: "GET", path: "/blog/:id/*path" }, async (params: {id: number; path: string}): Promise { // Use id and path to query database... } ) ``` ### [Cookie parameters](https://encore.dev/docs/ts/primitives/defining-apis\#cookie-parameters) Cookies are defined by setting the field type to `Cookie<"Name-Of-Cookie">`. It can be used in both request and response data types. In the example below we define an optional field that will be parsed from the cookie header. ```ts import { Cookie } from "encore.dev/api"; interface Params { settings?: Cookie<"settings">; // parsed from cookie header } ``` Read more about cookies [here](https://encore.dev/docs/ts/primitives/cookies) ### [Fallback routes](https://encore.dev/docs/ts/primitives/defining-apis\#fallback-routes) Encore supports defining fallback routes that will be called if no other endpoint matches the request, using the syntax `path: "/!fallback"`. This is often useful when migrating an existing backend service over to Encore, as it allows you to gradually migrate endpoints over to Encore while routing the remaining endpoints to the existing HTTP router using a raw endpoint with a fallback route. For example: ```ts // Route all requests to the existing HTTP router if no other endpoint matches. export const fallback = api.raw( { expose: true, method: "*", path: "/!path" }, async (req, resp) { // Call old router } ) ``` ## [Custom HTTP status codes](https://encore.dev/docs/ts/primitives/defining-apis\#custom-http-status-codes) By default, Encore automatically sets appropriate HTTP status codes for your API responses. We recommend using these default status codes, but there are situations where you might need to set a custom HTTP status code, such as when porting an existing API that clients depend on for specific status codes. To set a custom HTTP status code, include an `HttpStatus` field in your response interface: ```ts import { api, HttpStatus } from "encore.dev/api"; interface Response { msg: string; status: HttpStatus; } export const endpoint = api( { expose: true, method: "GET", path: "/path" }, async (): Promise => { return { msg: "Hello", status: HttpStatus.Created }; } ); ``` The `HttpStatus` enum includes all standard HTTP status codes like `HttpStatus.OK`, `HttpStatus.Created`, `HttpStatus.BadRequest`, etc. ## [Raw endpoints](https://encore.dev/docs/ts/primitives/defining-apis\#raw-endpoints) In case you need to operate at a lower abstraction level, Encore supports defining raw endpoints that let you access the underlying HTTP request. This is often useful for things like accepting webhooks. Learn more in the [raw endpoints guide](https://encore.dev/docs/ts/primitives/raw-endpoints). ## [Sensitive data](https://encore.dev/docs/ts/primitives/defining-apis\#sensitive-data) When handling sensitive information like API keys, passwords, or personally identifiable information (PII), you may want to prevent these details from appearing in traces. Encore provides the `sensitive` option for API endpoints for this purpose. To mark an endpoint as sensitive, add `sensitive: true` to the API options: ```typescript export const processPayment = api( { expose: true, method: "POST", path: "/payments", sensitive: true }, async (params: PaymentParams): Promise => { return { /* ... */ }; } ); ``` When `sensitive: true` is set, Encore automatically redacts all request and response payloads and excludes HTTP headers from traces. [Services](https://encore.dev/docs/ts/primitives/services) [Validation](https://encore.dev/docs/ts/primitives/validation) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/primitives/defining-apis.mdx) ## API Request Validation Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) - [Validation](https://encore.dev/docs/ts/primitives/validation) - [API Calls](https://encore.dev/docs/ts/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints) - [GraphQL](https://encore.dev/docs/ts/primitives/graphql) - [Streaming APIs](https://encore.dev/docs/ts/primitives/streaming-apis) - [API Errors](https://encore.dev/docs/ts/primitives/errors) - [Static Assets](https://encore.dev/docs/ts/primitives/static-assets) - [Cookies](https://encore.dev/docs/ts/primitives/cookies) - [Types](https://encore.dev/docs/ts/primitives/types) - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) When receiving incoming requests it's best practice to validate the payload to ensure it meets your expectations and includes all required fields. Encore.ts has request validation built in, designed to work seamlessly with TypeScript. It uses the natural TypeScript types directly to validate incoming requests, so you get the best of both worlds: the clean, concise TypeScript syntax, and runtime schema validation. This means your APIs are type-safe during both runtime and compile-time. Encore.ts makes it easy to define API endpoints that combine data from different sources: some fields from the request body, others from the query parameters, and yet others from the HTTP headers. It looks like this: ```ts import { Header, Query, api } from "encore.dev/api"; interface Request { // Optional query parameter. Parsed from the request URL. limit?: Query; // Custom header that must be set. Parsed from the HTTP headers. myHeader: Header<"X-My-Header">; // Required enum. Parsed from the request body. type: "sprocket" | "widget"; } export const myEndpoint = api( { expose: true, method: "POST", path: "/api" }, async ({ limit, myHeader, type }) => { // ... }, ); ``` In the above example, if a request to the endpoint is missing the `X-My-Header` HTTP header or the `type` field in the request body, Encore will return a `400` Bad Request response. The `limit` query parameter is optional and will be either a number or `undefined` in the endpoint handler function. ## [Supported validation types](https://encore.dev/docs/ts/primitives/validation\#supported-validation-types) ### [String fields](https://encore.dev/docs/ts/primitives/validation\#string-fields) ```ts interface Schema { name: string; } ``` ### [Number fields](https://encore.dev/docs/ts/primitives/validation\#number-fields) The `number` type will accept both int and float values: ```ts interface Schema { age: number; } ``` ### [Boolean fields](https://encore.dev/docs/ts/primitives/validation\#boolean-fields) ```ts interface Schema { isHuman: boolean; } ``` ### [Array fields](https://encore.dev/docs/ts/primitives/validation\#array-fields) You can define fields wit array values of `string`, `number`, `boolean`, `null`: ```ts interface Schema { strings: string[]; numbers: number[]; booleans: boolean[]; nulls: null[]; } ``` You can have an array of objects: ```ts interface Schema { users: { name: string; age: number }[]; } ``` It is also possible to have arrays of multiple types: ```ts interface Schema { values: (string | number)[]; } ``` ### [Enum fields](https://encore.dev/docs/ts/primitives/validation\#enum-fields) String enums can be used in the request schema to validate that a field is one of a set of predefined values. For example: ```ts interface Schema { type: "BLOG_POST" | "COMMENT" } ``` You can also use TypeScript enums: ```ts enum PostType { BlogPost = "BLOG_POST", Comment = "COMMENT" } interface Schema { type: PostType, } ``` The two above examples are equivalent. ### [Optional fields](https://encore.dev/docs/ts/primitives/validation\#optional-fields) You can make a field optional by adding a `?` after the field name: ```ts interface Request { name?: string; } export const myEndpoint = api( { expose: true, method: "POST", path: "/body" }, async (req: Request) => { // req.name is a string or undefined }, ); ``` Request will reach the endpoint handler even if the `name` field is missing. If the field is missing, the value will be `undefined`. ### [Nullable fields](https://encore.dev/docs/ts/primitives/validation\#nullable-fields) You can make a field nullable by using the `| null` type: ```ts interface Request { name: string | null; } export const myEndpoint = api( { expose: true, method: "POST", path: "/body" }, async (req: Request) => { // req.name is a string or null }, ); ``` ### [Union fields](https://encore.dev/docs/ts/primitives/validation\#union-fields) You can define a field that can be one of several types by using a union type: ```ts interface Request { value: string | number | boolean; } export const myEndpoint = api( { expose: true, method: "POST", path: "/body" }, async (req: Request) => { // req.value is a string, number, or boolean }, ); ``` ### [Reference schema](https://encore.dev/docs/ts/primitives/validation\#reference-schema) ```ts interface Schema { str: string; // String int: number; // Number list: number[]; // Array of numbers listOfTypes: (number | string )[]; // Array multiple types nullable: number | null; // Nullable maybe?: string; // Optional multiple: boolean | number | string | { name: string }; // Union enum: "John" | "Foo"; // Enum } ``` ## [Value based validation rules](https://encore.dev/docs/ts/primitives/validation\#value-based-validation-rules) Use Encore.ts's composable value based validation for use cases like checking the format of an email address or the length of a string. It uses TypeScript's type system and allows you to define validation rules directly in your type definitions. ### [Built-in validation Rules](https://encore.dev/docs/ts/primitives/validation\#built-in-validation-rules) - `Min` / `Max`: Validate minimum/maximum values for numbers - `MinLen` / `MaxLen`: Validate minimum/maximum lengths for strings and arrays - `IsURL` / `IsEmail`: Validate that a string is a URL or email address - `StartsWith` / `EndsWith` / `MatchesRegexp`: Validate that a string matches a specific pattern ### [Examples](https://encore.dev/docs/ts/primitives/validation\#examples) Import validation rules from the `encore.dev/validate` package: ```ts import { Min, Max, MinLen, MaxLen, IsEmail, IsURL } from "encore.dev/validate"; interface Schema { // Number between 3 and 1000 (inclusive) count: number & (Min<3> & Max<1000>); // String between 5 and 20 characters username: string & (MinLen<5> & MaxLen<20>); // Must be either a valid URL or email address contact: string & (IsURL | IsEmail); // Array of up to 10 email addresses recipients: Array & MaxLen<10>; } ``` ### [Combining Rules](https://encore.dev/docs/ts/primitives/validation\#combining-rules) You can combine multiple validation rules using: - `&` (and) to require all rules to pass - `|` (or) to require at least one rule to pass ```ts interface Schema { // Must be both >= 3 and <= 1000 count: number & (Min<3> & Max<1000>); // Must be either a URL or an email contact: string & (IsURL | IsEmail); } ``` ### [Performance](https://encore.dev/docs/ts/primitives/validation\#performance) These validation rules are executed directly in Rust at runtime, before the request reaches your JavaScript code. This provides excellent performance while maintaining type safety at both compile-time and runtime. ## [Body](https://encore.dev/docs/ts/primitives/validation\#body) By default, the data is parsed as a JSON body for incoming requests: ```ts interface Request { name: string; // Parsed from the JSON body } export const myEndpoint = api( { expose: true, method: "POST", path: "/body" }, async (req) => { // req.name is a string }, ); ``` Here, `name` is a required field in the request body. If the request body is missing the `name` field, Encore will return a `400` Bad Request response. ## [Query](https://encore.dev/docs/ts/primitives/validation\#query) For HTTP methods that support request bodies, parameters are by default read from the HTTP request body as JSON. In those cases, the `Query` type can be used to specify that a field should be parsed from the query string instead. ```typescript import { api, Query } from "encore.dev/api"; interface Schema { query: Query; // this will be parsed from the '?query=...' parameter in the request url } // A simple API endpoint that echoes the data back. export const echo = api( { method: "POST", path: "/example" }, async (params: Schema) => { // params.query is a string }, ); ``` This API endpoint expects incoming requests to look like this: ```output POST /example?query=hello HTTP/1.1 Content-Type: application/json ``` For `GET`, `HEAD` and `DELETE` requests, parameters are read and validated from the query string by default, since those HTTP methods do not support request bodies. For those methods, the `Query` type is not necessary: ```typescript import { Query } from "encore.dev/api"; interface Schema { limit: Query; // always a query parameter author: string; // query if GET, HEAD or DELETE, otherwise body parameter } ``` ### [Nested query fields](https://encore.dev/docs/ts/primitives/validation\#nested-query-fields) Using the `Query` type as a nested fields has no effect: ```typescript import { api, Query } from "encore.dev/api"; interface Data { query: Query; // this will be parsed from the '?query=...' parameter in the request url nested: { query2: Query; // Query has no effect inside nested fields }; } export const echo = api( { method: "POST", path: "/nested" }, async (params: Data) => { // ... }, ); ``` Nested query params will be sent as part of the JSON body. The above endpoint expects incoming requests to look like this: ```output POST /nested HTTP/1.1 Content-Type: application/json { "nested": { "query2": "not a query string" } } ``` ## [Headers](https://encore.dev/docs/ts/primitives/validation\#headers) Request headers are defined and validated by setting the field type to `Header<"Name-Of-Header">`. It can be used in both request and response data types. In the example below, the `language` field will be fetched from the `Accept-Language` HTTP header. If the request is missing the `Accept-Language` header, Encore will return a `400` Bad Request response. ```ts import { Header } from "encore.dev/api"; interface Params { language: Header<"Accept-Language">; // parsed from header author: string; // not a header } ``` ### [Nested header fields](https://encore.dev/docs/ts/primitives/validation\#nested-header-fields) Using the `Header` type as a nested fields has no effect: ```typescript import { api, Header } from "encore.dev/api"; interface Data { header: Header<"X-Header">; // this field will be read from the http header nested: { header2: Header<"X-Other-Header">; // Header has no effect inside nested fields }; } // A simple API endpoint that echoes the data back. export const echo = api( { method: "POST", path: "/nested" }, async (params: Data) => { // ... }, ); ``` Nested headers will be sent as part of the JSON body. The above endpoint expects incoming requests to look like this: ```output POST /nested HTTP/1.1 Content-Type: application/json X-Header: this is a header { "nested": { "header2": "not a header", } } ``` ## [Params](https://encore.dev/docs/ts/primitives/validation\#params) Dynamic path parameters are also defined in the request schema. The parameter will be parsed from the request URL and made available in the request object: ```ts import { api } from "encore.dev/api"; interface Request { // Required path parameter. Parsed from the request URL. id: string; } export const myEndpoint = api( { expose: true, method: "POST", path: "/user/:id" }, async ({ id }: Request) => { // ... }, ); ``` You can also use the `number` type for path parameters: ```ts interface Request { id: number; } ``` Encore.ts will then try to parse the path parameter as a number. If the path parameter is not a valid number, Encore will return a `400` Bad Request response. ## [Combining sources](https://encore.dev/docs/ts/primitives/validation\#combining-sources) You can combine data from different sources in the same request schema. For example, you can have fields that are parsed from the request body, others from the query parameters, and yet others from the HTTP headers. It looks like this: ```ts import { Header, Query, api } from "encore.dev/api"; interface Request { // Required path parameter. Parsed from the request URL. id: number; // Optional query parameter. Parsed from the request URL. limit?: Query; // Custom header that must be set. Parsed from the HTTP headers. myHeader: Header<"X-My-Header">; // Required enum. Parsed from the request body. type: "sprocket" | "widget"; } export const myEndpoint = api( { expose: true, method: "POST", path: "/user/:id" }, async ({ id, limit, myHeader, type }: Request) => { // ... }, ); ``` ## [Errors](https://encore.dev/docs/ts/primitives/validation\#errors) If the validation is not successful, Encore will return a `400` Bad Request response with a JSON body that contains the error message: ```output HTTP/1.1 400 Bad Request { "code": "invalid_argument", "message": "unable to decode request body", "internal_message": "Error(\"missing field name\", line: 1, column: 18)" } ``` ## [Response](https://encore.dev/docs/ts/primitives/validation\#response) Encore.ts will not perform runtime validation for response data, but you will get compilation errors if you try to return a value that does not match the expected response type. ### [Reusing the request type as the response type](https://encore.dev/docs/ts/primitives/validation\#reusing-the-request-type-as-the-response-type) You often want to return the same data type that you received in a request. In this case, you can reuse the request type as the response type: ```typescript import { api, Header, Query } from "encore.dev/api"; interface Data { header: Header<"X-Header">; // this field will be read from the http header query: Query; // this will be parsed from the '?query=...' parameter in the request url body: string; // this will be sent as part of the JSON body } // A simple API endpoint that echoes the data back. export const echo = api( { method: "POST", path: "/echo" }, async (params: Data): Promise => { return params; // echo the data back }, ); ``` This API endpoint expects incoming requests to look like this: ```output POST /echo?query=hello HTTP/1.1 Content-Type: application/json X-Header: this is a header { "body": "a body", } ``` For HTTP responses the `Query` type is considered to be part of the JSON response body, since query strings only make sense for incoming requests. Responses returned from this endpoint will be serialized as a HTTP response to looks like this: ```output HTTP/1.1 200 OK Content-Type: application/json X-Header: this is a header { "query": "hello", "body": "a body", } ``` ## [Under the hood](https://encore.dev/docs/ts/primitives/validation\#under-the-hood) Encore.ts parses your source code to understand the request and response schema that each API endpoint expects, including things like HTTP headers, query parameters, and so on. The schemas are then processed, optimized, and stored as a Protobuf file. When the Encore.ts Rust runtime starts up, it reads the Protobuf file and pre-computes a request decoder and response encoder, optimized for each API endpoint, using the exact type definition each API endpoint expects. In fact, Encore.ts even handles request validation directly in Rust, ensuring invalid requests never have to even touch the JavaScript layer, mitigating many denial of service attacks. Encore’s understanding of the request schema also improves performance. JavaScript runtimes like Deno and Bun use a similar architecture (in fact, Deno also uses Rust+Tokio+Hyper), but lack Encore’s understanding of the request schema. As a result, they need to hand over the un-processed HTTP requests to the single-threaded JavaScript engine for execution. Encore.ts, on the other hand, handles much more of the request processing inside Rust, and only hands over the decoded request objects. By handling much more of the request life-cycle in multi-threaded Rust, the JavaScript event-loop is freed up to focus on executing application business logic instead of parsing HTTP requests, resulting in a significant performance improvement. [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) [API Calls](https://encore.dev/docs/ts/primitives/api-calls) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/primitives/validation.mdx) ## API Calls Overview Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) - [Validation](https://encore.dev/docs/ts/primitives/validation) - [API Calls](https://encore.dev/docs/ts/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints) - [GraphQL](https://encore.dev/docs/ts/primitives/graphql) - [Streaming APIs](https://encore.dev/docs/ts/primitives/streaming-apis) - [API Errors](https://encore.dev/docs/ts/primitives/errors) - [Static Assets](https://encore.dev/docs/ts/primitives/static-assets) - [Cookies](https://encore.dev/docs/ts/primitives/cookies) - [Types](https://encore.dev/docs/ts/primitives/types) - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Calling API endpoints between services, i.e. service-to-service calls, looks like regular function calls with Encore.ts. This gives you a simple monolith-like developer experience, even when you use multiple services. The only thing you need to do is import the service you want to call from `~encore/clients` and then call its API endpoints like functions. This works because, when compiling your application, Encore uses [static analysis](https://encore.dev/docs/ts/concepts/application-model) to parse all APIs and make them available through the `~encore/clients` module for internal calls. 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. ### [Example](https://encore.dev/docs/ts/primitives/api-calls\#example) In the example below, we import the service `hello` and call the `ping` endpoint using a function call to `hello.ping`. ```typescript import { hello } from "~encore/clients"; // import 'hello' service export const myOtherAPI = api({}, async (): Promise => { const resp = await hello.ping({ name: "World" }); console.log(resp.message); // "Hello World!" }); ``` ### [Service client references](https://encore.dev/docs/ts/primitives/api-calls\#service-client-references) Encore uses static analysis to determine which services are calling each other. This means service client objects can't be passed around however you like, as it makes static analysis impossible in many cases. To simplify your workflow, given these restrictions, Encore supports defining a "reference" to a service client that can be passed around any way you want. To create a reference, call the `.ref()` method on any service client: ```typescript import { hello } from "~encore/clients"; // Create a reference that can be passed around const helloRef = hello.ref(); // Pass the reference to other functions function doSomething(client: typeof helloRef) { return client.ping({ name: "World" }); } doSomething(helloRef); ``` ##### Related example [encoredev/examples/simple-event-driven](https://github.com/encoredev/examples/tree/main/ts/simple-event-driven) Simple microservices example application with service-to-service API calls. `$ encore app create --example=ts/simple-event-driven` [Validation](https://encore.dev/docs/ts/primitives/validation) [Raw Endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/primitives/api-calls.mdx) ## Raw API Endpoints Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) - [Validation](https://encore.dev/docs/ts/primitives/validation) - [API Calls](https://encore.dev/docs/ts/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints) - [GraphQL](https://encore.dev/docs/ts/primitives/graphql) - [Streaming APIs](https://encore.dev/docs/ts/primitives/streaming-apis) - [API Errors](https://encore.dev/docs/ts/primitives/errors) - [Static Assets](https://encore.dev/docs/ts/primitives/static-assets) - [Cookies](https://encore.dev/docs/ts/primitives/cookies) - [Types](https://encore.dev/docs/ts/primitives/types) - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Sometimes you need to operate a lower abstraction than Encore.ts normally provides. For example, you might want to access the underlying HTTP request, often useful for things like accepting webhooks. Encore.ts has you covered using "raw endpoints". To define a raw endpoint, use the `api.raw` function. It works similarly to `api`, but does not accept a request and response schema. Instead, it works like the Node.js `http` module and `Express.js`, where the function receives two parameters: a request object and a response writer. It looks like this: ```ts import { api } from "encore.dev/api"; export const myRawEndpoint = api.raw( { expose: true, path: "/raw", method: "GET" }, async (req, resp) => { resp.writeHead(200, { "Content-Type": "text/plain" }); resp.end("Hello, raw world!"); }, ); ``` It can be called like so: ```bash $ curl http://localhost:4000/raw Hello, raw world! ``` ##### Related example [encoredev/examples/slack-bot](https://github.com/encoredev/examples/tree/main/ts/slack-bot) Slack Bot example application that uses Raw endpoints to accept webhooks. `$ encore app create --example=ts/slack-bot` ##### Related docs [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) [API Calls](https://encore.dev/docs/ts/primitives/api-calls) [GraphQL](https://encore.dev/docs/ts/primitives/graphql) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/primitives/raw-endpoints.mdx) ## GraphQL API Guide Get started - [Installation](https://encore.dev/docs/ts/install) - [Quick Start](https://encore.dev/docs/ts/quick-start) - [AI Integration](https://encore.dev/docs/ts/ai-integration) - [FAQ](https://encore.dev/docs/ts/faq) Concepts - [Benefits](https://encore.dev/docs/ts/concepts/benefits) - [Application Model](https://encore.dev/docs/ts/concepts/application-model) - [Hello World](https://encore.dev/docs/ts/concepts/hello-world) Tutorials - [Building a REST API](https://encore.dev/docs/ts/tutorials/rest-api) - [Building an Uptime Monitor](https://encore.dev/docs/ts/tutorials/uptime) - [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) - [Building a Slack bot](https://encore.dev/docs/ts/tutorials/slack-bot) Primitives - [App Structure](https://encore.dev/docs/ts/primitives/app-structure) - [Services](https://encore.dev/docs/ts/primitives/services) - APIs - [Defining APIs](https://encore.dev/docs/ts/primitives/defining-apis) - [Validation](https://encore.dev/docs/ts/primitives/validation) - [API Calls](https://encore.dev/docs/ts/primitives/api-calls) - [Raw Endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints) - [GraphQL](https://encore.dev/docs/ts/primitives/graphql) - [Streaming APIs](https://encore.dev/docs/ts/primitives/streaming-apis) - [API Errors](https://encore.dev/docs/ts/primitives/errors) - [Static Assets](https://encore.dev/docs/ts/primitives/static-assets) - [Cookies](https://encore.dev/docs/ts/primitives/cookies) - [Types](https://encore.dev/docs/ts/primitives/types) - [Databases](https://encore.dev/docs/ts/primitives/databases) - [PostgreSQL Extensions](https://encore.dev/docs/ts/primitives/databases-extensions) - [Object Storage](https://encore.dev/docs/ts/primitives/object-storage) - [Cron Jobs](https://encore.dev/docs/ts/primitives/cron-jobs) - [Pub/Sub](https://encore.dev/docs/ts/primitives/pubsub) - [Secrets](https://encore.dev/docs/ts/primitives/secrets) Development - [Authentication](https://encore.dev/docs/ts/develop/auth) - ORMs - [Metadata](https://encore.dev/docs/ts/develop/metadata) - [Testing](https://encore.dev/docs/ts/develop/testing) - [Debugging](https://encore.dev/docs/ts/develop/debug) - [Middleware](https://encore.dev/docs/ts/develop/middleware) - [Multithreading](https://encore.dev/docs/ts/develop/multithreading) - [Running Scripts](https://encore.dev/docs/ts/develop/running-scripts) - [Environment Variables](https://encore.dev/docs/ts/develop/env-vars) - Monorepo CLI - [CLI Reference](https://encore.dev/docs/ts/cli/cli-reference) - [Client Generation](https://encore.dev/docs/ts/cli/client-generation) - [Infra Namespaces](https://encore.dev/docs/ts/cli/infra-namespaces) - [CLI Configuration](https://encore.dev/docs/ts/cli/config-reference) - [Telemetry](https://encore.dev/docs/ts/cli/telemetry) - [MCP](https://encore.dev/docs/ts/cli/mcp) Frontend - [Hosting](https://encore.dev/docs/ts/frontend/hosting) - [CORS](https://encore.dev/docs/ts/frontend/cors) - [Request Client](https://encore.dev/docs/ts/frontend/request-client) - [Template Engine](https://encore.dev/docs/ts/frontend/template-engine) - [Mono vs Multi Repo](https://encore.dev/docs/ts/frontend/mono-vs-multi-repo) Observability - [Development Dashboard](https://encore.dev/docs/ts/observability/dev-dash) - [Logging](https://encore.dev/docs/ts/observability/logging) - [Distributed Tracing](https://encore.dev/docs/ts/observability/tracing) - [Flow Architecture Diagram](https://encore.dev/docs/ts/observability/flow) - [Service Catalog](https://encore.dev/docs/ts/observability/service-catalog) - [Metrics](https://encore.dev/docs/ts/observability/metrics) Self Hosting - [CI/CD](https://encore.dev/docs/ts/self-host/ci-cd) - [Build Docker Images](https://encore.dev/docs/ts/self-host/build) - [Configure Infrastructure](https://encore.dev/docs/ts/self-host/configure-infra) - [Deploy to DigitalOcean](https://encore.dev/docs/ts/self-host/deploy-digitalocean) - [Deploy to Railway](https://encore.dev/docs/ts/self-host/deploy-railway) How to guides - [Handle file uploads](https://encore.dev/docs/ts/how-to/file-uploads) - [Use NestJS with Encore](https://encore.dev/docs/ts/how-to/nestjs) Migration guides - [Migrate away from Encore](https://encore.dev/docs/ts/migration/migrate-away) - [Migrate from Express.js](https://encore.dev/docs/ts/migration/express-migration) Community - [Get Involved](https://encore.dev/docs/ts/community/get-involved) - [Contribute](https://encore.dev/docs/ts/community/contribute) - [Open Source](https://encore.dev/docs/ts/community/open-source) - [Principles](https://encore.dev/docs/ts/community/principles) - [Submit Template](https://encore.dev/docs/ts/community/submit-template) Encore.ts has great support for GraphQL with its type-safe approach to building APIs. Encore's automatic tracing also makes it easy to find and fix performance issues that often arise in GraphQL APIs (like the [N+1 problem](https://hygraph.com/blog/graphql-n-1-problem)). ##### Related example [encoredev/examples/graphql](https://github.com/encoredev/examples/tree/main/ts/graphql) Using Apollo GraphQL together with Encore.ts. `$ encore app create --example=ts/graphql` ## [Concept](https://encore.dev/docs/ts/primitives/graphql\#concept) To serve a GraphQL API, you can leverage [Raw endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints). Raw endpoints provide direct access to the underlying HTTP request and response objects, enabling integration with a GraphQL library. Below is an outline of the high-level steps required for setup: 1. Take client requests with a Raw endpoint. 2. Pass along the request object (body, headers, query params, etc.) to the GraphQL library. 3. Use the GraphQL library to handle the queries and mutations. 4. Return the GraphQL response from the Raw endpoint. Which GraphQL library you choose is up to you. It should work any GraphQL library that allows you to pass along the request object and get a GraphQL response object back without having to start a new HTTP server. ## [Example](https://encore.dev/docs/ts/primitives/graphql\#example) Here's an example using [Apollo](https://www.apollographql.com/docs/apollo-server/) to create a GraphQL API: ```ts import { HeaderMap } from "@apollo/server"; import { api } from "encore.dev/api"; const { ApolloServer, gql } = require("apollo-server"); import { json } from "node:stream/consumers"; // Type definition schema const typeDefs = gql` ... `; // Resolver functions const resolvers = { // ... }; const server = new ApolloServer({ typeDefs, resolvers }); await server.start(); export const graphqlAPI = api.raw( { expose: true, path: "/graphql", method: "*" }, async (req, res) => { // Make sure the Apollo server is started server.assertStarted("/graphql"); // Extract headers in a format that Apollo understands const headers = new HeaderMap(); for (const [key, value] of Object.entries(req.headers)) { if (value !== undefined) { headers.set(key, Array.isArray(value) ? value.join(", ") : value); } } // Get response from Apollo server const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({ httpGraphQLRequest: { headers, method: req.method!.toUpperCase(), body: await json(req), search: new URLSearchParams(req.url ?? "").toString(), }, context: async () => { return { req, res }; }, }); // Set headers for (const [key, value] of httpGraphQLResponse.headers) { res.setHeader(key, value); } // Set status code res.statusCode = httpGraphQLResponse.status || 200; // Write response if it's complete if (httpGraphQLResponse.body.kind === "complete") { res.end(httpGraphQLResponse.body.string); return; } // Write response in chunks if it's async for await (const chunk of httpGraphQLResponse.body.asyncIterator) { res.write(chunk); } res.end(); }, ); ``` ##### Related docs [Building a GraphQL API](https://encore.dev/docs/ts/tutorials/graphql) ## [Call REST APIs in resolvers](https://encore.dev/docs/ts/primitives/graphql\#call-rest-apis-in-resolvers) It's often a good idea to create REST endpoints for your business logic and let your resolvers forward requests to those endpoints. This has a few benefits: 1. **Getting traces** \- Calls to Encore endpoints results in traces being created, even for internal API calls. Having traces makes it easy to find and fix performance issues that often arise in GraphQL APIs (like the [N+1 problem](https://hygraph.com/blog/graphql-n-1-problem)). 2. **Thin resolvers** \- By making your REST request/response objects extend the generated GraphQL types, your resolvers will just be thin wrappers around your REST endpoints. 3. **Testing** \- You can easily test your resolvers by mocking the API calls. 4. **REST & GraphQL** \- You will have both a REST and GraphQL API. Here's an example of how it might look like if you can call a REST API from a resolver: ```typescript schema.graphql type Query { books: [Book] } type Book { title: String! author: String! } ``` ```ts resolver.ts // Import the book service from the generated service clients import { book } from "~encore/clients"; import { QueryResolvers } from "../__generated__/resolvers-types"; const queries: QueryResolvers = { books: async () => { // Call book.list to get the list of books const { books } = await book.list(); return books; }, }; export default queries; ``` ```ts book.ts import { api } from "encore.dev/api"; // Import Book type the generated schema types import { Book } from "../__generated__/resolvers-types"; const db: Book[] = [\ {\ title: "To Kill a Mockingbird",\ author: "Harper Lee",\ },\ // ...\ ]; // REST endpoint to get the list of books export const list = api( { expose: true, method: "GET", path: "/books" }, async (): Promise<{ books: Book[] }> => { return { books: db }; }, ); ``` [Raw Endpoints](https://encore.dev/docs/ts/primitives/raw-endpoints) [Streaming APIs](https://encore.dev/docs/ts/primitives/streaming-apis) [Edit this file on GitHub](https://github.com/encoredev/encore/edit/main/docs/ts/primitives/graphql.mdx)