{"id":16777562,"url":"https://github.com/alessfg/nginx-api-gateway-demo","last_synced_at":"2025-07-25T01:04:37.230Z","repository":{"id":37301793,"uuid":"427403435","full_name":"alessfg/nginx-api-gateway-demo","owner":"alessfg","description":"Demo showcasing how to set up and configure NGINX Plus as an API gateway","archived":false,"fork":false,"pushed_at":"2022-11-01T18:09:31.000Z","size":88313,"stargazers_count":6,"open_issues_count":0,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T20:53:36.103Z","etag":null,"topics":["api","api-gateway","nginx","nginx-api-gateway","nginx-app-protect"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alessfg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-11-12T15:15:28.000Z","updated_at":"2023-09-30T20:30:54.000Z","dependencies_parsed_at":"2023-01-20T21:00:55.113Z","dependency_job_id":null,"html_url":"https://github.com/alessfg/nginx-api-gateway-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alessfg/nginx-api-gateway-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessfg%2Fnginx-api-gateway-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessfg%2Fnginx-api-gateway-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessfg%2Fnginx-api-gateway-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessfg%2Fnginx-api-gateway-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alessfg","download_url":"https://codeload.github.com/alessfg/nginx-api-gateway-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessfg%2Fnginx-api-gateway-demo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266935907,"owners_count":24009307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-gateway","nginx","nginx-api-gateway","nginx-app-protect"],"created_at":"2024-10-13T07:25:07.801Z","updated_at":"2025-07-25T01:04:35.448Z","avatar_url":"https://github.com/alessfg.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NGINX API Gateway Demo\n\n## Overview\n\nThis demo uses Terraform to automate the setup of an NGINX Plus (and NGINX App Protect WAF) API gateway pseudo-production environment that includes a mock API backend database.\n\nA PDF containing accompanying slides for this demo can also be found under the name of [`Deploy and Secure Your API Gateway with NGINX.pdf`](Deploy%20and%20Secure%20Your%20API%20Gateway%20with%20NGINX.pdf).\n\n## Requirements\n\n### Terraform\n\nThis demo has been developed and tested with Terraform `0.13` through `1.1.5`.\n\nInstructions on how to install Terraform can be found in the [Terraform website](https://www.terraform.io/downloads.html).\n\n### NGINX Plus \u0026 NGINX App Protect WAF\n\nYou will need to download the NGINX Plus (including NGINX App Protect WAF) license to a known location. You can specify the location of the license in the corresponding Terraform variables.\n\n### AWS R53\n\nYou will need to create R53 hosted zone beforehand. Make sure you own the domain you are using through the R53 hosted zone or you risk running into DNS issues. You should specify the R53 hosted zone `id` as well as a FQDN for the NGINX Plus API gateway and backend API in the corresponding Terraform variables.\n\n## Deployment\n\nTo use the provided Terraform scripts, you need to:\n\n1. Export your AWS credentials as environment variables (or alternatively, tweak the AWS provider in [`terraform/provider.tf`](terraform/provider.tf)).\n2. Set up default values for variables missing a value in [`terraform/variables.tf`](terraform/variables.tf) (you can find example values commented out in the file). Alternatively, you can input those variables at runtime (beware of dictionary values if you do the latter).\n\nOnce you have configured your Terraform environment, you can either:\n\n* Run [`./setup.sh`](setup.sh) to initialize the AWS Terraform provider and start a Terraform deployment on AWS.\n* Run `terraform init` and `terraform apply`.\n\nAnd finally, once you are done playing with the demo, you can destroy the AWS infrastructure by either:\n\n* Run [`./cleanup.sh`](cleanup.sh) to destroy your Terraform deployment.\n* Run `terraform destroy`.\n\n## Demo Overview\n\nYou will find a series of NGINX configuration files in the [`nginx_api_gateway_config`](nginx_api_gateway_config/) folder. The folder is divided into individual steps, meant to be copied into their respective directory in order. By default, the folder is uploaded to your NGINX API gateway instance.\n\nDo note that you will have to replace the `\u003cbackend-api-fqdn\u003e` placeholder value found in the API backends NGINX configuration file in Step 3 with the corresponding value you used when deploying the Terraform environment (see [`nginx_api_gateway_config/step_3/api_backends.conf`](nginx_api_gateway_config/step_3/api_backends.conf) for more details).\n\nA deployment script to help you copy the configuration files, [`deploy.sh`](nginx_api_gateway_config/deploy.sh), is also provided. To run the script, use the step number as a parameter, e.g. `./deploy.sh 1` for step 1. You might need to make the deployment script executable by running `sudo chmod +x deploy.sh`.\n\n### Step 1 -\u003e Define the entry point of the NGINX API gateway\n\nTo deploy:\n\n`./deploy.sh 1`\n\nTo test:\n\n`curl -s http://localhost:8080`\n\nExpected response:\n\n```html\n\u003chtml\u003e\n\u003chead\u003e\u003ctitle\u003e400 Bad Request\u003c/title\u003e\u003c/head\u003e\n\u003cbody\u003e\n\u003ccenter\u003e\u003ch1\u003e400 Bad Request\u003c/h1\u003e\u003c/center\u003e\n\u003chr\u003e\u003ccenter\u003enginx/1.19.5\u003c/center\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Step 2 -\u003e Define default JSON error codes\n\nTo deploy:\n\n`./deploy.sh 2`\n\nTo test:\n\n`curl -s http://localhost:8080`\n\nExpected response:\n\n```json\n{\"status\":400,\"message\":\"Bad request\"}\n```\n\nTo test (headers):\n\n`curl -sI http://localhost:8080`\n\nExpected response:\n\n```text\nHTTP/1.1 400 Bad Request\n...\n```\n\n### Step 3 -\u003e Define the API endpoints and upstream/backend servers\n\nTo deploy:\n\n`./deploy.sh 3`\n\nTo test:\n\n`curl -s http://localhost:8080/api/f1/drivers/hamilton | jq`\n\nExpected response:\n\n```json\n{\"MRData\": {\n    \"xmlns\": \"http://ergast.com/mrd/1.4\",\n    \"series\": \"f1\",\n    \"url\": \"http://ergast.com/api/f1/drivers/hamilton\",\n    \"limit\": \"30\",\n    \"offset\": \"0\",\n    \"total\": \"1\",\n    \"DriverTable\": {\n      \"driverId\": \"hamilton\",\n      \"Drivers\": [{\n          \"driverId\": \"hamilton\",\n          \"permanentNumber\": \"44\",\n          \"code\": \"HAM\",\n          \"url\": \"http://en.wikipedia.org/wiki/Lewis_Hamilton\",\n          \"givenName\": \"Lewis\",\n          \"familyName\": \"Hamilton\",\n          \"dateOfBirth\": \"1985-01-07\",\n          \"nationality\": \"British\"\n      }]\n    }\n}}\n```\n\n### Step 4 -\u003e Enable rate limiting\n\nTo deploy:\n\n`./deploy.sh 4`\n\nTo test (run multiple times in quick succession):\n\n`curl -s http://localhost:8080/api/f1/drivers/hamilton`\n\nExpected response:\n\n```json\n{\"status\":429,\"message\":\"API rate limit exceeded\"}\n```\n\n### Step 5 -\u003e Set up API Key authentication\n\nTo deploy:\n\n`./deploy.sh 5`\n\nTo test (unauthorized requests):\n\n`curl -s http://localhost:8080/api/f1/drivers/hamilton`\n\nExpected response (unauthorized requests):\n\n```json\n{\"status\":401,\"message\":\"Unauthorized\"}\n```\n\nTo test (authorized requests):\n\n`curl -sH \"apikey: 7B5zIqmRGXmrJTFmKa99vcit\" http://localhost:8080/api/f1/drivers/hamilton | jq`\n\nExpected response (authorized requests):\n\n```json\n{\"MRData\": {\n    \"xmlns\": \"http://ergast.com/mrd/1.4\",\n    \"series\": \"f1\",\n    \"url\": \"http://ergast.com/api/f1/drivers/hamilton\",\n    ...\n}}\n```\n\n### Step 6 -\u003e Set up JWT authentication\n\nTo deploy:\n\n`./deploy.sh 6`\n\nTo test (unauthorized requests):\n\n`curl -s http://localhost:8080/api/f1/drivers/hamilton`\n\nExpected response (unauthorized requests):\n\n```json\n{\"status\":401,\"message\":\"Unauthorized\"}\n```\n\nTo test (authorized request):\n\n`curl -sH \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZX0.kFplw9Kkg-6DLFGfVZAPIuWgGPMY9nnMZMQ2iIRN8_s\" http://localhost:8080/api/f1/drivers/hamilton | jq`\n\nExpected response (authorized request):\n\n```json\n{\"MRData\": {\n    \"xmlns\": \"http://ergast.com/mrd/1.4\",\n    \"series\": \"f1\",\n    \"url\": \"http://ergast.com/api/f1/drivers/hamilton\",\n    ...\n}}\n```\n\nTo test (missing JWT claims):\n\n`curl -sH \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2V9.i7o5c8MEGZWD223IWFIs-Qn6f8FBe_DjvZWn-xBzcvI\" -X DELETE http://localhost:8080/api/f1/drivers/hamilton`\n\nExpected response (missing JWT claims):\n\n```json\n{\"status\":405,\"message\":\"Method not allowed\"}\n```\n\n### Step 7 -\u003e Set up JSON body validation using NJS (optional, NGINX App Protect WAF -step 8- will validate JSON bodies)\n\nTo deploy:\n\n`./deploy.sh 7`\n\nTo test (incorrect JSON):\n\n`curl -sH \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZX0.kFplw9Kkg-6DLFGfVZAPIuWgGPMY9nnMZMQ2iIRN8_s\" -i -X POST -d 'garbage123' http://localhost:8080/api/f1/seasons`\n\nExpected response (incorrect JSON):\n\n```text\nHTTP/1.1 415 Unsupported Media Type\n```\n\nTo test (correct JSON):\n\n`curl -sH \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZX0.kFplw9Kkg-6DLFGfVZAPIuWgGPMY9nnMZMQ2iIRN8_s\" -i -X POST -d '{\"season\":\"2020\"}' http://localhost:8080/api/f1/seasons`\n\nExpected response (correct JSON):\n\n```text\nHTTP/1.1 200 OK\n```\n\n### Step 8 -\u003e Set up NGINX App Protect WAF protection using an OpenAPI spec file\n\nTo deploy:\n\n`./deploy.sh 8`\n\nTo test:\n\n`curl -sH \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZX0.kFplw9Kkg-6DLFGfVZAPIuWgGPMY9nnMZMQ2iIRN8_s\" -i -X POST -d 'garbage123' http://localhost:8080/api/f1/seasons`\n\nExpected response:\n\n```json\nHTTP/1.1 403 Forbidden\n{\"supportID\": \"4839869788531770938\"}\n```\n\n**Note:** SupportID is a unique identifier so your ID will be different\n\nTo check logs:\n\n`sudo cat /var/log/app_protect/security.log`\n\nExpected response:\n\n```text\nattack_type=\"HTTP Parser Attack”... support_id=\"4839869788531771448”...\n```\n\n**Note:** Check that the supportID you received in the previous step is present in the security log\n\n## Author Information\n\n[Alessandro Fael Garcia](https://github.com/alessfg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessfg%2Fnginx-api-gateway-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falessfg%2Fnginx-api-gateway-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessfg%2Fnginx-api-gateway-demo/lists"}