Tying Services to Client Side Logging
This chapter shows the use of static events, demonstrating how Causal can be used with microservices. It shows how to request impressions in a backend service (microservice or other service) and log the corresponding events in the presentation layer. Causal will still tie all the data together, and you still get the benefit of annotated and strongly typed interfaces.
The assumption of this example is that we want to make minimal changes to the browser's AJAX payload because there is already infrastructure in place to ship the relevant content and we don't want to change it. If you don't have such constraints, please refer to the other SSR examples.
Please read Basic React Usage and SSR first. They provide context necessary for this example
The repo for all Causal code examples is here.
$ git clone git@github.com:CausalLabs/examples.git
If you'd like to run the app
npm install
npm run dev
You can view the running app at http://localhost:3005/events-microservice?pid=iphone
Code walk through
To view the full source code, please visit events-microservice
In this example we are going to:
- Collect the feature's data server side.
- Pass the data to the page with out disrupting current APIs and hydration.
- Use Causal's static event functions on the client to signal events.
This mimics how front end code can interact with a micro-service. You can have a microservice get data from Causal. That microservice may already have an interface, so you don't want to add new piping.
Causal will still tie all the data together, and you still get the benefit of annotated and strongly typed interfaces.
With this approach is the type system cannot carry the feature selection through from the original query, so you have to manually pick the correct features to signal events on.
Identify, request and process the impression
An impression corresponds to a user viewing a page or product. The front end and back end need to agree on impressions. They do this by identifying impressions with unique ids.
Causal can generate impression ids, or you can use your own by passing one in. Passing in your own impression id has several advantages:
- It is easier to join data in the data warehouse with your existing data
- Often you will have a similar id, like a requestId, already available to both the presentation layer and the backend. This obviates the need for new plumbing to pass the id around - a goal of this example.
Please see Impressions, Memoization, and Caching for more details on impressions.
Create the query and request an impression. For simplicity, we are just using a fixed string for the impression id.
export async function getServerSideProps(
context: GetServerSidePropsContext
): Promise<{ props: SSRProps }> {
const deviceId = getOrGenDeviceId(context);
const product =
products[context.query.pid as keyof typeof products] ?? products["iphone"];
const session = new Session({ deviceId }, context.req);
const { impression, flags, error } = await session.requestImpression(
qb().getRatingBox({ product: product.name }),
"imp-1234"
);
Populate the props, and return the data to the front end.
The assumption of this example is that we want to make minimal changes to the browser's AJAX payload because there is already infrastructure in place to ship the relevant content and we don't want to change it. So we take the data out of the impression and put it into the pre-existing type.
return {
props: {
showRatingBox: flags.RatingBox,
product,
CTA: impression.RatingBox?.callToAction ?? "",
},
};
}
Render and signal events
The rest of this example is very similar to the previous examples. The difference is the use of static methods to signal events. Notice the static call signature for RatingBox.signalRating
export default function ProductInfo({ showRatingBox, product, CTA }: SSRProps) {
const [rating, setRating] = useState(0);
const router = useRouter();
return (
<div className="center">
<h1>{product.name}</h1>
<img src={product.url} alt="product image" />
<h3>{CTA}</h3>
{showRatingBox && (
<>
<RatingWidget
curRating={rating}
onSetRating={(newRating) => {
setRating(newRating);
RatingBox.signalRating(
{ deviceId: getOrGenDeviceId(router) },
"imp-1234",
{
stars: rating,
}
);
// For autocomplete convenience, you can also reference the feature
// classes through the allFeatureTypes variable.
//
// allFeatureTypes.RatingBox.signalRating(deviceId, impressionId, {
// stars: rating,
// });
}}
/>
<a href={router.route + "?pid=" + product.next}>Rate Another</a>
</>
)}
</div>
);
}
Congrats
You've finished the tying services to client side logging walk through.