Server Integration
The Speakeasy SDK is the primary mechanism for passing API data to Speakeasy. With the data collected from API requests, Speakeasy is able to power a number of use cases:
- API Key Management - With Speakeasy’s API key management, your users can create and revoke API keys in your API portal.
- API Request Viewer - Enable your users to troubleshoot API integration issues without engineering support.
- API Usage Dashboard - Give your users advanced insight into their API traffic patterns.
SDKs are available for a number of languages and frameworks. They are open-sourced under Apache 2.0.
When installing the SDK you will need a few values to include in the configuration:
- APIKey: This key identifies your account within the Speakeasy platform. If you need a key, head over to the API Keys tab in the Speakeasy platform and click "New API Key".
- ApiID: This is an arbitrary ID to help you identify you API within the platform. Use something that is meaningful to your team. Supports any string up to 128 characters, using Alphanumeric characters or the following symbols (. - _ ~). For example: test-api
- VersionID: Use this field to demarcate the version of the API that you are tracking. Supports any string up to 128 characters, using Alphanumeric characters or the following symbols (. - _ ~). For example: v1.0.0
Initial Install
- Go
- Java
- Typescript
- Rust
- Ruby
- Python
- Cloudflare Workers
Full Documentation available on pkg.go.dev
The Speakeasy Go SDK for evaluating API requests/responses. Compatible with any API framework implemented on top of Go's native http library.
Requirements
Supported routers:
- gorilla/mux
- go-chi/chi
- http.DefaultServerMux
We also support custom HTTP frameworks:
- gin-gonic/gin
- labstack/echo
Usage
Speakeasy uses Go Modules to manage dependencies.
go get github.com/speakeasy-api/speakeasy-go-sdk
Minimum configuration
Sign up for free on our platform. Create a workspace and generate an API key
Import and configure the Speakeasy SDK as below. Note:
APIKey
: enter the key you generated aboveApiID
andVersionID
: these are labels that you can specify. Requests from your API will show up in the web application using this ApiID (name) and version label.
import "github.com/speakeasy-api/speakeasy-go-sdk"
func main() {
// Configure the Global SDK
speakeasy.Configure(speakeasy.Config {
APIKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
ApiID: "YOUR API ID HERE", // enter a name that you'd like to associate captured requests with.
// This name will show up in the Speakeasy dashboard. e.g. "PetStore" might be a good ApiID for a Pet Store's API.
// No spaces allowed.
VersionID: "YOUR VERSION ID HERE", // enter a version that you would like to associate captured requests with.
// The combination of ApiID (name) and VersionID will uniquely identify your requests in the Speakeasy Dashboard.
// e.g. "v1.0.0". You can have multiple versions for the same ApiID (if running multiple versions of your API)
})
// Associate the SDK's middleware with your router
r := mux.NewRouter()
r.Use(speakeasy.Middleware)
}Build and deploy your app.
That's it! Go to your dashboard to view and manage your API Ops.
Visit our docs site to learn more.
Supported Versions
Full documentation available on github.
This is the Speakeasy JVM SDK for evaluating API requests/responses. Currently only supports the Springboot API Framework.
Requirements
- Spring Boot 2.7.X
- Java 8+ (Also works with Kotlin, official support coming soon)
Usage
Add the Speakeasy SDK to your project's build.gradle file:
dependencies {
implementation 'dev.speakeasyapi:speakeasyapi-jvm-springboot-sdk:1.2.1'
}
Please check on maven central for the latest release of the SDK.
Minimum configuration
Sign up for free on our platform. Create a workspace and generate an API key
Import and configure the Speakeasy SDK as below. Note:
APIKey
: enter the key you generated aboveApiID
andVersionID
: these are labels that you can specify. Requests from your API will show up in the web application using this ApiID (name) and version label.
@EnableConfigurationProperties(EnableSpeakeasy.class)
public class SpringWebApp {
// ...
}Add your api key to
application.properties
:speakeasy-api.apiKey=[your-api-key]
speakeasy-api.apiID=[your-api-id]
speakeasy-api.versionID=[your-api-version-id]Build and deploy your app
That's it! Go to your dashboard to view and manage your API Ops.
Visit our docs site to learn more.
(Optional) Building our SDK from source
gradle clean build
Running the Java Example Service (Coming Soon!)
Supported Versions
Version | Features |
---|---|
v1.1.0 | https://github.com/speakeasy-api/speakeasy-jvm-springboot-sdk/releases/tag/1.1.0 |
v1.0.4 | https://github.com/speakeasy-api/speakeasy-jvm-springboot-sdk/releases/tag/1.0.4 |
Full documentation available on npm.
The Speakeasy Typescript SDK for evaluating API requests/responses.
Requirements
Supported frameworks:
- Express
- NestJs
Usage
The Speakeasy Typescript SDK is hosted on NPM and can be installed with the following command:
npm install @speakeasy-api/speakeasy-typescript-sdk
Minimum configuration
Sign up for free on our platform. Create a workspace and generate an API key
Import and configure the Speakeasy SDK as below. Note:
APIKey
: enter the key you generated aboveApiID
andVersionID
: these are labels that you can specify. Requests from your API will show up in the web application using this ApiID (name) and version label.
Express
Configure Speakeasy in your middleware chain as follows:
import speakeasy, { Config } from "@speakeasy-api/speakeasy-typescript-sdk";
import express from "express";
const app = express();
// Configure the global speakeasy SDK instance
const cfg: Config = {
apiKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
apiID: "YOUR API ID HERE", // enter a name that you'd like to associate captured requests with.
// This name will show up in the Speakeasy dashboard. e.g. "PetStore" might be a good ApiID for a Pet Store's API.
// No spaces allowed.
versionID: "YOUR VERSION ID HERE", // enter a version that you would like to associate captured requests with.
// The combination of ApiID (name) and VersionID will uniquely identify your requests in the Speakeasy Dashboard.
// e.g. "v1.0.0". You can have multiple versions for the same ApiID (if running multiple versions of your API)
port: 3000, // The port number your express app is listening on (required to build full URLs on non-standard ports)
};
speakeasy.configure(cfg);
// Add the speakeasy middleware to your express app
app.use(speakeasy.expressMiddleware());
// Rest of your express app setup codeNestJS
Configure Speakeasy in your NestJS app as follows:
import speakeasy, { Config } from '@speakeasy-api/speakeasy-typescript-sdk';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// Configure the global speakeasy SDK instance
const cfg: Config = {
apiKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
apiID: "YOUR API ID HERE", // enter a name that you'd like to associate captured requests with.
// This name will show up in the Speakeasy dashboard. e.g. "PetStore" might be a good ApiID for a Pet Store's API.
// No spaces allowed.
versionID: "YOUR VERSION ID HERE", // enter a version that you would like to associate captured requests with.
// The combination of ApiID (name) and VersionID will uniquely identify your requests in the Speakeasy Dashboard.
// e.g. "v1.0.0". You can have multiple versions for the same ApiID (if running multiple versions of your API)
port: 3000, // The port number your express app is listening on (required to build full URLs on non-standard ports)
};
speakeasy.configure(cfg);
// Configure the NestJS middleware for the routes of you Controller
consumer.apply(speakeasy.nestJSMiddleware()).forRoutes(AppController);
}
}Build and deploy your app
That's it! Go to your dashboard to view and manage your API Ops.
Visit our docs site to learn more.
Full Documentation also available on crates and docs.rs
Requirements
Supported Frameworks:
- Actix 3
Usage
Available on crates: crates.io/crates/speakeasy-rust-sdk
Documentation available at: docs.rs/speakeasy-rust-sdk
Run:
cargo add speakeasy-rust-sdk --features actix3
Or add it directly to your Cargo.toml
speakeasy-rust-sdk = {version = "0.1.0", features = ["actix3"]}
Minimum configuration
Sign up for free on our platform. After you've created a workspace and generated an API key enable Speakeasy in your API as follows:
Configure Speakeasy at the start of your main()
function:
use actix_web::{
get, post,
web::{self, ReqData},
App, HttpResponse, HttpServer, Responder,
};
use speakeasy_rust_sdk::{middleware::actix3::Middleware, Config, SpeakeasySdk};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
let config = Config {
// retrieve from Speakeasy API dashboard.
api_key: "YOUR API KEY HERE".to_string(),
// enter a name that you'd like to associate captured requests with.
// This name will show up in the Speakeasy dashboard. e.g. "PetStore" might be a good ApiID for a Pet Store's API.
// No spaces allowed.
api_id: "YOUR API ID HERE".to_string(),
// enter a version that you would like to associate captured requests with.
// The combination of ApiID (name) and VersionID will uniquely identify your requests in the Speakeasy Dashboard.
// e.g. "v1.0.0". You can have multiple versions for the same ApiID (if running multiple versions of your API)
version_id: "YOUR VERSION ID HERE".to_string(),
};
// Create a new Speakeasy SDK instance
let mut sdk = SpeakeasySdk::try_new(config).expect("API key is valid");
// create middleware
let speakeasy_middleware = Middleware::new(sdk);
let (request_capture, response_capture) = speakeasy_middleware.into();
App::new()
.wrap(request_capture)
.wrap(response_capture)
...
})
.bind(("127.0.0.1", 35290))?
.run()
.await
}
Build and deploy your app and that's it. Your API is being tracked in the Speakeasy workspace you just created and will be visible on the dashboard next time you log in. Visit our docs site to learn more.
Capture CustomerIDs
Capturing CustomerIDs is not required, but is highly recommended. By setting a CustomerID you can associate requests to your customers/users, which powers the filters needed to build a customer-facing API dashboard.
If you are planning to build a developer experience portal, then it's important to pick a CustomerID that is stable, and connected to your identity system. When users login to your portal, Speakeasy will then be able to filter the data to just those relevant to the user.
For example: CustomerID is preferable to api_key because an API key can be rotated and is therefore unstable.
To associate requests with a customer ID, you need to instantiate the Speakeasy middleware in your handler functions and then set the CustomerID field. Below are per language examples of setting a CustomerID in a handler function.
- Go
- Java
- Typescript
- Rust
func MyHandler(w http.ResponseWriter, r *http.Request) {
ctrl := speakeasy.MiddlewareController(req)
ctrl.CustomerID("a-customers-id") // This customer ID will be used to associate this instance of a request with your customers/users
// the rest of your handlers code
}
@GetMapping("/v1/users/{id}") // The path template "/v1/users/{id}" is captured automatically by the SDK
public String getUser(@PathVariable("id") String id, @RequestAttribute(SpeakeasyInterceptor.ControllerKey) SpeakeasyMiddlewareController controller) {
controller.setCustomerID("a-customers-id"); // This customer ID will be used to associate this instance of a request with your customers/users
// your handler logic here
}
const app = express();
app.use(speakeasy.expressMiddleware());
app.all("/", (req, res) => {
// Provide a path hint for the request using the OpenAPI Path Templating format: https://swagger.io/specification/#path-templating-matching
req.controller.setCustomerID("a-customers-id"); // This customer ID will be used to associate this instance of a request with your customers/users
// the rest of your handlers code
});
#[post("/index")]
async fn index(controller: ReqData<Arc<RwLock<MiddlewareController>>>) -> HttpResponse {
controller
.write()
.unwrap()
.set_customer_id("123customer_id".to_string());
// rest of the handlers code
}
Mask Sensitive Fields
Speakeasy can mask sensitive data in the query string parameters, headers, cookies and request/response bodies captured by the SDK. This is useful for maintaining sensitive data isolation, and retaining control over the data that is captured.
Mask By Endpoint
By not assigning the middleware to their router, you will cause the SDK to not capture any requests to that router. But if you would like to be more selective, you can mask certain sensitive data using our middleware controller allowing you to mask fields as needed in different handlers:
- Go
- Java
- Typescript
- Rust
func MyHandler(w http.ResponseWriter, r *http.Request) {
ctrl := speakeasy.MiddlewareController(req)
ctrl.Masking(speakeasy.WithRequestHeaderMask("Authorization")) // Mask the Authorization header in the request
// the rest of your handlers code
}
The Masking
function takes a number of different options to mask sensitive data in the request:
speakeasy.WithQueryStringMask
- WithQueryStringMask will mask the specified query strings with an optional mask string.speakeasy.WithRequestHeaderMask
- WithRequestHeaderMask will mask the specified request headers with an optional mask string.speakeasy.WithResponseHeaderMask
- WithResponseHeaderMask will mask the specified response headers with an optional mask string.speakeasy.WithRequestCookieMask
- WithRequestCookieMask will mask the specified request cookies with an optional mask string.speakeasy.WithResponseCookieMask
- WithResponseCookieMask will mask the specified response cookies with an optional mask string.speakeasy.WithRequestFieldMaskString
- WithRequestFieldMaskString will mask the specified request body fields with an optional mask. Supports string fields only. Matches using regex.speakeasy.WithRequestFieldMaskNumber
- WithRequestFieldMaskNumber will mask the specified request body fields with an optional mask. Supports number fields only. Matches using regex.speakeasy.WithResponseFieldMaskString
- WithResponseFieldMaskString will mask the specified response body fields with an optional mask. Supports string fields only. Matches using regex.speakeasy.WithResponseFieldMaskNumber
- WithResponseFieldMaskNumber will mask the specified response body fields with an optional mask. Supports number fields only. Matches using regex.
import dev.speakeasyapi.sdk.SpeakeasyMiddlewareController;
import dev.speakeasyapi.sdk.utils.SpeakeasyInterceptor;
import dev.speakeasyapi.sdk.masking.Masking;
@GetMapping("/")
public String handle(@RequestAttribute(SpeakeasyInterceptor.ControllerKey) SpeakeasyMiddlewareController controller) {
Masking masking = Masking.builder()
.maskRequestHeaders(List.of("Authorization", "x-api-key"))
.build();
controller.setMasking(masking);
// your handler logic here
}
import { Masking } from '@speakeasy-api/speakeasy-typescript-sdk';
const app = express();
app.use(speakeasy.expressMiddleware());
app.all("/", (req, res) => {
ctrl := req.controller;
ctrl.setMaskingOpts(Masking.withRequestHeaderMask("authorization")) // Mask the authorization header in the request
// the rest of your handlers code
}
The Masking
function takes a number of different options to mask sensitive data in the request:
Masking.withQueryStringMask
- withQueryStringMask will mask the specified query strings with an optional mask string.Masking.withRequestHeaderMask
- withRequestHeaderMask will mask the specified request headers with an optional mask string.Masking.withResponseHeaderMask
- withResponseHeaderMask will mask the specified response headers with an optional mask string.Masking.withRequestCookieMask
- withRequestCookieMask will mask the specified request cookies with an optional mask string.Masking.withResponseCookieMask
- withResponseCookieMask will mask the specified response cookies with an optional mask string.Masking.withRequestFieldMaskString
- withRequestFieldMaskString will mask the specified request body fields with an optional mask. Supports string fields only. Matches using regex.Masking.withRequestFieldMaskNumber
- withRequestFieldMaskNumber will mask the specified request body fields with an optional mask. Supports number fields only. Matches using regex.Masking.withResponseFieldMaskString
- withResponseFieldMaskString will mask the specified response body fields with an optional mask. Supports string fields only. Matches using regex.Masking.withResponseFieldMaskNumber
- withResponseFieldMaskNumber will mask the specified response body fields with an optional mask. Supports number fields only. Matches using regex.
But if you would like to be more selective you can mask certain sensitive data using our middleware controller allowing you to mask fields as needed in different handlers:
use speakeasy_rust_sdk::{Masking, MiddlewareController, SpeakeasySdk, StringMaskingOption};
#[post("/index")]
async fn index(controller: ReqData<Arc<RwLock<MiddlewareController>>>) -> HttpResponse {
// create a specific masking for this request/response
let mut masking = Masking::default();
masking.with_request_field_mask_string("password", StringMaskingOption::default());
// set new masking for this request/response
controller.write().unwrap().set_masking(masking);
// rest of the handlers code
}
The Masking
struct can be set with a number of different options to mask sensitive data in the request:
masking.with_query_string_mask
- with_query_string_mask will mask the specified query strings with an optional mask string.masking.with_request_header_mask
- with_request_header_mask will mask the specified request headers with an optional mask string.masking.with_response_header_mask
- with_response_header_mask will mask the specified response headers with an optional mask string.masking.with_request_cookie_mask
- with_request_cookie_mask will mask the specified request cookies with an optional mask string.masking.with_response_cookie_mask
- with_response_cookie_mask will mask the specified response cookies with an optional mask string.masking.with_request_field_mask_string
- with_request_field_mask_string will mask the specified request body fields with an optional mask. Supports string fields only. Matches using regex.masking.with_request_field_mask_number
- with_request_field_mask_number will mask the specified request body fields with an optional mask. Supports number fields only. Matches using regex.masking.with_response_field_mask_string
- with_response_field_mask_string will mask the specified response body fields with an optional mask. Supports string fields only. Matches using regex.masking.with_response_field_mask_number
- with_response_field_mask_number will mask the specified response body fields with an optional mask. Supports number fields only. Matches using regex.
For complete docs on masking see the docs.rs/speakeasy-sdk-rust
Mask Globally
Masking can also be done more globally on all routes or a selection of routes by taking advantage of middleware. Here is an example:
- Go
- Java
- Typescript
- Rust
speakeasy.Configure(speakeasy.Config {
APIKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
ApiID: "YOUR API ID HERE", // this is an ID you provide that you would like to associate captured requests with.
VersionID: "YOUR VERSION ID HERE", // this is a Version you provide that you would like to associate captured requests with.
})
r := mux.NewRouter()
r.Use(speakeasy.Middleware)
r.Use(func (next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Mask the Authorization header in the request for all requests served by this middleware
ctrl := speakeasy.MiddlewareController(req)
ctrl.Masking(speakeasy.WithRequestHeaderMask("Authorization"))
})
})
Coming Soon!
import speakeasy, { Config, Masking } from "@speakeasy-api/speakeasy-typescript-sdk";
import express from "express";
const app = express();
// Configure the global speakeasy SDK instance
const cfg: Config = {
apiKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
apiID: "YOUR API ID HERE", // custom Api ID to associate captured requests with.
versionID: "YOUR VERSION ID HERE", // custom Version ID to associate captured requests
port: 3000, // The port number your express app is listening on (required to build full URLs on non-standard ports)
};
speakeasy.configure(cfg);
// Add the speakeasy middleware to your express app
app.use(speakeasy.expressMiddleware());
app.use((req: Request, res: Response, next: NextFunction) => {
// Mask the authorization header in the request for all requests served by this middleware
ctrl := req.controller;
ctrl.setMaskingOpts(Masking.withRequestHeaderMask("authorization"))
next();
});
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
let config = Config {
// retrieve from Speakeasy API dashboard.
api_key: "YOUR API KEY HERE".to_string(),
// enter a name that you'd like to associate captured requests with.
// This name will show up in the Speakeasy dashboard. e.g. "PetStore" might be a good ApiID for a Pet Store's API.
// No spaces allowed.
api_id: "YOUR API ID HERE".to_string(),
// enter a version that you would like to associate captured requests with.
// The combination of ApiID (name) and VersionID will uniquely identify your requests in the Speakeasy Dashboard.
// e.g. "v1.0.0". You can have multiple versions for the same ApiID (if running multiple versions of your API)
version_id: "YOUR VERSION ID HERE".to_string(),
};
// Create a new Speakeasy SDK instance
let mut sdk = SpeakeasySdk::try_new(config).expect("API key is valid");
// Configure masking for query
sdk.masking.with_query_string_mask("secret", "********");
sdk.masking
.with_query_string_mask("password", StringMaskingOption::default());
// Configure masking for request
sdk.masking
.with_request_field_mask_string("password", StringMaskingOption::default());
// Configure masking for response
sdk.masking
.with_response_field_mask_string("secret", StringMaskingOption::default());
// create middleware
let speakeasy_middleware = Middleware::new(sdk);
let (request_capture, response_capture) = speakeasy_middleware.into();
App::new()
.wrap(request_capture)
.wrap(response_capture)
...
})
.bind(("127.0.0.1", 35290))?
.run()
.await
}
Manage Versioning
The Speakeasy SDK provides both a global and per Api configuration option. If you want to use the SDK to track multiple Apis or Versions from the same service you can configure individual instances of the SDK.
- Go
- Java
- Typescript
- Rust
import "github.com/speakeasy-api/speakeasy-go-sdk"
func main() {
r := mux.NewRouter()
// Configure a new instance of the SDK for the store API
storeSDKInstance := speakeasy.New(speakeasy.Config {
APIKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
ApiID: "store_api", // this is an ID you provide that you would like to associate captured requests with.
VersionID: "1.0.0", // this is a Version you provide that you would like to associate captured requests with.
})
// Configure a new instance of the SDK for the product API
productSDKInstance := speakeasy.New(speakeasy.Config {
APIKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
ApiID: "product_api", // this is an ID you provide that you would like to associate captured requests with.
VersionID: "1.0.0", // this is a Version you provide that you would like to associate captured requests with.
})
// The different instances of the SDK (with differnt IDs or even versions assigned) can be used to associate requests with different APIs and Versions.
s := r.PathPrefix("/store").Subrouter()
r.Use(storeSDKInstance.Middleware)
s := r.PathPrefix("/products").Subrouter()
r.Use(productSDKInstance.Middleware)
}
Create your own WebMvcConfigurer
implementation for each instance of the SDK, like the example below:
package dev.speakeasyapi.sdk;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import dev.speakeasyapi.sdk.utils.SpeakeasyFilter;
import dev.speakeasyapi.sdk.utils.SpeakeasyInterceptor;
@Configuration
@ConfigurationProperties(prefix = "myspeakeasyinstancev1")
@ConfigurationPropertiesScan
@Import(SpeakeasyFilter.class) // This enables request and response capture and is a requirement for the SDK to work
public class MySpeakeasyInstanceV1 implements WebMvcConfigurer {
private String apiKey;
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SpeakeasyInterceptor(apiKey, "users", "v1.0.0"))
.addPathPatterns("/v1/users/**") // Match paths
.excludePathPatterns("/v1/products/**"); // Exclude paths
}
}
Express
import { Config, SpeakeasySDK } from "@speakeasy-api/speakeasy-typescript-sdk";
import express from "express";
const app = express();
// Configure a new instance of the SDK for the store API
const storeSDK = new SpeakeasySDK({
apiKey: "YOUR API KEY HERE", // retrieved from Speakeasy API dashboard.
apiID: "store_api", // this is an ID you provide that you would like to associate captured requests with.
versionID: "1.0.0", // this is a Version you provide that you would like to associate captured requests with.
port: 3000, // The port number your express app is listening on (required to build full URLs on non-standard ports)
});
// Configure a new instance of the SDK for the product AP
const productSDK = new SpeakeasySDK({
apiKey: "YOUR API KEY HERE", // retrieved from Speakeasy API dashboard.
apiID: "product_api", // this is an ID you provide that you would like to associate captured requests with.
versionID: "1.0.0", // this is a Version you provide that you would like to associate captured requests with.
port: 3000, // The port number your express app is listening on (required to build full URLs on non-standard ports)
});
// The different instances of the SDK (with differnt IDs or even versions assigned) can be used to associate requests with different APIs and Versions.
const storeRouter = app.route("/store");
storeRouter.use(storeSDK.expressMiddleware());
const productsRouter = app.route("/products");
productsRouter.use(productSDK.expressMiddleware());
// Rest of your express app setup code
NestJS
import { Config, SpeakeasySDK } from '@speakeasy-api/speakeasy-typescript-sdk';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// Configure a speakeasy SDK instance
const cfg: Config = {
apiKey: "YOUR API KEY HERE", // retrieve from Speakeasy API dashboard.
apiID: "YOUR API ID HERE", // custom Api ID to associate captured requests with.
versionID: "YOUR VERSION ID HERE", // custom Version ID to associate captured requests
port: 3000, // The port number your express app is listening on (required to build full URLs on non-standard ports)
};
const sdk = new SpeakeasySDK(cfg);
// Configure the NestJS middleware for the routes of you Controller
consumer.apply(sdk.nestJSMiddleware()).forRoutes(AppController);
}
}
Coming Soon!
Provide Path Hints
The Speakeasy SDK out of the box will do its best to match requests to your provided OpenAPI Schema. It does this by extracting the path template used by the Mapping annotation for your controller handlers and attempting to match it to the paths defined in the OpenAPI Schema, for example:
- Go
- Java
- Typescript
- Rust
r := mux.NewRouter()
r.Use(sdkInstance.Middleware)
r.HandleFunc("/v1/users/{id}", MyHandler) // The path template "/v1/users/{id}" is captured automatically by the SDK
@GetMapping("/v1/users/{id}") // The path template "/v1/users/{id}" is captured automatically by the SDK
public String getUser(@PathVariable("id") String id) {
// your handler logic here
}
Express
const app = express();
app.use(speakeasy.expressMiddleware());
app.all("/v1/user/:id/action/:action", myHandler); // The path template "/v1/user/{id}/action/{action}" is captured automatically by the SDK after being normalized to the OpenAPI spec format for paths.
NestJS
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get('/v1/user/:id/action/:action') // The path template "/v1/user/{id}/action/{action}" is captured automatically by the SDK after being normalized to the OpenAPI spec format for paths.
myHandler(): string {
// handler code here
}
}
// The path template "/v1/users/{id}" is captured automatically by the SDK
#[get("v1/users/{id}")]
async fn handler_function(id: web::Path<String>) -> impl Responder {
// handler function code
}
This isn't always successful or even possible, meaning requests received by Speakeasy will be marked as unmatched
, and potentially not associated with your Api, Version or ApiEndpoints in the Speakeasy Dashboard.
This normally happens if your path contains regex patterns or is a catch all path and your handler parses the routes manually.
To help the SDK in these situations you can provide path hints per request handler that match the paths in your OpenAPI Schema:
- Go
- Java
- Typescript
- Rust
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Provide a path hint for the request using the OpenAPI Path Templating format: https://swagger.io/specification/#path-templating-matching
ctrl := speakeasy.MiddlewareController(req)
ctrl.PathHint("/v1/users/{id}")
// the rest of your handlers code
}
Wildcard path matching in Echo & Chi will end up with a OpenAPI path paramater called {wildcard} which will only match single level values represented by the wildcard. This is a restriction of the OpenAPI spec (Detail Here). For example:
chi template: /user/{id}/path/* => openapi template: /user/{id}/path/{wildcard}
And in the above example a path like /user/1/path/some/sub/path
won't match but /user/1/path/somesubpathstring
will, as /
characters are not matched in path parameters by the OpenAPI spec.
import dev.speakeasyapi.sdk.utils.SpeakeasyInterceptor;
@GetMapping("/")
public String handle(@RequestAttribute(SpeakeasyInterceptor.ControllerKey) SpeakeasyMiddlewareController controller) {
controller.setPathHint("/v1/users/{id}"); // This path hint will be used to match requests to your OpenAPI Schema
// your handler logic here
}
The following example will work for NestJS as well, just get the Speakeasy MiddlewareController from the request object.
const app = express();
app.use(speakeasy.expressMiddleware());
app.all("/", (req, res) => {
// Provide a path hint for the request using the OpenAPI Path Templating format: https://swagger.io/specification/#path-templating-matching
req.controller.setPathHint("/v1/user/{id}/action/{action}");
// the rest of your handlers code
});
If using nested Routers in express or Controller path prefixs in NestJS the SpeakeasySDK will not be able to get the full path template for the request due to a current issue in express. To work around this you can manually set path hints as above or we can monkey patch in a modification to express to enable the SpeakeasySDK to get the full path template:
/* eslint-disable */
// @ts-nocheck
import express from "express";
/* Credit to @watson and @jagadish-kb https://github.com/expressjs/express/issues/2879#issuecomment-269433170 */
const origUse = express.Router.use;
express.Router.use = function (fn) {
if (typeof fn === "string" && Array.isArray(this.stack)) {
let offset = this.stack.length;
const result = origUse.apply(this, arguments);
let layer;
for (; offset < this.stack.length; offset++) {
layer = this.stack[offset];
// I'm not sure if my check for `fast_slash` is the way to go here
// But if I don't check for it, each stack element will add a slash to the path
if (layer && layer.regexp && !layer.regexp.fast_slash)
layer.__mountpath = fn;
}
return result;
} else {
return origUse.apply(this, arguments);
}
};
var origPP = express.Router.process_params;
express.Router.process_params = function (layer, called, req, res, done) {
const path =
(req.route &&
(req.route.path || (req.route.regexp && req.route.regexp.source))) ||
layer.__mountpath ||
"";
if (req.__route && path) {
const searchFromIdx = req.__route.length - path.length;
if (req.__route.indexOf(path, searchFromIdx) > 0) {
return origPP.apply(this, arguments);
}
}
req.__route = (req.__route || "") + path;
return origPP.apply(this, arguments);
};
Create a file called expressmonkeypatch.ts
or similar and import it into your service's main.ts
file import "./expressmonkeypatch";
. This will path express and allow the SDK to determine the full path automatically.
#[post("/special_route")]
async fn special_route(controller: ReqData<Arc<RwLock<MiddlewareController>>>) -> HttpResponse {
// Provide a path hint for the request using the OpenAPI Path templating format:
// https://swagger.io/specification/#path-templating-matching
controller
.write()
.unwrap()
.set_path_hint("/special_route/{wildcard}");
// the rest of your handlers code
}
Generate Embed Tokens
The Speakeasy SDK can generate access tokens for Embedded Components.
Below are examples for how to generate access tokens:
- Go
- Java
- Typescript
- Rust
import "github.com/speakeasy-api/speakeasy-schemas/grpc/go/registry/embedaccesstoken"
ctx := context.Background()
// If the SDK is configured as a global instance,
// an access token can be generated using the
// `GenerateAccessToken` function on the speakeasy package.
accessToken, err := speakeasy.GetEmbedAccessToken(ctx, &embedaccesstoken.EmbedAccessTokenRequest{
Filters: []*embedaccesstoken.EmbedAccessTokenRequest_Filter{
{
Key: "customer_id",
Operator: "=",
Value: "a-customer-id",
},
},
})
// If you have followed the `Advanced Configuration` section above,
// you can also generate an access token using the
// `GenerateAccessToken` function on the sdk instance.
accessToken, err := storeSDKInstance.GetEmbedAccessToken(ctx, &embedaccesstoken.EmbedAccessTokenRequest{
Filters: []*embedaccesstoken.EmbedAccessTokenRequest_Filter{
{
Key: "customer_id",
Operator: "=",
Value: "a-customer-id",
},
},
})
// Or finally if you have a handler that you would like to generate an access token from,
// you can get the SDK instance for that handler from the middleware controller
// and use the `GetEmbedAccessToken` function it.
func MyHandler(w http.ResponseWriter, r *http.Request) {
ctrl := speakeasy.MiddlewareController(req)
accessToken, err := ctrl.GetSDKInstance().GetEmbedAccessToken(ctx, &embedaccesstoken.EmbedAccessTokenRequest{
Filters: []*embedaccesstoken.EmbedAccessTokenRequest_Filter{
{
Key: "customer_id",
Operator: "=",
Value: "a-customer-id",
},
},
})
// the rest of your handlers code
}
@GetMapping("embed_access_token")
public String getSpeakeasyEmbedAccessToken(@RequestAttribute(SpeakeasyInterceptor.ControllerKey) SpeakeasyMiddlewareController controller){
String customerId=null;
// populate your customerId
// Restrict data by customer id
SpeakeasyEmbedAccessTokenRequestBuilder requestBuilder = new SpeakeasyEmbedAccessTokenRequestBuilder();
requestBuilder.withCustomerFilter(customerId);
// Restrict data by time (last 24 hours)
Instant startTime=Instant.now().minusSeconds(60*60*24);
requestBuilder.withTimeFilter(startTime,SpeakeasyEmbedAccessTokenRequestFilterOperator.GreaterThan);
String embedAccessToken=controller.getEmbedAccessToken(requestBuilder.build());
// build response
}
import { EmbedAccessTokenRequest } from "@speakeasy-api/speakeasy-schemas/registry/embedaccesstoken/embedaccesstoken_pb";
// If the SDK is configured as a global instance, an access token can be generated using the `generateAccessToken` function on the speakeasy package.
const req = new EmbedAccessTokenRequest();
const filter = new EmbedAccessTokenRequest.Filter();
filter.setKey("customer_id");
filter.setOperator("=");
filter.setValue("a-customer-id");
req.setFiltersList([filter]);
const accessToken = await speakeasy.getEmbedAccessToken(req);
// If you have followed the `Advanced Configuration` section above you can also generate an access token using the `GenerateAccessToken` function on the sdk instance.
const req = new EmbedAccessTokenRequest();
const filter = new EmbedAccessTokenRequest.Filter();
filter.setKey("customer_id");
filter.setOperator("=");
filter.setValue("a-customer-id");
req.setFiltersList([filter]);
const accessToken = await storeSDK.getEmbedAccessToken(req);
// Or finally if you have a handler that you would like to generate an access token from, you can get the SDK instance for that handler from the middleware controller and use the `GetEmbedAccessToken` function it.
app.all("/", (req, res) => {
const req = new EmbedAccessTokenRequest();
const filter = new EmbedAccessTokenRequest.Filter();
filter.setKey("customer_id");
filter.setOperator("=");
filter.setValue("a-customer-id");
req.setFiltersList([filter]);
const accessToken = await req.controller.getSDKInstance().getEmbedAccessToken(req);
// the rest of your handlers code
});
Coming Soon!