Basic Client#
This guide provides a step-by-step walkthrough for setting up a basic client to interact with the Fleet API. Follow the instructions to authenticate and connect to the API in order to make requests. Some general gRPC and protobuf knowledge are recommended for working with this example.
Project setup#
First download the Fleet State API proto file from here: fleet_state.proto
mkdir fleetstate-client-go && cd fleetstate-client-go
cp your-downloads/fleet_state.proto .
Generate the go code for the supplied proto file using protoc or buf.
Note
We will use the statev1beta1
package name as output directory for our generated code. Make sure to replace this with the current version of the proto file.
Code Generation with buf
Download the buf configuration files for generating code:
buf.yaml file from here:
buf.yaml
buf.gen.yaml file from here:
buf.gen.yaml
cp your-downloads/buf.yaml your-downloads/buf.gen.yaml .
buf mod update
buf generate
Code Generation with protoc
protoc --go_out=./fleetstatev1beta1 --go_opt=paths=source_relative \
--go-grpc_out=./fleetstatev1beta1 --go-grpc_opt=paths=source_relative \
-I .
fleet_state.proto
Finalize the code generation by creating a go module.
go mod init moia.io/fleetstate-client
go mod tidy
Create our client file. We can use go run
to quickly run our go code.
touch client.go
go run client.go
We will use some dependencies during this tutorial, depending on your setup you might need to repeatedly run go mod tidy
or go get
in order to install them.
Requires Node.js 12 or higher.
We will use the ts-proto
and nice-grpc
libraries to generate a TypeScript client for the FleetState API with either protoc or buf.
The commands below will set up a new project and install the required dependencies.
Please also make sure to copy the protobuf definitions of the Fleet API to the fleetstate-client-ts
directory (last command). The protobuf definitions can be found here fleet_state.proto
.
mkdir fleetstate-client-ts && cd fleetstate-client-ts
brew install protobuf
cp your-donwloads/fleet_state.proto .
Code Generation with buf
Download the buf configuration files for generating code:
buf.yaml file from here:
buf.yaml
buf.gen.yaml file from here:
buf.gen.yaml
cp your-downloads/buf.yaml your-downloads/buf.gen.yaml .
npm install @bufbuild/buf
npx buf mod update
npx buf generate
Code Generation with protoc
Run protoc with the TypeScript plugin to generate the client code. Checkout ts-proto for more information on the options used below.
npm init -y
npm install nice-grpc ts-proto
protoc \
--plugin=./node_modules/.bin/protoc-gen-ts_proto \
- I . \
--ts_proto_out=. \
--ts_proto_opt=outputServices=generic-definitions \
--ts_proto_opt=outputServices=nice-grpc \
--ts_proto_opt=esModuleInterop=true \
--ts_proto_opt=useExactTypes=false \
./fleet_state.proto
We then create our client file and can use ts-node
to quickly run our TypeScript code.
touch client.ts
npx ts-node client.ts
File structure#
package main
import (
context "context"
"log"
"net/http"
"time"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
grpc "google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
"google.golang.org/protobuf/encoding/protojson"
"moia.io/fleetstate-client/fleetstatev1beta1"
)
func main() {
// The code of the next steps goes here
}
import * as grpc from "nice-grpc";
import { FleetStateServiceDefinition } from "./fleetstate";
async function main() {
// The code of the next steps goes here
}
Authenticate#
First you need to obtain a token from the API gateway. To do this we will use the golang.org/x/oauth2
library. You can find more information about authentication here.
You can also use an OAuth2 client, as long as you authenticate with the client_credentials
grant type and use the authorization header with the Basic
scheme.
clientId := os.Getenv("CLIENT_ID")
clientSecret := os.Getenv("CLIENT_SECRET")
const tokenUrl = "https://fleet-api.int.eu-central-1.moia-group.io/" +
"auth/oauth/token"
const testVehicleId = "819f0cda-62dd-4251-9efd-9c4294b4f42d"
oauthConfig := clientcredentials.Config{
ClientID: clientId,
ClientSecret: clientSecret,
TokenURL: tokenUrl,
AuthStyle: oauth2.AuthStyleInHeader,
}
httpCtx := context.Background()
httpClient := &http.Client{Timeout: 30 * time.Second}
httpCtx = context.WithValue(httpCtx, oauth2.HTTPClient, httpClient)
// Used in the next steps
accessToken, err := oauthConfig.Token(httpCtx)
if err != nil {
log.Fatalf("could not fetch token: %v", err)
}
First you need to obtain a token from the API gateway. To do this we will send an HTTP request to the tokenURL
using the fetch()
API. You can find more information about authentication here.
You can also use an OAuth2 client, as long as you authenticate with the grant type client_credentials
and use the authorization header with the Basic
scheme.
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const tokenUrl =
"https://fleet-api.int.eu-central-1.moia-group.io/" +
"auth/oauth/token";
const testVehicleId = "819f0cda-62dd-4251-9efd-9c4294b4f42d";
const authHeader = Buffer.from(`${clientId}:${clientSecret}`).toString(
"base64"
);
const http2AuthClient = http2.connect(tokenUrl);
const tokenRequest = http2AuthClient.request({
":method": "POST",
":path": "/auth/oauth/token",
"authorization": `Basic ${authHeader}`,
"content-type": "application/x-www-form-urlencoded",
});
tokenRequest.setEncoding("utf8");
const requestBody = "grant_type=client_credentials";
tokenRequest.write(requestBody);
tokenRequest.end();
let tokenResponse = "";
for await (const data of tokenRequest) {
tokenResponse += data;
}
const parsedTokenResponse = JSON.parse(tokenResponse) as Record<string, any>;
if (parsedTokenResponse.error) {
console.error(parsedTokenResponse.error_description);
return;
}
http2AuthClient.close();
// Used in the next steps
const accessToken = parsedTokenResponse.access_token;
Connect to the API#
Setup SSL credentials for transport and supply our token with every request using grpc.WithPerRPCCredentials
.
perRPC := oauth.TokenSource{
TokenSource: oauth2.StaticTokenSource(accessToken),
}
const apiUrl = "fleet-api.int.eu-central-1.moia-group.io:443"
conn, err := grpc.Dial(
apiUrl,
grpc.WithTransportCredentials(
credentials.NewClientTLSFromCert(nil, ""),
),
grpc.WithPerRPCCredentials(perRPC),
)
if err != nil {
log.Fatalf("could not connect to %s: %v", apiUrl, err)
}
defer conn.Close()
fleetStateClient := fleetstatev1beta4.NewFleetStateServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), unaryTimeout)
defer cancel()
// Used for printing responses
jsonMarshalOptions := protojson.MarshalOptions{
Multiline: true,
EmitUnpopulated: true,
}
We use nice-grpc
to set up a gRPC channel and client. We use the grpc.Metadata
class to add the Authorization
header to all of our gRPC requests.
const apiUrl = "fleet-api.int.eu-central-1.moia-group.io:443";
const clientFactory = grpc.createClientFactory().use(deadlineMiddleware);
const channel = grpc.createChannel(
apiUrl,
grpc.ChannelCredentials.createSsl()
);
const client = clientFactory.create(FleetStateServiceDefinition, channel, {
"*": {
metadata: new grpc.Metadata({
Authorization: `Bearer ${accessToken}`,
}),
},
});
Continue with the API Usage example where we will call some of the Fleet API methods.