Make Static Pages Dynamic With Netlify Edge Functions
with Alex Shyba
Resources & Links
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON: Hello, everyone. And welcome to another episode of Learn with Jason! Today on the show we've got Alex Shyba. How you doing?
ALEX: I'm good, Jason. How are you? Good to be here.
JASON: I'm doing great. I did that thing today where I like had a few minutes before a meeting and then I was like, I'll just do one quick thing. And then I was late. So, thanks, everybody, for being patient while I show up late to my own show. So, anyways, Alex, I'm super-excited to have you on the show. For folks who aren't familiar with your work, do you want to give us a bit of a background?
JASON: Very cool. Yeah. So, there's -- yeah. So, there's a lot -- a lot to talk about in this space. So, I'm gonna start with the general question and let you guide me down a topic hole here. But what we are talking about in general is this idea of Edge Functions, right? And so, the technology is kind of not the point. What we're really interested in is the outcome here. So, from -- from my perspective, one of the biggest challenges we have been trying to solve is you have the -- what's the -- somebody had a really good phrase for this. I think it was slow versus stale tradeoff, right? So, as we have been building for the web, you have this -- if you want ultra-fresh content, you have to effectively make all of the queries, do all of the rendering, all of this work has to happen at request time. And the problem is that makes things slow, right?
ALEX: And complicated, yep.
JASON: And complicated and flaky and there's a lot of reasons why you wouldn't want to do that. But then you've got this other problem which is if you do all the work ahead of time, you get a whole bunch of benefits. This is why people I think got really excited about the Jamstack approach in general. You're removing work. You're removing flakiness. You're reducing your operational overhead and your deployment costs and all these things. But then content can be stale, right? So, you've got this longer lag between when something updates and when somebody sees it on their screen. What we're seeing is sort of at the highest level this sort of discussion about the tradeoffs. Should you do everything in the server so that the data is always fresh? Or should you do everything ahead of time with pre-rendering and, you know, building early so that your content is very fast? Right? So, fresh versus fast, or slow versus stale, depending on whether you're talking about the upsides or downsides. What I found interesting when we start talking about Edge Functions is that some of those tradeoffs go away. And that feels like a big shift, right? So, I would love to hear, you know, it sounds like this is a space that you have been really stuck into at Uniform. I would love to hear sort of what your take is on this evolution and where the lines for the tradeoffs are starting to be drawn.
ALEX: Absolutely. We went from this heavy server-origin-centric world. I come from the Microsoft space. So, think like very expensive, heavy web servers. That, you know, not cheap to scale. Then you look into kind of the global customer footprint perspective. Some brands that actually want to reach everybody globally. That becomes even more expensive. And then you add state into it where you actually have to have a centralized like session store. Like that kills everything. That kills performance, that kills scale. And then you have traffic spikes you need to scale out. Sometimes you have cold starts that are, you know, multiple minutes. That's the reality of like I come from that space with like heavy enterprise investments, right? Jamstack kind of gave completely fresh perspective. And when we went on the completely opposite side of the spectrum, we're like, forget about servers. Let's pre-render everything. And push Jamstack as hard as we can all the way to the other side of the spectrum where everything is pre-rendered. There's no servers to run stuff. And you have essentially elasticity of CDN and all of these good benefits that we are all aware of. And I felt like now we kind of re-evaluating and trying to expand the use cases of Jamstack and Edge Functions is that enabling technology. Not just because it's cool. But it's actually creates -- a no-compromise approach to this sort of architecture. Where you have the speed, scalability of Jamstack. But you have the dynamism of server side rendering without needing to actually render stuff. It's almost like we're adding extra level of transformation. We have the ability to obviously now do edge rendering. Which is another kind of avenue to explore. And with interesting challenges as well. But with what I'm excited about is that this particular Edge Functions context of the Jamstack, it extends the use cases of Jamstack. It makes the experiences more dynamic. So, it's about making sure we can serve fresh content. But also, we can personalize and individualize each web page if we need to. If we have the right data and we can write rules, configuration, we can actually transform these static -- traditionally static web pages into dynamic experiences without paying the cost of going all the way back or in the middle of the spectrum. Let's provision a bunch of Node servers and run SSR again. Let's not go back in time. Let's move forward. That's what I'm excited about it.
ALEX: It very progressively -- it progressively makes your existing Jamstacks better and brings skeptics on board as well that may be held back on the Jamstack train a little bit.
JASON: So, when -- when you're looking at this, I know -- so, maybe we should talk a little bit just for folks who aren't familiar about what Uniform does and why this is so relevant to your space.
ALEX: Yeah. Definitely. We have maybe easiest way to think about it is that missing piece in between you may not realize you need before you see our demo. It -- we help bring this whole -- like Jamstack elevated this -- this need for composable architecture which is about true best of breed systems coming together. Right? And that means you pick the best -- almost like a UNIX philosophy versus Windows philosophy. Where instead of getting something from one vendor in a box, we has, you know, a CMS, maybe a rendering engine, maybe marketing automation and a bunch of other things, maybe analytics. You assemble your own stack. It means that you can achieve more agility in terms of just different moving pieces, swapping components around. But you are the architect of your own stack in a way.
ALEX: In this composable space. So, this is where we are playing as an enabling technology to help brands embrace this mindset and lower the tax of entry. Because we know like Linux is not for everybody, right? So, there is definitely that kind of translates into this sort of architecture. Composable requires a lot more thinking up front. Who is gonna be the mediator? Who is gonna be the orchestrater in this architecture we have? Content distributed, you have content system, you have content management system. Maybe you have two content management systems, right? And the larger the companies, the more CMSs they have in their ecosystem. So, how does it all fit together? And where brands can have a way to evolve their architecture without throwing it away every 5 to 7 years. When they can experiment with different components. So, we are that stitching piece that prevents you from building your own glue code. And having somebody else inherit and dealing with that within the organization. So, we help you be the thing in the middle. And also help close the gaps with headless. Traditionally tools for marketers, traditional A/B testing were not fully integrated in the headless content management systems. So, that's where we started the company. As that pain killer and gap-closing technology to provide that missing piece. So, you can assemble a stack out of this best of breed technologies that headless, modern. But functionally you're not compromising. You have all the tools for business users as well. And you empower not just developers, but marketers. So, this is where we're -- we build this engine that allows you to run personalization as fast as possible on the edge of your choice. And then you bring the fuel for that engine externally. Either through some of those external systems like custom data platforms. Maybe HTTP request is a nice source of that data which has a large ability to act on. This is where we landed on the true edge-based execution. Where what if we run this layer without going anywhere? And depending on the system, essentially have everything ready. Content, configuration, all pre-baked. And all we need is a lightweight transformation layer at the edge to make the magic happen. So, how do you get to 100 milliseconds or less time to first byte for personalized page?
ALEX: That's really the only way to do it. So, you don't pay the cost of a render. You don't pay cost for content fetch, which could have unpredictable parameters. And you can scale with the elasticity of content delivery networks. But retain the dynamism of natural. That's our value proposition together with the larger proposition engine that allows you to build pages without the involvement. We stay true to the headless nature. We don't have frontend in a box, but we have the ability to render anywhere you would like.
JASON: I gotcha. And you touched on something that is really valuable. It's part of this shift to a Jamstack architecture means is this idea of combining the best tool for the job instead of trying to find one tool that's pretty close and then like shoe horning it into a bunch of different applications. And when you're talking about people needing to roll over their stack every 5 to 7 years, in my experience, that's a direct result of people trying to use things for purposes they're not built for. And you start getting these sort of -- I don't know what you would call them. Barnacle layers on top of systems. So, if you've got something that's built to be a blog and then somebody hacks it to be a forum. It's not that it was bad at being a blog. It's just that it's not good at doing all the things that aren't blogging. And so, you find yourself with these complicated work arounds and those all become in-house code that when the person who built it leaves, or if they transfer it to another team. Now you've got people who have never seen the code before, it's not documented, all this mystery, it's hard to maintain. If turns over two or three times. The first person builds it, and they move somewhere else and someone inherits it. Now you're three or four layers away from the person who made the code. This is legacy code, we don't know how it works. It's faster to build it over. But if you treat your services as composable and day they have all these really clear API boundaries, your blogging system should be the best-in-class blogging system out there. That's based on your needs. Are you a really developer-focused company? Sanity and Contentful is great. Coming over from WordPress? Keep WordPress. Use a WordPress blog as your API and plug that into your layer. When you need a forum, get a forum system, don't make WordPress or Sanity into a forum, right? And by doing that, what we get, instead of having to declare stack bankruptcy, you can declare tool bankruptcy. Okay. We've outgrown the capability of this tool. We're gonna switch out our blog from WordPress to Contentful. And that's gonna give us XYZ flexibility. And that choice doesn't require you to throw away your frontend. It doesn't require you to throw away all the other systems you have built. It lets you rip and replace the one component that needed to be replaced. And I think that's such a magical thing to consider. It means that you end up with the ship of Theseus. If you aren't familiar, with all the ship pieces replaced, is it the same ship? All the components of the website are evolved and rebuilt, but one component at a time. You have the same site, the same experience. It's just constantly evolving and getting replaced. And that means, as you said, it's now incremental. It's not stop everything, no feature work. burn this thing down and green field it. We have a new tech stack. We have this slow 6 months to a year of doing all the work required to get the full stack replaced and rolled out, et cetera, et cetera, et cetera. It's like, you get this one thing, take a few weeks to rip that piece out. The rest of the business is at usual. You never see the slowdowns for the major rewrites and rebuilds. That to me is such a game changer. And a lot of that comes in because of this ability to stitch things together as you were saying. And the less in-house custom code you're writing to make services work together, the more you're focusing on just the pieces that you need to write in-house. And that means you're building more value, not more tech debt.
ALEX: Absolutely. Yeah. We have been saying this like as an industry like focus on solving the right problem. Or solve your custom problems like that business value-generating problems. But it felt like, reflecting on the past decade, it's kind of -- it's left to be a promise, right? There is only a few brands that kind of crack that nut of always innovating and putting the budgets where it actually matters. A lot of the folks in the space still are stuck keeping the lights on. And like hoping that their next upgrade of their digital experience platform is going to give them all the answers. And it's --
ALEX: It's about changing mindset. And about -- what I like about this movement is let's stick to composable architecture and embrace it. Embrace it. It's harder to repeat mistakes from the past of building it on a monolith, right? And building something that's not sustainable. Having one of the reasons why we started Uniform is we have seen so much waste in this space. Millions are spent. And then mediocre results are there. And then like you're really not innovating. So, it needs to be -- it's a different approach that's required. And I think a lot of the element of like make -- having like the strong foundation of like static site generators --
ALEX: It's a solid foundation we can build upon and make it better by adding the elements of this edge transformation logic. Where we'll have answers. Where do I run my redirects? Why do I run my business logic that redirects to certain regions? That requires Ad Hoc programmability. And now we have an answer to that and you don't have to keep your server's infrastructure in place just to keep that logic running somewhere in your Dotnet code. Now you have the way to properly modernize things and do things incrementally. This is super-exciting times. You feel like we now have a chance to have quality with different architectures and different outcomes for businesses to approach.
JASON: Yeah. This gets me so excited. You know, the chat, if you've watched the show before. You've probably seen me get really excited about this. We've done a lot of episodes around edge functions and what makes them so cool. And why the shift toward this type of architecture is so amazing. So, let me do this. I'm going to just drop a link to a previous discussion about this stuff. Because otherwise I'm gonna spend this whole episode waxing philosophical and I really, really, really want to make sure we have time to build. We are about 25 minutes into the stream here. So, I'm going to go ahead and switch us over into the pair programming view. Give me just a second to get to that view here. And before we get started, a shoutout to our captioning. We've got Amanda here today doing all the live captioning from White Coat Captioning. And that is made possible through the support of our sponsors. We've got Netlify, Nx, and New Relic, all kicking in to make this show more accessible to more people. Which I very much appreciate. And just a reminder for all you companies out there, I am still looking for another sponsor. So, if you need to get your name in this little list here. Look at these great companies. This could be you. Hit me up. Let me know. We are talking today to Alex Shyba. You can find Alex on the old Tweeter. And if you want to watch along with the captions, they are on the home page of the site, learn with Jason.dev, and I shared a link to the edge functions. A good collection here. This one I think is a really good one. Sunil at Cloudflare. And the talk about how the move to the edge is really powerful. Highly recommend getting out there. That's the wrong button. Let me move this off screen, actually. So, Alex, at this point, I've got no idea what to do next. [ Laughter ] So, before we started, I cloned a repo that you sent to me that was a starter repo. And that's all I know. So, I'm gonna -- I'm gonna go ahead and open this one. What should I do next?
ALEX: Right. So, I think one thing we can do like maybe more gentle introduction of Netlify Edge Functions. We can deploy a hello world Edge Functions on a static file. Have an index JS and an Edge Function in front of it just so we have a common base before we go into a more of an opinionated starter kit.
JASON: Not in here. Start another one?
ALEX: Yeah. Start a new repo.
JASON: So, we'll call this edge-functions-hello-world-personalization. Move into it. I'm gonna get init. And then I will -- let's build it out from here. So, I'm gonna start here. We'll do an index.html. Put this together. Anything specific you want on this page for the hello world?
ALEX: Just the save. Make sure it's static message, hello world works.
JASON: Okay. And so, we'll just do -- do a hello world. So, then, if we run the Netlify Dev command, it's gonna pick up that we've got the index.html. It spits it out for us and now to make changes. Show up on a refresh.
JASON: So, you wanted to then set up a Edge Function?
ALEX: Yeah. Let's do an Edge Function that maybe overrides this and returns something completely different in the response. Where we completely hijack a response and return, completely different message. And it's pretty cool examples repo where it's just getting started. So, if folks want to check it out. Check out edge-functions-examples .Netlify.app.
JASON: Make sure that ends up in the show notes and in the chat. So, I've got my hello world here. Which literally does nothing but return a response that says hello world. So, let's get it configured. I have done all of this from memory. Let's see how it goes today.
ALEX: That's impressive.
JASON: Let's go path is going to be here. And the function is going to be hello-world. All right. Have I written enough Edge Functions in the last few days to memorize it. Let's pick up the last edge function.
ALEX: After you start modifying the function, it's a different experience.
JASON: Once it picks up the initial Edge Function. This originally was set up with only that -- that static server. It wasn't listening on the Edge Function stuff. But now that we've set one, whenever we make changes, it will pick up automatically. We can do a hello chat, come back out here. Refresh. And now that it's actually reloaded, we see the hello chat. All right.
ALEX: We completely took over request response model. This is what you used to do in like a web server where you plug in some sort of pipeline and do your magic and then modify response, maybe end up redirecting, injecting more stuff. You couldn't do this before. Now this runs on every edge note as far as I understand. It's fully integrated with the CDN itself. It's not like another origin. This literally sits in front of your CDN node.
ALEX: It means latency is minimal.
ALEX: That is awesome. One fun thing we can do, maybe, instead of hijacking the response, maybe we'll -- there was one more example that talks about transformation of -- this is kind of more in line with where we're going. What if we have a static page? It's generated through Next.js export and we are doing some post-export fun on it. Such as manipulation of the HTML source. What if we do the upper text our existing static HTML so we can transform it instead of doing it kind of this way. Maybe we just do like upper case of existing HTML. So, we have access to the whole request model. And Netlify Edge context. Which gives us some good grease for our future fun as well.
JASON: We need to install. Netlify/functions. So, that we have access to the -- oh, no, we're in Deno. What am I doing? I don't need to install anything because we use URLs. So, scrap that. We're just gonna run this thing.
ALEX: Nice. Now we're shouting at people.
JASON: That's cool that worked.
ALEX: Impressive memory. I wouldn't be able to do this without the -- some -- on the site.
JASON: The recipe file just created this VSCode settings for anybody who is interested in seeing what happens under the hood. Just so that you don't have to do it yourself. And that gives us import maps and stuff and pieces that we need so that we are able to use this without having to install types and stuff like that.
ALEX: That's very cool.
ALEX: I had actually this question how do I do this with TypeScript. TypeScript was shouting at me on line 1.
JASON: Exactly. Yeah. That's a helpful thing we do. Probably a little DX boost would be once you run any Edge Function, hey, do you want to add VSCode settings? You could just automatically do that. But baby steps, right? This stuff is pretty dang new. All right. Now we're transforming content, right? The thing that's really cool about this, if I come in here now. And I say something different... it automatically picks up what's going on. And then we can even do a little bit more where we've got like the -- the geo here. Where I can get the city will be context.geo.city. Right? And then I can do something like... let's see. Const. New text equals. Is this gonna do what I want? We'll find out. Text, plus, city. And then we'll send back new text. And does it do what I want?
ALEX: No, maybe geo is undefined. Oh, whoa. Okay.
JASON: It's because I was trying to do it before it reloaded.
JASON: So, I was refreshing early. But yeah. Here we go.
ALEX: And the geo headers even if you're running locally. That's pretty cool.
JASON: Right? It's pretty dang cool stuff. So, this is some really powerful stuff that we can do to figure out where, you know, like do I want to personalize? Would I like to show a thing? I just built this demo where it's a -- I have to actually go through and like make a little article about this. But what it does, if my Internet will cooperate. Come on, Internet. Running too many things all at once here. So, this is a little demo that if you want to try this, chat, you can go -- whoops. Go hit that URL. And it will give you -- it should show you a dot where you are on this map when you open it up. It's a quick personalization thing that you can do. So, that if you're building any kind of service, like, yeah, go get it done. And that code is... mapbox, here. And it's, you know, like I said, this is work in progress. So, please, please ignore the man behind the mirror. The man behind the curtain? Man in the mirror, that's a Michael Jackson song. [Laughs] So, here we go. We've got some really cool stuff going on here.
ALEX: Yeah. And think about it. Traditionally what does it mean? You need a server, you need a service. You need to get your geo protection. That's neat. It's on the request. It works as fast as possible. And probably if we deploy this, we should enjoy comfortable time to first byte of this page for real, maybe. Should we?
JASON: Yeah. Absolutely. Let's do it. We can get init -- we've already got it get inited. I'm going to add everything. Add everything. Say POC. And then I'm gonna GitHub repo create. I want to create -- I want to use this local repository. I'm gonna call it the same thing that I called it here. So, give it that name. Off it goes. It's public. Got a remote, there we go. Yes, yes, yes. And then what I'm gonna do is I screwed up my auth somehow. So, the Learn with Jason org doesn't show up the way I want it to on... on Netlify. And I have been too lazy to like log out and log back in figure out what's going on. So, what I'm gonna do is I'm gonna go in here and I'm going to create a new site. Import an existing project. Gonna use GitHub. Don't need Netlify's org today. So, we'll skip that. And back we go to Learn with Jason. And then I can say personal. That should get me enough. Edge Functions, hello world, personalization. Great. Can deploy the site. And this should build -- let's see, it's a first build. So, let's say under a minute here.
ALEX: Yep. Builds are getting faster and faster. I'm loving where the space is evolving.
JASON: Yeah. It's been really, really nice.
ALEX: So, yeah, this personalization aspect is very -- now we're kind of approaching that as developers personalizing the page. But we -- there was a larger opportunity here. What if we took all of this power that we're about to go and deploy and put in the hands of the non-technical business users? So, they can activate these capabilities running with all this special sauce without developer involvement? That would be something. Wouldn't it?
JASON: It looks like we got a bit of a backlog here on the build. Oh, here we go. A lot of people pushing right now. So, it must have had a bit of a build queue. So, onwards. And now, while we're waiting for this to build. Yeah, we're already to the Edge Function bundling. You know what? By the time I was going get to my question, we're already done. So, let's just go ahead and check out the live site here. You're done. What are you doing? There it is. And here is our live site. It's updated. And everybody can go and look. I'm seeing some subs in here today. Thank you so much for the subs. Cynthia, Lucky T13 and -- for the gift sub. Thank you. It's personalizing at the edge. You should see your city listed here when you open this URL. And again, it should be nice and fast. Yeah. Tony, you can't use the please hold. It's too slow. We need a turbo "Please hold." City undefined. I wonder -- I wonder if there's a geo restriction where like some -- I've never tried this out of a metro area. I don't know what happens if you're in a suburb. So, that's a good -- okay. Fallback's needed. But yeah. So, you know --
ALEX: I live in San Francisco. It works.
JASON: And the French countryside. I wonder if 4G like cell phone towers don't give the same geodata. Interesting. That's an interesting thing to poke around on. Yeah, worked in South Carolina. Love it, love it, love it. Okay. We've got our hello world here, we're doing some text transformation, doing some personalization. But obviously this is pretty heavy-handed. So, what should we do next? What's our next move here?
ALEX: Yep. So, this is where I think we have an opportunity to take this mental model and apply it to maybe a more realistic website. You probably have a framework, a framework like Next.js running the show, right? Imagine you're running in the static mode and you want to take advantage of this goodness. But you also want to make sure this is configurable and can be activated on any component. Not just kind of globally per page. And by a business user without code change. So, this is where we can go back to our starter. But before we do, let's go ahead and create Uniform project. And if you don't have a new account, we can sign up. We have a free account. Uniform app, or .dev sign-up. From here. Yep. We got to start from scratch.
JASON: Let's see. We'll use GitHub.
ALEX: Let's see...
JASON: And here we go.
ALEX: These are optional.
JASON: All right.
ALEX: We can go into that and explore how it's all set up. So, yeah, lots of onboarding things. So, first thing's first here. We have an integration. Every starter is an integration. We can go in, actually wire up our app frontend by going into the integration settings and find that hello world starter that was installed. Yep. Hello World Starter. Here we have the ability. We already started it. We can open that on Stack Blitz. We can even fast track this and check it out without even pulling anything down locally.
JASON: Yeah. And so, I -- I've already got here, right? This is my -- okay. So, I'm gonna close the hello world so that we are focused on this one. I already have this. But I need to generate my environment variables. I assume I need to do it offscreen?
ALEX: It's going to be read-only API key. No harm in that.
JASON: So, we feel our API keys here. They have been copied. Good. Good, good.
ALEX: And the view file. Yeah, this is kind of just -- we already CD'd into that directory. This is a slightly modified starter. This actually has the Netlify Edge Function already in there. So, we could skip a few steps. But this -- this -- the one that is on -- not on our screen, it's more vanilla. It doesn't have a Netlify Edge Function. But we'll have a little bit more extra. But we'll walk through that process. So, that's -- we're ready to npm run, npm install, npm run Dev within that --
JASON: And so, let me... npm install. What have I done? Oh. I'm in the -- I'm in the wrong thing. We need to get back into the Netlify Edge Function Next.js starter. Run, install, and then I'm going to run Netlify Dev. And it picks up it's a Next site. It's going to run all the pieces that we need and then it will start the Edge Functions as well for us.
ALEX: Yep. So, first thing it's doing is downloading what's known as manifest. This is personalization configuration that this starter ships with. So, it gets bundled within the app. When you run personalization, you don't really need to go back to -- oh. Have some TypeScript fun here.
JASON: Explicitly has an any context. Okay. So, we'll come back here. And we will fix that. So, we can make this -- what we need to do. So, we'll make that a request. And then we'll make this one context. And we're gonna get that from -- do you have the Netlify? We'll -- oh, wait. I already have this in my clipboard history, actually. Hm. Maybe I'm not gonna do that. Don't know what's in my clipboard history. So, let's instead go back to here. Copy from here. And drop this up at the top and so, that will do that for us. Can then I'm gonna run that Netlify. That's -- there we go. Now our Edge Functions are happy. We can come back out here and reload.
ALEX: All right. Something is happening.
JASON: And there's it's compiled.
JASON: All right. Things have happened!
ALEX: It happened. Here is default, and all the link does, it adds query string parameter into our page. We can do that. You can see, it says launch and now we see completely different content. This can be a completely different component if you want to it to. That's personalization action. The signal is query string-based. Marketers use them a lot. Essentially it takes place now with the context of our running locally, through Netlify Dev. This will run also when we statically export the site and run it off Netlify as well. So, let's take a look how it's put together. So, that -- this is where we can enter our context middleware function. So, this is that function that actually does the magic. And first thing's first here. We are just making sure that we don't run this request on non-page requests. Because if Netlify sits in front of all our static resources, this is not pretty, but that's the most-effective way we've found to ignore making sure we only run a request when page requests are happening. And making sure it's only happening on gets and not on posts. So, that's where we're short circuiting on when we're entering that. There's probably a better way of doing it. But we couldn't find --
JASON: So, one idea. One idea for folks who are looking at other ways, we have access to the headers as well. We can check the content type and make sure it's application HTML. We could potentially send custom headers that say like, you know, X used Uniform and set that to true. There are a lot of ways that you could dig into that that are, you know, like but this is a perfectly valid way to do it. You're just checking for these. The trick will come if you added a .xml for a RSS feed, you would have to remember to come in and edit this. Or I mean, or not if it's not doing anything.
ALEX: It's probably something you need to deal with like targeting of all this edge function logic. You need to figure out where do we activate certain logic on which requests? That's something to keep in mind. You don't want to be over-processing, having to process every single request, even static requests, if you don't intend to. That's an important thing to keep in mind. And then what we do is create Edge context. Edge context contains that configuration, line 20, yeah, 23. That necessary configuration describing the rules, the personalization rules on how do we want to personalize? So, that's a part of that manifest JSON that gets bundled into a it. And that's something Uniform generates on build. So, that describes how you want to personalize. And -- and we need an -- original response from Netlify and then we run our handler to post-process that response with our own special sauce items here. And you can see here we run it through a handler. And then we give it back to Netlify. And return him in response. And here we just log if we actually process requests. It's just helpful to troubleshoot. Making sure pages are getting actually processed so there's some logging in place here. And that's pretty much it. So, that's how we return modify response body. And what's missing here, we're trying to sort out how to actually re-enable compression. Because we lose compression at this point. The page comes back without gzip or broadly. And that's an extra piece we'll be adding into this example function. Make sure you're not doing that.
JASON: And the Deno team I think just shipped support for that. So, I believe... I think that was an issue early on. But I believe that that is no longer an issue. So, it should be -- should be possible today to get that working. I don't know exactly what the steps are. We would have to look that up and that is not our goal for today. So, just know that it's possible. And if you can't figure it out, DM me, we'll get you sorted.
ALEX: Awesome. That's great. So, that's something you have to activate once. And now every component you drop in this page, every new page you create, you have access to all this logic, right? That you've activated once. You can think about it. This is new feature release. And then you enabled business user to actually put this feature to work. Which we can do inside of Uniform canvas interface where we actually manage compositions and components and manage all of this configuration.
ALEX: So, yeah. Personalization. This is where it starts. We only have one signal. And that's how we personalize. You need to have at least one signal. And kind of like a Seinfeld moment. Signals, Jerry, signals. You need a signal. I love Seinfeld. That's why I -- there's an opportunity to plug in Seinfeld joke, I will. You build the signal side of criteria. They can be based on query string, cookie, geodata that we push into quirks. We will revisit quirks. But this is as simple as you have. It's free. You have HTTP request. You have data on that request. You put the data to work. Every time the campaign is launched, we have a score added to our visitor profile. And that allows us to then construct Canvas, our composition tool, what do we want to show in this criteria? This is configuration, and then we put it to work inside Canvas screen where we actually manage our home page composition. So --
ALEX: Let's go into -- yeah. Does this make sense, Jason? So, we can -- we can also modify this criteria if we wanted to.
JASON: I -- so, I like I get it. But I want to repeat it back just to make sure that I'm understanding exactly how this works. So, when we talk about a signal, personalization is one of those things that it gets very complex very fast, right? So, you know right now we have one signal. But you can imagine if we were trying to build out e-commerce. I would have lots of signals. There would be whether or not I'm interested in, you know, men's clothing or women's clothing. Whether or not I'm interested in summer or winter clothing. Whether or not I want like sporty stuff or like high-fashion stuff. And so many other things, right? And so, open of those is its own signal, right? Like I'm -- every time I click on something, I'm giving you a hint as the site that I'm interested in a thing. So, if I open a pair of leather wingtip shoes, you could get the hint that, okay. I am looking into high fashion men's shoes, right? And so, each of those could boost my signal. And my signal could be, say, plus 10, right? So, we've got a visitor's score. So, if I open up -- then the next thing that I open up is like a, you know, some hoodie or whatever. Now you've got another high-fashion signal. But now it's like street wear, right? Great. Now we've got different signals. You now know pretty confidently that I'm more interested in fashion than other things because I've clicked on more than one thing that gets into that high fashion thing. So, you can nudge high fashion things up the list. And because I looked at shoes, because I looked at men's shoes, you can maybe just experiment. Maybe you nudge the shoes a little higher on the list. Maybe you nudge the men's stuff a little higher on the list. And that is personalizing. But then if I'm like, actually, I was just looking at it because it was a cool-looking shoe. What I'm really interested in is hats. And then I click on four hats. Then my hat signal gets higher and you're personalizing hats up higher. Despite the fact that I'm still signaling that I want high fashion, I want these things. By using signals, we're not saying -- you don't have to build a custom page each time, right? You can kind of say, these prosecute multivariate pieces that cause somebody to, you know, it's more of like a radar chart. Or like a, you know, it's a lot of different scores. It's not one personalization score.
JASON: And that's what really make this is stuff, first, extraordinarily powerful. And second, extraordinarily hard to do in a performant way. Because if you've got multiple scores, that means everybody who is looking at a page is looking at a 100% bespoke page after they have given a few signals, right?
ALEX: Right. It's completely individualized. And based on the signals can be weak, or it can be strong. If you checked out a couple of product pages. But what if you favorited a couple of products. Maybe that's something you want to add 25, 30 on the score. Added product to cart, that's a stronger signal. Depending on your use case, you can create a whole strategy out of that. and I remember you and Tim geeked out about that in the past. Pretty much you create your own. It's Canvas. Where you can start really small. You're running planning page and team personalization, you can. If you want to get more nuanced signals that are based on behavior. And you can really -- it's a framework you can build on and get progressively -- it's -- it's crawl, walk, run. And, you know, signal, this is kind of very much the starting point for that.
ALEX: Yep. Thanks. Helpful background. It helps to -- to run it through your prism as well.
JASON: Yeah. For sure. And I think, you know, with stuff like this of especially, because we're talking about how -- how just complex this stuff is. If you've never actually worked through something like this, you know, for Devs who have never tried personalization, how would you start? Like what would you do first? Right? And I honestly don't -- I don't know what I would have done had I not had the conversation with Tim about Signals. Because I probably would have done something completely different like having a -- well, yeah. I need to build out like different variants of the store and then you just personalize the best you can by redirecting people to the right page. But this idea of being able to get, you know, richer signals based on somebody's behavior means that we can make better decisions and give people better results. Right? So, that is pretty dang cool. Oh! Oh! Oh! And Tim just said that the chat, you can pre-personalize based on like not having signals. So, geodata. I was talking about winter versus summer line. We could use GEO to figure out which hemisphere you're in, and figure out what seasons are happening in which season. If you're in summer, show you water sports. If you're in winter, show you the snow boards and the skis.
ALEX: You want to do that?
JASON: Yeah. That's a cool --
ALEX: All right. That's a cool use case. We do that with quirks. And quirks is like arbitrary key value pair that gives us something. And in this project, I don't think we have any quirks, we might.
JASON: I don't think we set any. Because I saw a object for it.
JASON: Oh, boy.
ALEX: Resolved from --
JASON: Okay. So, the way that we're gonna have to figure this out is we're gonna have to get hemisphere. And I'm gonna do the -- the absolute worst thing that I can think of. Which is to -- all right. So, let's actually start. We'll get country. And that's gonna be from context geo... Netlify context. Netlify context... geo.country. Right? And then what we have to do is we'll go with north equals -- oh, boy. I'm just gonna put a few in, y'all. I'm so sorry. Let's see... Ben's in Belgium. That's BE. Who else? We got somebody in -- what is it? Denmark? We got folks in Mexico, I think. Is that MX? Right. That's gonna be our northern hemisphere. Shoutout if you're in a different country. And then in the southern hemisphere, we need... oh, we need more. Okay. We need -- Italy just got shouted out. What are country codes for southern hemisphere? We've got Australia. We've got -- we want New Zealand. We've got... what are the -- AG is Argentina?
JASON: Is that CH? MX is northern hemisphere, though. We got that one.
ALEX: CHL, actually.
JASON: CHL -- wait, what? Aren't they all two-character?
ALEX: That's a different iso standard. All right.
JASON: Oh, no. Peru. What is Peru?
ALEX: CL. That's for Chile.
JASON: CL is Chile. Peru, PE? All right. And then we are going to do... oh, god. Okay. Here we go. Here we go. Adrian for the win. Thank you so much for all of those. Let's see. So, here's -- I think this is the whole list. And then I could take each of these... give 'em a quick quote. And there we go. All right. So, what I'm going to do is we're going say, your hemisphere is going to be -- let's check if you are in the North. Includes... country. Right? And then what we'll say is... north. Or we'll go south. You know what? Why did I need two lists of countries? I don't know. Because what I just did doesn't need that at all. We're going to do that. So, by default, we'll check to see if you're in a northern hemisphere country. Otherwise we'll put you in the southern hemisphere. And that will let us test because I can drop the US out and force into south so that we can see the personalization happening. So, terrible code. Do not write that. If you ship that code, don't send me hate mail. But this is -- this is one very hackie way to get to that proof of concept. Do not do as I do, do as I say. Oh, jeez. All right. So, let's get -- let's get this quirk in. And so, now that I've run it. Failed reloading. Type undefined. You're not undefined. And I don't know anything about -- undefined is not assignable.
JASON: Can I force it? My TypeScript works. Put Mac on the show to teach advanced TypeScript. There. I think we forced it. So, that then, if I reload... should...
ALEX: We need to register. That's edge function, we need to register logic. We need to add quirk into our system. We need to go back to signals into quirks and actually registering it.
JASON: Here we go. Got it.
ALEX: So, one thing. Yeah. Let's name it hemisphere -- making sure public ID matches that key that we have in code. Which it does. And then we can even do a list instead of text field and have a list right before data type.
ALEX: And then enter pipe separated list of values. So, north, and colon south. That's it.
ALEX: That's our quirk. Let's save. Let's go ahead and create a signal based on it.
ALEX: Brand new signal.
JASON: We go to quirks. If we get hemisphere equals north... then we've got a signal.
ALEX: Yep. So, that means score of 50 will be added to the signal. Let's call it north hemisphere. Yep.
ALEX: And save and close. And after that, all of these changes are gonna be a part of the manifest. So, it means we need to publish. Make sure both quirks and this new signal gets into that JSON file with manifest. So, let's make sure we hit publish here.
ALEX: And now -- now let's put it to work. We have in Canvas section we do have our composition where we have our home page modeled. So, let's complete it -- yeah. In here, we have personalization container. And that's -- that's how we configure personalization. There's two variants. One is personalized, one is default. Get another one. Another hero. That's the only component we have.
JASON: Okay. Let's see -- it's summer time in the northern hemisphere. And so, we'll do something like that. And then description. That's the --
ALEX: The syntax below that.
JASON: That's this text here.
ALEX: at this point, this could be content from your headless CMS if you install an integration. And you install integration from Sanity, Contentful, we have 14 different CMSs. We're not a CMS. This is kind of just decorative content for your component. And all we need to do is configure personalization below. When do we actually show this variant?
JASON: I gotcha. We're in northern hemisphere.
ALEX: If we want to be pedantic, maybe put a number here. In this case, it will score anything more than zero. We should have 50 every time we resolve hemisphere.
ALEX: That's it. Save and publish. And we should be able to...
JASON: And then here, we can say, because our default is gonna be --
ALEX: Let's put, yeah.
JASON: We'll go winter wonderland. And oh, it didn't -- it's in the paragraph tag, got it. Okay. So, we'll say something like, snuggle up by the fire with a hot cocoa. Right? All right. So, now we've got -- put this in a -- as well. So, I can save. Okay.
ALEX: Let's publish -- there is a publish is a separate command in the drop down. That's on the same save button.
JASON: I got to go in here.
ALEX: It's really hard to find.
JASON: Okay. Got it. Published. All right.
ALEX: Now we can restart our app. And -- because we need fresh manifest. We need that fresh cycle of npm so we can pull the latest manifest from the server. We need the quirks and those signals.
JASON: Got it, got it, got it.
ALEX: And let's see what happens.
JASON: Let's see if my code works. Oh.
ALEX: What we see is personalized based on launch. We already matched that signal. So, we need to reset. And we have this nice facility, this red circle on the right. You can actually use that to reset --
JASON: Oh, perfect.
ALEX: These things. So, clear all dimensions. Actually reset it to default. And then we need to trigger -- oh. So, that's -- that's the default.
JASON: Okay. So, that's the default. And now we get... okay. So my code didn't work.
ALEX: Quirks, yeah. In the same extension, in that red extension, we should be able to see quirks as well. So, we can see -- we do see south in here.
JASON: Okay. So, let's log -- let's log what I did. Because my guess is I did something wrong. And so, let's log the hemisphere. And let's also log the country and make sure that I'm actually getting that properly. So, then it's re-loading. There it is. And when we come out here, country -- oh, it's a code. I got to get the code of the -- of the country. So, country code. Object is possibly undefined?
ALEX: Let's put a bang in the end, maybe? I usually put an exclamation mark at the end. Every time I need.
JASON: Okay. We'll just default to US if it's undefined. How about that? Now you can't yell at me anymore, TypeScript.
JASON: Oh, wait! There we go. Okay. So, now we get our summertime classics, right? And that is great. So, we saw our default. We hit winter wonderland. We got... the check-in when the check is actually coded properly. Then we end up in the hemisphere that I'm in. And that should also show down here hemisphere north, country US, right? Perfect. So, for anybody else using this, it would be wherever you are. So, if you're in the southern hemisphere, it should show you the winter wonderland. If you're in the northern hemisphere, it should show you this summertime classics page. And we spent more time fixing my bugs than we did actually coding this up, right? That's pretty -- that's pretty fast.
ALEX: And on top of that, we can extend the quirks with the standard Netlify quirks. So, we actually take all this geodata from Netlify context and make them available as quirks. So, we can add them to the quirks object and destructure them. Yeah. I have them here in the import. So, essentially, using all of the Netlify geodata as standard quirk within Uniform. So, what we can do is bring that that into quirk's object.
JASON: We bring it in.
ALEX: Yeah. And I think we need to destructure it. Put dot, dot, dot in front. And that would... I think that would do it. So, essentially in addition to these custom quirk that we just created, which is hemisphere, we have standard Netlify quirks which come with Netlify integration. So, we add Netlify integration to our project. We should have additional full quirks that I think we ship with.
JASON: Integrations and let's find --
ALEX: Netlify --
JASON: By --
ALEX: Add to project.
ALEX: A bonus item we can even track build statuses if you wanted to. That's optional. So, quirk's already installed. but this allows us, that top widget, to actually monitor how is your site doing.
JASON: Oh, cool.
ALEX: Set up webhooks, et cetera. It requires Netlify. Not sure if you want to expose your Netlify access now.
JASON: Yeah, I'm probably not gonna do that just because it would show a token. But that is very cool. And if you were to want to create one of those, you would go to your site settings and then I think it's in here. No. Where is it?
ALEX: I think in the profile top right under your bubble, I think? And under your settings opinion
JASON: User settings, that's right. So, applications. We can register access tokens. So, you would do it right here. So, user settings, applications, and then personal access tokens there. But we're not gonna do that because you hackers would immediately wreak havoc on all of my stuff. So, no. Stop it. [Laughs] Let's jump in over here. So, now we've got this set up. I'm going back to my Canvas. We've got the home page. And if I go in and look at -- let's just create a new one... add criteria. Oh, no, no. sorry. I need to add a signal before I do this. I need to bail. Back to the signals, which were --
JASON: Add a new one. And I'm gonna go into my quirks. And here are all of my pieces. So, this is the -- the geodata. So, I didn't need to do -- well, I guess I could have done hemisphere and then put my country codes as a list as the quirk itself? Right?
ALEX: This is a little bit dangerous free form entry.
JASON: Yeah. So, I prefer the -- yeah. Doing some -- doing some code because then you have to remember where you entered. But where this would be really useful is like if we're running a special in -- what? Like Belgium. you know, we could show a special for whatever the Belgium branch of the company is doing. This could be really useful if you're working on like a multi-national brand. Where you need to show, like, okay. We are running a special today in all of our California stores. And we -- we want to make sure that anybody in California who visits the site sees that this special is available. But you don't to want show it to people in New York because they're not gonna get that deal. They'd call customer service, they would be upset. This makes it really easy, you know? You come in here. You say, all right. Let's show it to -- what is it? Subdivision name, I think? Would give us the state. Region or subdivision? One of these works. And that would be, like, CA. Okay. Great. Now let's make a variation of the hero that only shows up if you're in California, right? So, really, really fast to start thinking about how do you do these -- these targeted-rollouts of things? Which in the past would have been -- like you would just -- the conversation would immediately stop. The engineering team would be like, no. We don't have time to do that. Like there's no way you gave us the 9 weeks of notice that we need to go build out a geo-aware server system and all the DNS routing and other things that we would have to be able to figure out to make that work. They just immediately shut down the conversation. Whereas with this, you don't need to talk to engineering. You can literally go and build this yourself. That's so freaking cool.
ALEX: Yeah. Exactly. Yeah. And the power just kind of begins here. A lot of organizations have that -- this personalization engine needs fuel, right? And that fuel could come from existing services. Maybe you do have an Endpoint that returns customer profile data, maybe a CDP that gets you computed traits from your visitor profile and you want to activate that as quickly as possible. Because you can do fetch also from Edge worker. You can actually bring additional elements of this visitor context. And through quirks, pipe it through, and activate it. So, do you have clear bit is another example, actually, I have integration with them. They have reveal APIs. And based on your IP address, you get company metadata. For account-based marketing. Great, you can market to startups with, you know, more than 20 million seed round. Something like that. Why not? So --
JASON: 20 million seed round. [ Laughter ]
ALEX: Those probably exist. but --
JASON: Oh, boy. But you can see really quickly how powerful this becomes. Because if what we're doing here is we're registering things. Like I can, using literally any system that I can hit via API. I can say, you know, my logged in user has this account ID. And we've got through our analytics, we know that they use this tool. Or they are interested in these things. Or, you know, like you said, Clearbit, we've got data enrichment. We can start doing some things that are truly mind boggling. And we don't have to like do a full client side re-render of the page. We don't have to move our entire site to a completely server-based stack and then figure out how to scale that. We can just put this edge function together. Drop in any data we want here. Like we -- any data. Right? We could figure out what their favorite dog is based on their Instagram likes or something. Like obviously, don't do that. That would be a privacy concern. But you know what I mean? You can do some really interesting stuff here. Based on data that is publicly available. Or data that you have -- they have opted into sharing with you through like being in a user of your service. That is freaking cool. There's so much cool stuff that you can do with this personalization to really make apps more useful to people and to really give them more flexibility and power. It's -- like it's so -- I love it. I -- this gets my gears turning so fast.
ALEX: And anchor to that our initial chat on kind of scaling back. And this is what you need the server for that. Historically, you have an API with a secret API key. You cannot do it from the browser. Now you have this facility to act as your server middleware negotiator to actually do these things in a way where, you know, the browser doesn't have to. I Slacked you the CDP mock Endpoint that we built. This is like a segment-esque CDP Endpoint that just returns visitor traits based on visitor ID. So, we can put it to work. And we had user 1 and user 2 programmed in there. And we can have some fun here too.
JASON: Okay. So, I've got -- we've got -- this is okay to share on screen? This URL?
ALEX: Yes. Running it on Netlify. On the Netlify function. And it kind of simulates the latency of segment API around a hundred, 200 millisecond time to first byte. So, let's go ahead and --
JASON: Duplicates here. Let's get res. And then we can do an if res, okay. And then we can console.error oops. And just continue living our lives, I guess. And then down here, we will get data equals await -- is it -- it's gonna give me back JSON?
JASON: Okay. So, now I've got this data. What should I pull out of this data?
ALEX: Data the traits. And we should do dot, dot, dot data.traits here. And traits object contains stuff that we actually can put as quirks. So, that would be self-computed traits. Something your CD professional did and prepared so you have this fast user profile service.
ALEX: And one of the traits. We can open that our requests, we can actually see the payload. The one that potentially -- big spender is one of them. Maybe we can target. And create a group for. And we have -- yeah. bigSpender --
JASON: Oh, yeah, referring demain. Whether or not they have been invited. What campaign they came through on. Like this is all really interesting stuff. And then you said you set up another one for like a user ID2.
JASON: User ID2. Okay.
ALEX: Maybe based on the cookie you have the visitor identification already happening by whatever that tracker script does, right? So, you pick it up from the cookie and pass it through, resolved the visitor traits. And you can activate from that. We can keep it hard-coded. But since you have access to the whole request, you can parse the cookies and get the actual user identification.
JASON: Go in here, get the quirks. We would be able to add a new one and we would say, you know, big spender. And then, you know, whatever. It's a text field.
ALEX: You can get a list with true and false. As an example. Yeah.
JASON: Okay. We've got our bigSpender. True or false. And then in here, we'll be spreading those traits., we'll pull that one in. And that would let us then -- so, we published this. Let's go to signals. We can add a new one. And we can say, if the cookie name... I'm not gonna do that. If the quirks -- bigSpender -- is true, then what we are gonna do is --
ALEX: We can also combine them and add another quirk. And let's say bigSpender is in the northern hemisphere. And we can combine both quirks we have created for example.
JASON: Right. We could do bigSpenders in the US. Or something like that. I'm gonna leave this off. I guess it doesn't matter because we're hard coding these. Yep. We'll do that. If they are a big spender in the US, then we will be able to do something. We can go to the Canvas. And we can do a home page. And in the home page -- can I put something under the hero that will... like if I want to put more content below, can I do that?
JASON: Okay. So, let's do a personalization. And we're going call this "Big Spender Note," number of variations to show. Two, is that right?
ALEX: This is an extra placeholder value. It defaults to one. We can only put one hero in here.
JASON: Okay. And that one only shows up if big spender is greater than 50 which will be true. Okay. And then we'll add another one. We'll say hero. And we'll say... I don't know why this default is Canadian. But here we go. So, what is -- how do I make this -- do I have to put this one below to be the default?
ALEX: You got it. Default should be at the bottom. Yeah. Exactly. There we go.
JASON: There it goes. Now we can save it. And then we can publish it.
ALEX: And also --
JASON: And because you're sending --
ALEX: Let's publish the manifest. We didn't publish the manifest with the new signal. We need to do that as well.
JASON: Yes. Back to the personalization. Publish. Okay. Stop and restart. And this one should show us our bigSpender. Okay.
ALEX: There we go.
JASON: Hey, big spender. And then if we change this to user ID2, who is not a big spender. Once this finishes reloading...
ALEX: So, we would need to actually reset the value. Because he already had the score from the previous run, right?
JASON: Oh, that's right, that's right, that's right. So, we need to go in here. Clear all dimensions.
JASON: And then close it up.
ALEX: There we go.
JASON: There we go.
JASON: That's extremely cool. I will deploy this. I will get it up as a link on the episode description. But we are out of time. So, for everyone. Make sure you go and follow Alex on Twitter. Also, make sure you go and check out Uniform.dev so that you can learn more of that. Where else should people go if they want to learn here? Where would you recommend they go to get more resources?
ALEX: There is also headless creator. It was a great resource to learn on all things headless CMS, headless commerce. So, amazing resources. Lots of courses. So, definitely highly recommended. There's a part of Uniform, but it's a different brand. So, there's a lot of juicy content there. If you want to elevate your headless -- headless CMS, headless commerce knowledge, that's a really good one to consider.
JASON: Got it. Tim is dropping the Discord link. So, I'll put that, again, this -- the reason I have to post it is that I have an automation into Discord, thanks to Aden, that sends every link I share automatically to Discord so that it gets included in the show notes. With that, another quick shoutout to our sponsors. We've had White Coat Captioning, Amanda's been here today taking down all these words, thank you very much, Amanda. And that's made possible through the support of our sponsors, Netlify, Nx, and New Relic, all kicking in to make this show more accessible to more people. Make sure what while you are on the site, you go check out the schedule. We've got some really, really good stuff coming up here, we've got Will Johnson coming, gonna teach us about Auth0 Actions. And we are going to do some generic stuff, I'm terrified. Thuy is going to teach us about custom media players. Which is really cool. And I just got Max Ferrera to teach us about Astro -- his demo is so freaking cool. I can't wait to show it to you all. And with that, I'm gonna call it a raging success. This was super-fun. I love talking about this stuff. So, excited about what is possible. Any parting words for the chat before we take off?
ALEX: I had a blast. This definitely made my week. Thanks for the invite. And yeah. I hope everyone has a fantastic rest of the week.
JASON: Happy to have you here. Chat, stay tuned. We're gonna go raid Ben Holmes. We will see you all next time.