Couper is a lightweight open-source API gateway that acts as an entry point for clients to your application (frontend API gateway) and an exit point to upstream services (upstream API gateway).
It adds access control, observability, and back-end connectivity on a separate layer. This will keep your core application code more simple.
Couper does not need any special development skills and offers easy configuration and integration.
Entity | Description |
---|---|
Frontend | Browser, App or API Client that sends requests to Couper. |
Frontend API | Couper acts as an entry point for clients to your application. |
Backend Service | Your core application - no matter which technology, if monolithic or micro-services. |
Upstream API | Couper acts as an exit point to upstream services for your application. |
Remote Service | Any upstream service or system which is accessible via HTTP. |
Protected System | Representing a service or system that offers protected resources via HTTP. |
Couper is available as docker image from Docker Hub
Running Couper requires a working Docker setup on your computer. Please visit the get started guide to get prepared.
To download/install Couper, open a terminal and execute:
docker pull avenga/couper
Couper needs a configuration file to know what to do.
Create a directory with an empty couper.hcl
file.
Copy/paste the following configuration to the file and save it.
server "hello" {
endpoint "/**" {
response {
body = "Hello World!"
}
}
}
Now cd
into the directory with the configuration file and start Couper in a docker container:
$ docker run --rm -p 8080:8080 -v "$(pwd)":/conf avenga/couper
{"addr":"0.0.0.0:8080","level":"info","message":"couper gateway is serving","timestamp":"2020-08-27T16:39:18Z","type":"couper"}
Now Couper is serving on your computer's port 8080. Point your
browser or curl
to localhost:8080
to see what's going on.
Press CTRL+c
to stop the container.
The following section will give you an introduction into Couper's configuration file.
Here you will find the full configuration reference.
If you prefer to learn about Couper by checking out certain features, visit the example repository.
The language for Couper's configuration file is HCL 2.0, a configuration language by HashiCorp.
⚠ Couper's configuration reference can be found here.
Couper provides its own IDE extension that adds Couper-specific highlighting and autocompletion to Couper's configuration file couper.hcl
in Visual Studio Code.
Get it from the Visual Studio Market Place or visit the Extension repository.
The file-ending of your configuration file should be .hcl
to have syntax highlighting within your IDE.
The file name defaults to couper.hcl
in your working directory. This can be changed with the -f
command-line flag. With -f /opt/couper/my_conf.hcl
couper changes the working directory to /opt/couper
and loads my_conf.hcl
.
Couper's configuration file consists of nested configuration blocks that configure the gateway. There are a large number of options, but let's focus on the main structure first:
server "my_project" {
files {
# ...
}
spa {
# ...
}
api {
access_control = ["foo"]
endpoint "/bar" {
proxy {
backend { }
}
request "sub-request" {
backend { }
}
response { }
}
}
}
definitions {
# ...
}
settings {
# ...
}
defaults {
# ...
}
server
main configuration block(s)files
configuration block for file servingspa
configuration block for Web serving (SPA assets)api
configuration block(s) that bundle(s) endpoints under a certain base path oraccess_control
listaccess_control
attribute that sets access control for a block contextendpoint
configuration block for Couper's entry pointsproxy
configuration block for a proxy request and response to an originbackend
configuration block for connection to local/remote backend service(s)
request
configuration block for a manual request to an originbackend
configuration block for connection to local/remote backend service(s)
response
configuration block for a manual client response
definitions
block for predefined configurations, that can be referencedsettings
block for server configuration which applies to the running instancedefaults
block for default/fallback values
See also: Configuration Reference
Since we use HCL 2.0 for our configuration, we are able to use attribute values as expression.
// Arithmetic with literals and application-provided variables
sum = 1 + addend
// String interpolation and templates
message = "Hello, ${name}!"
// Application-provided functions
shouty_message = upper(message)
See function reference.
The configuration file allows the use of some predefined variables. There are two phases when those variables get evaluated.
The first phase is at config load which is currently related to env
and simple function usage.
The second evaluation will happen during the request/response handling.
env
are the environment variablesrequest
is the client requestbackend_requests
contains all modified backend requestsbackend_responses
contains all original backend responses
See variables reference for details.
Couper contains a Web server for simple file serving and also takes care of the more complex web serving of SPA assets.
server "example" {
files {
document_root = "htdocs"
}
spa {
bootstrap_file = "htdocs/index.html"
paths = ["/**"]
}
}
The files
block configures Couper's file server. It needs to know which directory to serve (document_root
).
The spa
block is responsible for serving the bootstrap document for all paths that match the paths list.
Couper's main concept is exposing APIs via the configuration block endpoint
, fetching data from upstream or remote services, represented by the configuration block backend
.
server "example"{
endpoint "/public/**"{
path = "/**"
proxy {
backend {
origin = "https://httpbin.org"
}
}
}
}
This basic configuration defines an upstream backend service (https://httpbin.org
) and "mounts" it on the local API endpoint /public/**
.
An incoming request /public/foo
will result in an outgoing request https://httpbin.org/foo
.
Access control is controlled by an Access Control attribute that can be set for many blocks.
server "example" {
endpoint "/private/**" {
access_control = ["accessToken"]
path = "/**"
proxy {
backend {
origin = "https://httpbin.org"
}
}
}
definitions {
jwt "accessToken" {
signature_algorithm = "RS256"
key_file = "keys/public.pem"
header = "Authorization"
}
}
}
This configuration protects the endpoint /private/**
with the access control "accessToken"
configured in the definitions
block.
api "my_api" {
base_path = "/api/v1"
endpoint "/login/**" {
proxy {
backend {
origin = "http://identityprovider:8080"
}
}
}
endpoint "/cart/**" {
path = "/api/v1/**"
proxy {
url = "http://cartservice:8080"
}
endpoint "/account/{id}" {
proxy {
backend {
path = "/user/${request.param.id}/info"
origin = "http://accountservice:8080"
}
}
}
}
Incoming request | Outgoing request |
---|---|
/api/v1/login/foo | http://identityprovider:8080/login/foo |
/api/v1/cart/items | http://cartservice:8080/api/v1/items |
/api/v1/account/brenda | http://accountservice:8080/user/brenda/info |
An example to send an additional header with client request header to a configured backend and gets evaluated on per-request basis:
server "variables-srv" {
endpoint "/" {
proxy {
backend "my_backend_definition" {
set_request_headers = {
# simple variable lookup
x-uuid = request.id
# template string
user-agent = "myproxyClient/${request.headers.app-version}"
# expressions and function calls
x-env-user = env.USER != "" ? upper(env.USER) : "UNKNOWN"
}
}
}
}
}