If you’re building APIs with TypeScript, NestJS is probably on your radar. It’s the most popular TypeScript-first backend framework, used by teams at Roche, Adidas, and Autodesk, and it pairs well with a gateway that speaks the same language.
You’ll build a documented REST API with NestJS, generate an OpenAPI spec with
@nestjs/swagger, and use Zuplo to add rate limiting, API
key auth, and a developer portal, without writing a line of gateway code.
TypeScript end to end.
- Building REST APIs with TypeScript and NestJS
- Looking for a gateway that complements your TypeScript backend
- Need rate limiting and authentication without rolling your own middleware
- Want auto-generated API documentation from your OpenAPI spec
Prerequisites
Build a CRUD API with NestJS
Let’s start by scaffolding a new NestJS project using the CLI. If you don’t have
the NestJS CLI installed globally, you can use npx:
Choose npm as the package manager when prompted, then move into the project
directory:
Install dependencies
We need @nestjs/swagger to generate our OpenAPI spec and class-validator
plus class-transformer for DTO validation:
Enable validation globally
Open src/main.ts and add the global validation pipe so our DTOs are
automatically validated on incoming requests:
This sets up both the validation pipeline and the Swagger documentation
endpoint. When the app is running, you’ll be able to access the Swagger UI at
/docs and the raw OpenAPI JSON at /docs-json.
Create the bookmark DTO
Create a file for our data transfer objects:
The ! on title and url is TypeScript’s definite-assignment assertion. It’s
required because the scaffolded tsconfig.json has
strictPropertyInitialization, and class-validator populates these fields at
runtime rather than at construction time.
The @ApiProperty and @ApiPropertyOptional decorators tell @nestjs/swagger
how to describe each field in the generated OpenAPI spec. class-validator
decorators like @IsUrl() and @IsNotEmpty() handle runtime validation.
Invalid requests are rejected with a 400 Bad Request before they reach your
business logic.
Create the bookmark service
We’re using an in-memory array for simplicity. Swap it for Postgres or Mongo in production. The API contract, and therefore the OpenAPI spec, stays the same.
Create the bookmark controller
@ApiOperation and @ApiResponse give each route a description and a response
schema in the generated spec. Because we pass type: Bookmark, the OpenAPI
output includes the full response shape, which Zuplo’s developer portal can
render later. @ApiTags("bookmarks") groups the endpoints under “bookmarks” in
the Swagger UI.
Wire it up in the app module
Replace the contents of src/app.module.ts:
Test it locally
Start the dev server:
Visit http://localhost:3000/docs to see the Swagger UI with all five endpoints
documented. Test them right from the browser: create a bookmark with POST, then
fetch all bookmarks with GET.
For the next step, grab the raw OpenAPI JSON. From a terminal:
Or visit http://localhost:3000/docs-json in a browser and save the page.
You’ll paste this file into Zuplo shortly.
Deploy to Railway
Railway makes deploying Node.js apps straightforward. Push your project to a GitHub repository, then:
- Go to railway.com and sign in with your GitHub account
- Click New Project and select Deploy from GitHub repo
- Select your
bookmarks-apirepository
Railway detects a Node.js project, runs npm run build, and then starts your
app with npm start. It also injects a PORT environment variable, which is
why src/main.ts reads process.env.PORT. Your API is live in under a minute.
Once deployed, click Settings → Networking → Generate Domain to get
a public URL. Your OpenAPI spec will be available at
https://your-app.up.railway.app/docs-json.
Pro tip:
Railway’s free trial includes $5 of credits, which is plenty for testing and development.
Common mistake:
Bookmarks live in memory, so every redeploy or container restart wipes the
list. Fine for testing the gateway flow; for a real deployment, swap the array
in BookmarkService for a database.
Add an API gateway with Zuplo
You’ve got a documented API. Now put Zuplo in front of it to handle the cross-cutting concerns every production API needs: rate limiting, auth, and developer docs.
The idea is simple. Zuplo reads your OpenAPI spec, creates a route for each endpoint, and proxies traffic to your NestJS backend. You layer on policies like rate limiting and API key auth without touching your application code.
Import your OpenAPI spec
- Go to portal.zuplo.com and create a new project
- In the Code tab, open
routes.oas.json. This is Zuplo’s gateway config file. It’s an OpenAPI document where each operation also carries Zuplo-specific routing and policy metadata - Replace the contents with the OpenAPI JSON from your
openapi.jsonfile (or fetch the live Railway spec athttps://your-app.up.railway.app/docs-json) - Save the file
Zuplo parses the spec and creates a route for every endpoint automatically.
Pro tip:
You can also import your API from the OpenAPI tab in the Zuplo portal, or use the Zuplo CLI if you prefer working locally.
Configure the upstream URL
Your routes need to know where to forward requests. Set up an environment variable so you can easily change it per environment:
- Go to Settings → Environment Variables
- Add a variable called
BASE_URLwith the value set to your Railway URL (e.g.https://your-app.up.railway.app) - Back in the route designer, set the Forward to field on each route to
${env.BASE_URL}
Save and test. Your NestJS API should respond through the Zuplo gateway.
Getting Started with Zuplo
Learn how to set up your first API gateway with Zuplo, import your OpenAPI specification, and start applying policies.
Add rate limiting
Rate limiting protects your backend from abuse and ensures fair usage across consumers. With Zuplo, adding it takes about 30 seconds:
- Open one of your routes in the route designer
- Click Add Policy in the Policies section
- Search for Rate Limiting and select it
- Set
rateLimitBytoip,requestsAllowedto2, andtimeWindowMinutesto1. These low numbers make the limit easy to trigger while testing
The UI form writes the following config into your policies file. You don’t need to edit it by hand, but this is what’s happening under the hood:
Save and test. A third request within the same minute returns
429 Too Many Requests. In production, bump requestsAllowed and switch to
"rateLimitBy": "user" to rate limit per API key instead of per IP.
Rate Limit Policy
Zuplo's Rate Limit policy supports per-IP, per-key, and custom function-based rate limiting with configurable time windows.
Add API key authentication
API keys let you identify and control who accesses your API. Here’s how to add them:
- On your route, click Add Policy again
- Search for API Key Authentication and select it
- In the policy list for the route, drag the API key policy above the rate limiting policy so it runs first. Policies execute top-down.
Common mistake:
Authenticate before rate limiting. If you rate limit first, anonymous traffic eats into the per-key budget before auth rejects it, and you can’t tell real consumers apart in your logs.
Now create an API consumer so you have a key to call with:
- Open Services in the Zuplo portal sidebar
- Click the default API Key Service that ships with new projects
- Click Add Consumer, give it a name (e.g.
test-consumer), save it, and copy the generated key
Call your API without the key. You should get a 401 Unauthorized. Add an
Authorization header with Bearer YOUR_API_KEY and the request goes through.
API Key Authentication Policy
Zuplo's API Key Authentication validates keys against a managed key store, providing simple but powerful auth for your APIs.
Access your developer portal
This is where the TypeScript-to-TypeScript workflow pays off. Your NestJS
decorators describe the API, @nestjs/swagger turns them into an OpenAPI spec,
and Zuplo uses that spec to generate a developer portal for you.
Zuplo ships a developer portal alongside every project. It includes:
- Interactive API documentation generated from your OpenAPI spec
- An API playground where consumers can test endpoints directly
- Self-serve API key management so consumers can sign up, create keys, and start using your API
To open it, go to your deployment in the Zuplo portal and follow the Developer Portal link.
Because the API key policy sits in your OpenAPI spec, the portal documents the
Authorization header requirement for you. No hand-written auth docs.
Developer Portal
Zuplo automatically generates a developer portal from your OpenAPI spec with interactive docs, API playground, and key management.
Why this combination works
NestJS and Zuplo share a design philosophy: convention over configuration, TypeScript-native, and built around open standards.
- NestJS handles your business logic with decorators, dependency injection, and modular architecture
@nestjs/swaggergenerates a standards-compliant OpenAPI spec from your TypeScript code- Zuplo consumes that spec and handles the cross-cutting concerns (auth, rate limiting, monitoring, and developer docs) at the gateway level
Your NestJS codebase stays focused on business logic. No rate-limit middleware, no API key validation, no docs rendering in the app; Zuplo handles all of that from the spec.
For microservices it’s even better. Configure auth and rate limiting once at the gateway instead of reimplementing it in every service.
Wrapping up
You built a NestJS REST API with full CRUD operations, added OpenAPI
documentation with @nestjs/swagger, deployed it to Railway, and used Zuplo to
add rate limiting, API key authentication, and a developer portal, all without
modifying your NestJS code.
That’s an OpenAPI-first workflow end to end: decorators generate the spec, the spec drives the gateway, and the gateway handles what your consumers need.
