Skip to main content

Get started - Deliver and secure APIs in production

You've developed a world-class API, and now you want to make it available for clients to access. This guide takes you from bringing your API online in one line to configuring an API gateway that simplifies routing, security, contract management, protocol translation, and more, providing a single point of entry for clients and services accessing your APIs.

Prerequisites

You'll need an ngrok account to follow the steps in this guide. You can sign up for free if you don't already have one.

You'll also need a deployed API to manage with ngrok. If you're just getting started or performing a proof-of-concept, you can deploy our sample REST API for testing purposes. In that case, you'll also need git installed.

Install the ngrok agent

Download the appropriate version of the ngrok agent, and install it on the same subnet as the API you wish to manage.

Create an ngrok API key

Create an ngrok API key using the ngrok dashboard. Make sure you save the API key before you leave the screen because it won't be displayed again.

Add the API key to the ngrok agent

Run the following command to add the API key to your ngrok agent, substituting your API key for {API_KEY}:

ngrok config add-api-key {API_KEY}

Setup sample API

To test the policy actions in this guide, you can use the sample API to simulate an environment with multiple APIs deployed and managed independently.

Clone the repo:

git clone git@github.com:ngrok-samples/ngrok-rest-api.git

Install dependencies:

npm install

Start the application on the default port (8001):

npm run start

Locate src/index.js, and modify the PORT value to 8002:

const PORT = 8001; // Change to 8002
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Start a second instance:

npm run start

Create an internal endpoint

Create an internal endpoint for one of the instances of the API by running the following command:

ngrok http 8001 --url=https://api.internal

Reserve a domain

Run the following command to reserve a domain:

ngrok api reserved-domains create --url {NGROK_SUBDOMAIN}.ngrok.app

If the subdomain you chose is already in use, you'll get a 400 error with a message to that effect. In that case, run the command again with a different subdomain. Alternatively, you can reserve the domain through the dashboard, which will show you which subdomains are available as you type.

This example uses an ngrok-managed domain, but you can use your own branded domain when you're ready for production. In that case, substitute your branded subdomain for {NGROK_SUBDOMAIN}.ngrok.app in the command above, and follow the steps to configure DNS.

Bring your API online

You've created an internal endpoint to one of the instances of your API, but you need a cloud endpoint to receive traffic and apply policies before forwarding requests to your backend service via the internal endpoints. You'll start by bringing one of the instances online.

Create a traffic policy file

Unlike other endpoint types, cloud endpoints must contain at least one traffic policy action which specifies a forward-internal action and the URL to an internal endpoint where traffic should be forwarded.

Create a file called policies.yaml and paste the following contents:

---
on_http_request:
- actions:
- type: forward-internal
config:
url: https://api.internal
binding: internal

Create a cloud endpoint

Run the following command from the same directory where you created policies.yaml to create a cloud endpoint, substituting the subdomain (i.e. mydomain.ngrok.app) you reserved in the previous step for {YOUR_DOMAIN}:

ngrok api endpoints create \
--bindings public \
--description "sample description" \
--metadata sample-metadata \
--url https://{YOUR_DOMAIN} \
--traffic-policy "$(cat policies.yaml)"

You'll receive a 201 response similar to the following:

{
"bindings": [
"public"
],
"created_at": "2024-10-25T14:35:28Z",
"description": "sample description",
"domain": {
"id": "rd_2nvupgtpY4RuEPITujbfHpkhxmO",
"uri": "https://api.ngrok.com/reserved_domains/rd_2nvupgtpY4RuEPITujbfHpkhxmO"
},
"hostport": "mydomain.ngrok.app:443",
"id": "ep_2nvwbSCefcZv0At2ewhzMHPBT3V",
"metadata": "sample-metadata",
"proto": "https",
"public_url": "https://mydomain.ngrok.app",
"traffic_policy": "{\"on_http_request\":[{\"actions\":[{\"type\":\"forward-internal\",\"config\":{\"url\":\"https://mandy.ngrok.internal\",\"binding\":\"internal\"}}]}]}",
"type": "cloud",
"updated_at": "2024-10-25T14:35:28Z",
"uri": "https://api.ngrok.com/endpoints/ep_2nvwbSCefcZv0At2ewhzMHPBT3V",
"url": "https://mydomain.ngrok.app"
}

Save the value of id because you'll need it in a later step.

Access your API

You can now access the sample API via the subdomain you reserved above. For example, you can run the following command to access the cards endpoint:

curl -X GET https://{YOUR_SUBDOMAIN}.ngrok.app/cards

You'll receive a 200 response similar to to the following:

{
"cards": [
{
"id": "ccof:uIbfJXhXETSP197M3GB",
"billing_address": {
"address_line_1": "500 Electric Ave",
"address_line_2": "Suite 600",
"locality": "New York",
"administrative_district_level_1": "NY",
"postal_code": "10003",
"country": "US"
},
"bin": "411111",
"card_brand": "VISA",
"card_type": "CREDIT",
"cardholder_name": "Amelia Earhart",
"customer_id": "VDKXEEKPJN48QDG3BGGFAK05P8",
"enabled": true,
"exp_month": 11,
"exp_year": 2018,
"last_4": "1111",
"prepaid_type": "NOT_PREPAID",
"reference_id": "user-id-1",
"version": 1
}
]
}

Stop the agent to remove the internal endpoint you just created.

Manage APIs with traffic policy actions

Your API is now online, but it is completely exposed to the outside world. Next, add some traffic policy actions to authenticate requests and shape the traffic that reaches your backend service. You'll do this by running a command to update the configuration of your existing cloud endpoint, so you'll need the id from the previous step.

If you didn't save the id when you created the cloud endpoint, run the following command, locate your endpoint, and grab its id:

ngrok api endpoints list

Route by headers

You can configure traffic policy actions to direct traffic based on HTTP header values.

Start internal endpoints

To implement this example, run each of the following commands in a separate terminal to start internal endpoints to the two instances of the sample API:

ngrok http 8001 --url=https://v1.api.internal
ngrok http 8002 --url=https://v2.api.internal

Update traffic policies

This example looks for a header named X-Api-Version and routes traffic to different instances of the /cards endpoint of the sample API. Feel free to modify it for your own API if you're not using the sample app. Update your policies.yaml file with the contents below, overwriting all previous contents:

# Route by header
---
on_http_request:
- expressions:
- "'1' in req.headers['x-api-version']"
actions:
- type: forward-internal
config:
url: https://v1.api.internal
binding: internal
- expressions:
- "'2' in req.headers['x-api-version']"
actions:
- type: forward-internal
config:
url: https://v2.api.internal
binding: internal
- actions:
- type: forward-internal
config:
url: https://v2.api.internal
binding: internal
note

You must always include the forward-internal action as the final step in your policy action since once it executes, no subsequent policy actions will be executed. Additionally, you always need a terminating policy action. In this case, the traffic forwards to https://v2.api.internal if the header is set to a value other than 1 or 2 or if the header isn't present in the request.

Run the following command, substituting your {ENDPOINT_ID}, to update the cloud endpoint with the new policy actions:

 ngrok api endpoints update {ENDPOINT_ID} --traffic-policy "$(cat policies.yaml)"

Test traffic policies

You can test this by passing a header value when you send a request to this API. Try it with version 1.

curl -X GET https://{YOUR_SUBDOMAIN}.ngrok.app/cards --header "X-Api-Version: 1"

You'll receive a 200 response similar to to the following:

{
"cards": [
{
"id": "ccof:uIbfJXhXETSP197M3GB",
"billing_address": {
"address_line_1": "500 Electric Ave",
"address_line_2": "Suite 600",
"locality": "New York",
"administrative_district_level_1": "NY",
"postal_code": "10003",
"country": "US"
},
"bin": "411111",
"card_brand": "VISA",
"card_type": "CREDIT",
"cardholder_name": "Amelia Earhart",
"customer_id": "VDKXEEKPJN48QDG3BGGFAK05P8",
"enabled": true,
"exp_month": 11,
"exp_year": 2018,
"last_4": "1111",
"prepaid_type": "NOT_PREPAID",
"reference_id": "user-id-1",
"version": 1
}
]
}

Go to the terminal where you started the internal endpoint https://v1.api.internal on port 8001, and you'll see the request hit the correct endpoint.

ngrok                                                                                                                                                                                                                                               (Ctrl+C to quit)

Found a bug? Let us know: https://github.com/ngrok/ngrok

Session Status online
Account ngrok Marketing Team (Plan: All)
Version 3.18.1
Region United States (us)
Latency 97ms
Web Interface http://127.0.0.1:4040
Forwarding https://v1.api.internal -> http://localhost:8001

Connections ttl opn rt1 rt5 p50 p90
0 1 0.00 0.00 0.00 0.00

HTTP Requests
-------------

15:00:14.035 CDT GET /cards 200 OK

Change the header value to 2 and run the command again. You should see this request hit the https://v2.api.internal endpoint on port 8002.

Stop both agent instances to remove the internal endpoints you just created.

Route by path

Path-based routing enables you to direct traffic to different backend services based on the value of the path.

Start internal endpoints

To implement this example, run the following commands in separate terminals to start internal endpoints to the two instances of the sample API:

ngrok http 8001 --url=https://companyA.api.internal
ngrok http 8002 --url=https://companyB.api.internal

Update traffic policies

This example routes traffic to two different backends depending on the path. Copy the following contents into your policy.yaml file, overwriting all previous contents:

# Route by path
---
on_http_request:
# Handle /cards/*
- expressions:
- req.url.path.startsWith('/cards/')
actions:
- type: forward-internal
config:
url: http://companyA.api.internal
# Handle /payments/*
- expressions:
- req.url.path.startsWith('/payments/')
actions:
- type: forward-internal
config:
url: http://companyB.api.internal
note

You must always include the forward-internal action as the final step in your policy action since once it executes, no subsequent policy actions will be executed. Additionally, you always need a terminating policy action. In this case, the traffic forwards to https://companyB.api.internal if the path begins with a value other than /cards or /payments

Now, run the following command to apply the new policies to your cloud endpoint.

 ngrok api endpoints update {ENDPOINT_ID} --traffic-policy "$(cat policies.yaml)"

Test traffic policies

Run the following command to hit the /cards endpoint:

curl -X GET https://{YOUR_SUBDOMAIN}.ngrok.app/cards

Examine the terminal where you started the https://companyA.api.internal endpoint on port 8001, and you can see that the request hit the correct endpoint. Repeat the steps for the /payments endpoint and confirm the request was sent to the https://companyB.api.internal endpoint on port 8002.

Stop both agent instances to remove the internal endpoints you just created.

Configure URL rewrites

The URL Rewrite traffic policy action allows you to modify the incoming request URL using regular expressions before it reaches the upstream service, without changing the URL seen by the client. This action allows you to route user requests without exposing internal system details.

Start an internal endpoint

Run the following command to start an internal endpoint:

ngrok http 8001 --url=https://api.internal

Update traffic policies

These traffic policies allow you to redirect requests sent to the /credit-cards endpoint to the /cards endpoint without the client seeing the backend URL. Copy the following contents into your policy.yaml file, overwriting all previous contents:

# URL Rewrite
---
on_http_request:
- expressions:
- "req.url.path == '/credit-cards'"
actions:
- type: url-rewrite
config:
from: credit-cards
to: cards
- actions:
- type: forward-internal
config:
url: https://api.internal
binding: internal
note

You must always include the forward-internal action as the final step in your policy action since once it executes, no subsequent policy actions will be executed. Additionally, you always need a terminating policy action. In this case, the traffic forwards to https://api.internal if a path other than /credit-cards is encountered.

Test traffic policies

Run the following command to hit the /credit-cards endpoint, which doesn't exist, and confirm that you are redirected to the /cards endpoint instead:

curl -X GET https://{YOUR_SUBDOMAIN}.ngrok.app/credit-cards

Stop the agent to remove the internal endpoint you just created.

Enable rate limiting

Add rate limiting to your API gateway to ensure your service does not become overwhelmed by too many requests and that each of your clients has fair access to your API.

Start an internal endpoint

Run the following command to start an internal endpoint:

ngrok http 8001 --url=https://api.internal

Update traffic policies

This traffic policy action allows 30 requests over a 60 second window based on the API key of the client as determined by the value of the HTTP header. You can also limit by endpoint, authentication status, or pricing tier.

Copy the following contents into your policy.yaml file, overwriting all previous contents:

# Rate Limiting
---
on_http_request:
- expressions: []
name: Add rate limiting
actions:
- type: rate-limit
config:
name: Only allow 30 requests per minute
algorithm: sliding_window
capacity: 30
rate: 60s
bucket_key:
- req.Headers['x-api-key']
- actions:
- type: forward-internal
config:
url: https://api.internal
binding: internal
note

You must always include the forward-internal action as the final step in your policy action since once it executes, no subsequent policy actions will be executed.

Run the following command, substituting your {ENDPOINT_ID}, to update the cloud endpoint with the new policy actions:

 ngrok api endpoints update {ENDPOINT_ID} --traffic-policy "$(cat policies.yaml)"

Test traffic policies

You can test the rate limiting action by running the following command, substituting the appropriate value for {YOUR_DOMAIN}:

for i in `seq 1 50`; do curl -X GET -w '%{http_code}' https://{YOUR_DOMAIN}/ ; done

You'll see a response similar to the first example until you hit the rate limit, and then you'll see 429 errors returned.

Stop the agent to remove the internal endpoint you just created.

Add JWT validation

The JWT Validation policy action allows you to integrate your ngrok endpoints with your existing auth provider. This example provides step-by-step instructions for integrating with Auth0, but you can configure JWT validation for any OAuth provider. Check out our integrations guides for specific examples.

Define your API in Auth0

  1. Login to your Auth0 Tenant Dashboard:
  2. Select Applications > APIs
  3. Click + Create API
  4. Name your API whatever you'd like
  5. Add your ngrok domain as the identifier
  6. Leave the default values for JSON Web Token (JWT) Profile * and JSON Web Token Signing Algorithm *
  7. Click Create
Auth0 Create API

Access your JWT

Upon creating your new API, Auth0 will create an associated application under Applications > APIs in the left navigation bar.

Navigate to your application, and click on the Test tab. Here, you will find a signed, fully functional JWT, as well as examples of how to programmatically generate one.

Auth0 Test JWT

Start an internal endpoint

Run the following command to start an internal endpoint:

ngrok http 8001 --url=https://api.internal

Update traffic policies

This traffic policy integrates ngrok with your auth provider so ngrok can validate JWTs passed in requests to your API.

note

You must always include the forward-internal action as the final step in your policy action since once it executes, no subsequent policy actions will be executed.

Copy the following contents into your policy.yaml file, overwriting all previous contents:

---
on_http_request:
- actions:
- type: "jwt-validation"
config:
issuer:
allow_list:
- value: "https://{YOUR_AUTH0_TENANT_ID}.us.auth0.com/"
audience:
allow_list:
- value: "https://{YOUR_NGROK_DOMAIN}"
http:
tokens:
- type: "jwt"
method: "header"
name: "Authorization"
prefix: "Bearer "
jws:
allowed_algorithms:
- "RS256"
keys:
sources:
additional_jkus:
- "https://{YOUR_AUTH0_TENANT_ID}.us.auth0.com/.well-known/jwks.json"
- name: "Forward internal"
actions:
- type: "forward-internal"
config:
url: "https://api.internal"
binding: internal

Test traffic policies

You can test the JWT validation policy by running the following command, substituting your domain for {YOUR_NGROK_DOMAIN} and the JWT obtained from the Auth0 dashboard for {YOUR_JWT}:

curl -X GET https://{YOUR_NGROK_DOMAIN}/cards \
--header "Authorization: Bearer {YOUR_JWT}"

You'll receive a 200 response with the same contents as before.

Run the same command, switching out one character of your JWT, and you'll get a 403 Forbidden error.

Run the command with no Authorization header, substituting your domain for {YOUR_NGROK_DOMAIN}:

curl -X GET https://{YOUR_NGROK_DOMAIN}/cards

You'll receive a 201 Unauthorized error.