1. Introduction to Building Full Stack Applications
Hello, Amsterdam. It is really good to be here. Are you okay if we have some fun? We're going to have some fun. My name is David and I come to talk about building a full stack application. Sometimes things can go out of hand quickly when building a full stack application. There's a lot of dynamic complexity in assembling all the parts. The JS ecosystem has both beauty and tensions. Our goal is to ship features as soon as possible.
Hello, Amsterdam. People of Internet Land, all around the world. It is really good to be here. Are you okay if we have some fun? Would that be okay? All right. I mean, some of it, we're going to do a walkthrough. But some of it, well, it may get a little bit strange. But we're going to have some fun.
Again, my name is David and I come to talk about building a full stack application. If oh, yep, there we go. Oh, we went too far. My clicker. My clicker clicked. That's a question I want to ask you. So you want to build a full stack application. Yes? All right. Well, I've got some good news. But of course, you always start with the bad news. And the bad news is that sometimes things can go out of hand a little bit quickly, right? Is anyone, so question for you, anyone who's built a full stack application, who would say raise of hands that you've used probably 75% of those tools or tools like that in your application? Raise of hands? Right? How about 80, 90%? Raise of hands? Right. Okay. Maybe three out of four, but that's a part of the beauty of the ecosystem that we work in. And it's also one of the tensions of the ecosystem that we work in.
So what happened before this train literally went off the rails? You're shipping features, right? You just want to build features as quickly as possible, design, dev, deploy, on repeat. And sometimes it doesn't always go the way that you want it to go. There's this way that we assemble all the parts that adds a lot of dynamic complexity, right? And complexity means often going off the rails. The beauty of the JS ecosystem is its diversity, the modularity, and the rate of innovation. The bane of the JS ecosystem is its diversity, modularity, and rate of innovation. Right? It's powerful, but sometimes it goes wrong for the same reasons that it goes right. All right. So, again, our goal, we want to ship features, we want to do it frequently. We want to ship features as soon as possible.
2. Architecture and Process Challenges
And as our application scales, we want to ship features continually. We need an architecture and process that works efficiently across the stack. Config, integration, and dependencies add overhead. Separation of concerns is important, but it requires integration and config when bringing them back together.
And as our application, as our team scales, our user base scales, we want to ship features continually, frequently. Design, dev, and deploy. And it's not just the technology.
I'm a framework developer, so all I do is config, but I don't know if I love config. Integration, config, dependencies, all of that adds overhead over time, because also, you've got to move fast and keep up with those dependencies. As break and changes happen, you have to keep up with config. Conventions. Separation of concerns. Now this is actually interesting. In full stack, what you're taught to do is you're taught to separate concerns, which you have to. That could be at the specific endpoint level. Maybe you're creating an integration with a third-party application. Your entire API, right? We want to separate concerns. We want to separate from the front end, right? Working with React, working with state, working with data. If we want to just do design and lay up, we want to separate concerns. What happens when you need to bring those separate concerns back together? What are you doing? Oh, come on. That was a... So what was overhead? You're doing integration and config, right? So even when you separate concerns, then you have to come back and do integration and config on them, right? Okay, next one. Clicker.
3. Design Driven Full Stack Workflow
Clicker let me down today. Security. Is your API secure? Design driven full stack. A workflow for people that like to ship features. I'll show you how to have a full stack workflow from start to scale. Starting with a standalone project, we'll create components, design isolation, and work with data. We'll build API and UI for CRUD and make it secure. All in this presentation.
Clicker let me down today. Security. Is your API secure? Really? Because you did it? You wrote the CI test too, right? All right. Bad news. Let's go to the good news.
Design driven full stack. Yes, we kind of made that up. But it's a workflow for people that like to ship features. And I'm going to show you how you can have a full stack workflow end to end that includes that architecture process I talked about that lets you ship efficiently from the start of your project all the way through to scale.
So here's what that means. I want to start with a standalone project. And we're just going to have a file that's going to be HTML and CSS. And we are going to do a walkthrough. It takes me about 25 minutes and I knew that wouldn't fly. So we're going to go through some recorded and do a little bit at the end. I want to show you this. We're going to start HTML, CSS, JSX file. We're going to create a component. We're going to design an isolation and do layout. And then this coupling place. We need to work with data because data is what we need to couple. But the lack of being able to mock data specifically for fetch and state is often where coupling breaks down. Right? So we're going to work through that stage of doing mock data. And then we're going to build out all of our API for CRUD. We're going to build out all of our UI for CRUD. And then we're going to make it secure. And I'm going to do all that. How much time do I have, Ellie? Nevermind. Don't answer that. But we're going to do that in this presentation.
4. Design Driven Full Stack Solutions
And that is what we call design driven Full Stack. So how does design driven Full Stack solve those four challenges I showed you? Overhead. We're going to handle the overhead. I want to show you and introduce an innovation at Redwood called Redwood Cells. And lastly, I'll show you what it looks like to be secure by default, your frontend backend. And again, this is with Redwood. That's where I work. I'm a co-founder at Redwood. It's an open source project, so, you know, work, but there's other ways to do this across other systems.
And that is what we call design driven Full Stack. Sound good? See that now? That's good! I may be a little sleepy. So you all are helping me out. Keep it going.
So how does design driven Full Stack solve those four challenges I showed you? Overhead. We're going to have endian configuration, zero configuration, out of the box. We're going to have code generators and setup generators, so that you can get going without having to think about, wait, what add-on do I need for Storybook again? Wait, how do I actually mock data in Storybook? But then, what do I tell my back-end developers about that? So we're going to handle the overhead.
Conventions? True. I want to show you and introduce an innovation at Redwood called Redwood Cells. And Redwood Cells are a way to handle data, state and fetch. And I want to show you separation of concerns, and how we use data as a coupling, then, once we have the data structure defined, to then move into Crud and then integrate the frontend and the backend. And lastly, I'll show you what it looks like to be secure by default, your frontend backend. And again, this is with Redwood. That's where I work. I'm a co-founder at Redwood. It's an open source project, so, you know, work, but there's other ways to do this across other systems. And I just want to show you, for your stacks, some ideas and concepts that you can bring to it. So bear with me for a second.
5. Building a Dynamic App with Redwood.js
We're going to build a dynamic app and symbol the Avengers. We'll start with an HTML and CSS JSX file using Tailwind. Then, we'll create a dynamic web page with CRUD UI and an API. Redwood.js is a full stack, fully integrated, fully featured application that includes GraphQL and React.
Right? With a team. Okay, so here's where we're going. We need the Avengers. And what we're going to do is build a dynamic app and we're going to symbol the Avengers. See? It's fun, right? We're going to have a great time here.
Alright, because what I really want to show you is a bunch of code. But what we'll do is start with the JSX file there, and all it is is HTML and CSS. By the way we're going to use Tailwind and we're going to build a dynamic page to assemble the Avengers and all the team members there. Sound good? Alright. Let's see how this goes.
Okay, start with a quick little video. But this is the file we'll start with. It has some data in it that we're going to use and I just want to show you it's HTML and CSS. And then where we want to go to, this will be the dynamic web page that we're going to end up with, okay? Here's the CRUD UI. It's got an API with it. If you noticed, Vision was missing. That guy. By the way, WandaVision was great. Anyways, so you can add Vision in and now we've got a full CRUD application, right? That's what we're going to do.
Okay. Redwood.js. Real quick on Redwood. Started by a guy named Tom Preston Warner. He was a co-founder at GitHub. Built one of the largest, helped build one of the largest Rails applications in existence. Also built on Jekyll. And four years ago, conversations around what Redwood could be. Now, Redwood.js is a full stack, fully integrated, fully featured application that we say is going from side project to start-up as quick as possible. And includes a few of your favorite tools, right? So, you'll recognize GraphQL. React.
6. Creating a Redwood App with Storybook and Yarn
We're at the React conference. Prisma. Storybook and we're going to use Tailwind. Infinity Stone 1. Overhead. How quickly can we go from install to coding features? We're going to create a new Redwood app. Spins up in, I know. Yarn, packages, installing. We just started Storybook. Full stack, code base, Storybook fires up, all the add-ons are already ready for you. Quick note on the Redwood project code base. You've got Yarn workspaces. Two main projects, your API and your web. A proper API, like Rails services for your business logic.
We're at the React conference. Prisma. Storybook and we're going to use Tailwind. Okay. Those are our tools.
Infinity Stone 1. See? It's more fun already, right? Infinity Stone 1. Overhead. How quickly can we go from install to coding features? And all I wanted to show you is that I wasn't cheating, all right? Well, I could have been cheating because I could have edited the videos, but you don't need to know that.
All right. So, we're going to create a new Redwood app. Spins up in, I know. Yarn, packages, installing. Okay. So, I sped all that up. We're going to CD into the directory. Oh, sorry. And then immediately we did not start our local dev server. This is really important. We just started Storybook. All right? Full stack, code base, started Storybook, Storybook fires up, all the add-ons are already ready for you. There are no stories yet because we haven't even started coding. Accessibility is activated. Storybook is ready to go. So, that was our first step, install, run the Storybook process.
Quick note on the Redwood project code base. You've got Yarn workspaces. So, there's two main projects in there, your API and your web. We call it a proper API. If you're familiar with Rails, it's going to feel and look a lot like Rails services for your business logic.
7. Creating a Page with Storybook and Tailwind
We also have functions for server lists or using Fastify for deploy. The web is a React application. The first thing we need to do is get our page ready. We use a generator to create the page. You'll notice Storybook, which allows you to design inside of it using code. It comes with three generated files: main JSX file, story file, and a test. As you make changes to your CSS, you'll immediately see those changes inside of Storybook. Storybook is a wonderful tool for UI development.
We also have functions for server lists or using Fastify for deploy, GraphQL for the SDLs. And you'll notice Jest comes pre-config. And the web is a React application.
We'll get a bit more into that. But just, that's the code base. I don't have any more time to go into it now than we'll be looking at.
The first thing we need to do is get our page ready. And again, we're capturing stone number one here. I want to show you what we do with boilerplate.
So, this is a generator to create the page. You aren't generate page team, right? You got to start with the team page. That's done. You'll notice Storybook, the process is running and immediately Storybook, now you're seeing that page. We just have some boilerplate data that shows up and wouldn't it be nice if in one command you could just set up Tailwind? And it worked with Storybook, right? And so immediately you're designing inside of Storybook using code that you're going to use. Notice there's three files that got generated. This is going really quickly, I know. And those three files were your main JSX file, your story file, and a test. So it comes with a just test out of the box. And all I'm doing here is copying and pasting.
We want to work inside of our new page component. So I'm copying and pasting that starter code that I had. And now you have your page mocked up, not mocked up. You have your page created inside of Storybook. And as you're making changes to your CSS, in this case, I just want to replay that right there. As you're making changes to your CSS, you'll immediately see those changes inside of Storybook.
Has anybody used Storybook before? Yeah. Do you love it? Come on. We love it. Storybook is wonderful. And if you haven't used it, it's probably because of config and integration.
8. Introducing Redwood Cells and Handling State
I recommend checking out Storybook. Next, let's simplify the hard things and introduce Redwood cells. These cells handle React State, GraphQL, client-server, fetch requests, and endpoints. In the component architecture, we have a page, a team member component for layout and styling, and a Redwood cell that handles data and passes props to the team member component. Let's generate the Redwood cell for team members, which includes handling state: empty, failure, loading, and success.
I really recommend you check it out. Storybook is an amazing tool. All right. Next up, conventions. So how can we make the hard things simple and get Infinity Stone Number 2? I want to introduce you to Redwood cells. And Redwood cells are going to handle all the hard things about React State. GraphQL, client server, because we like to talk about GraphQL, but I don't know if we like GraphQL. And also fetch request and endpoints.
This is the component architecture I'm about to build out. I started with a page. So I already have the page. Next up, I'll have a team member component. And I'm going to use that predominantly for just the layout and the styling of a team member. Page component, you could basically think about it as a wrapper. Sorry, layout. In the way I'm doing it here. But there's a middle component now. And that's the Redwood cell. And what I will use that, the work that that cell is going to do... That's a good metaphor, right? Would it be an analogy or a metaphor? I screw that up sometimes. Anyway. The work the cell is going to do is that it's going to handle the data and pass the props down to the team member component. And that's it. I'll show you that code here in a second. But first off, let's get going. And of course there's a generator for that. I want to show you generating the Redwood cell for the team members. And immediately, you see these one story, but there's four substories there. And those are handling state. Empty, failure, loading and success.
9. The Structure and Benefits of Redwood Cells
Isn't that interesting? The team members cell in Redwood is effectively a higher order component that handles queries and states. It helps simplify complex tasks by leveraging conventions.
Isn't that interesting? Where did we get that from? And if you look at the team members cell, we have several things going inside of this one component. We have a query. We have all of our states. Loading, empty, failure, and success. We also have two other files that were generated there, the stories and the tests. But does anyone, what does this remind? This is a Redwood cell. The structure of a Redwood cell. Does this remind anyone of anything in React speak? It's effectively a higher order component. Not technically. I said effectively, because we've been corrected. But you can think of it as a higher order component. So it has the query for the GraphQL. We are using Apollo client. You have four states all inside of one component. And, again, this infinity stone, see, the analogy's really working. This infinity stone is helping us with conventions. And it's letting us leverage conventions so that we can make some things that are really hard and complex a lot more simple.
10. Cascading Code and Passing Props
I'll show you how the code cascades down the hierarchy from the page component to the team member cell. GraphQL queries are used to pass props to the cell. The mock is created for storybook.
I talked a little bit about the cell. But, again, you'll notice at the top there's a query, GraphQL query. We'll do some more work on that. And then we have, effectively, four components, the loading, empty, failure, and success. All right.
Next video I want to show you, there's a lot of code moving around. But remember that diagram I showed you, the architecture, the hierarchy that I want to create? Page, cell, and then a team member inside of it. I'll talk you through it. But all I'm doing is moving the code down the hierarchy. So I'm kind of cascading code. So that each component can do individual work. So here we go. First we're gonna start, the page component is where the code is. And I start with importing the team member cell that I've created. So that I'll go down. I don't need this data here anymore. Right? That was never meant to be other than just to start the process. And here, for my unordered list instead, that's where the team member cell is gonna do the work. And now, I'm over in the team member cell. In that unordered list that I just copied, I'm now pasting that in. Right? Just working down the hierarchy. And I'm also... like... I know GraphQL queries. Scary. But just props. Think of props. What props do I need? And I'm putting them in my query right there. Next up is a mock. And this is a mock for storybook.
11. Mock Files and Creating the Team Member Component
We're using mockServiceWorkers with Storybook to show the success state with mock data. You can instantly see your components with mock data across different states. There's a cell story file and a cell mock file. The story file gets its data from somewhere. We need one more component, the team member. The cell will handle the data, while the new component will focus on the layout using HTML and CSS.
Right? So if you've never seen a mock file before... by the way, we're using mockServiceWorkers for this. It goes with Storybook. Right? So, it's what Storybook is using to now show the success state with mock data. Isn't that cool? And what you can't see... I just couldn't do it side by side at the time coding. But you're working Storybook and instantaneously you're seeing your components with mock state across each of the states... sorry, mock data across each of the states immediately. So, that's pretty fun.
One thing I want to show you really quickly, there's a cell story file and a cell mock file. There's no magic there. It's just an import. All right? Kind of makes sense? The story file is getting its data from somewhere. All right.
Next up, we have... there was one more component we needed. Do you remember? Team member. You do remember. You were actually paying attention. That's good. Team member. And... I want the cell to do the work of the data. Because that's query and state for me. So, that's fetch and state. I want to move everything else into a new component that I'm calling my team member. And that's where I can think about my layout for the team members. So, that'll be HTML and CSS. Again, just moving code around. It's strange to not be typing this.
12. Importing Team Member Component and Using Mocks
But it's so much more efficient. We're now importing the team member component into the team members cell. Lastly, that mock that I created for my cell, I can use that same mock file for the team member component because I want to see that in storybook. I have all of my components displaying stories inside a storybook. It's a beautiful thing and it's really fun and magical to use.
But it's so much more efficient. And you don't get to discover what a terrible typist I am. All right, here we are. We're now importing the team member component into the team members cell, the plural to help us remember that it's iterating. Cleaning up our props. And here inside the team member, this is the component. And now this is where all of the HTML that's going to be for layout is going to be. And I need to make sure I pass in member. Actually, I'm glad I'm not doing this live right now. That wouldn't have gone well. You would have not been impressed. All right. Lastly, that mock that I created for my cell, I can use that same mock file for the team member component because I want to see that in storybook, right? In a cell, we'll be passing the props down to a team member, so in this case, I can handle that at the mock level. And then immediately, and again, it's more impressive you can see it full screen. I have all of my components displaying stories inside a storybook, right? The data is being passed up at the page level. I can see the mocks for the cell and the member. It's all getting, it's just all there. It's a beautiful thing and it's really fun and magical to use.
13. Coupling Front and Back End with Mock Data
Okay. So we have two stones so far. We're going to couple the front and back end development using mock data and state. We'll create a model in our data schema using the same structure as the team member cell query. With Redwood dev server, we can quickly move from local dev to working across the stack. Alignment between front end and back end is crucial, and we achieve it with cells and mock data.
Okay. So we have two stones So far. Following a bit. Is that cool? Has anybody seen something like that before with storybook? Yeah, I didn't think we wanted this to be. This was a vision we had for use of storybook. So I know it's a vision of storybook. We love working with the storybook team that they had as well.
So all right, two stones down, we have one stone to go here. Infinity stone three, separation of concurrence, actually, we have two. How can we couple the front and back end development using mock data in state? And how we're going to do that is we're going to take that query from the team member cell. We have our data structure already. And now in our data model or in our data schema, we're going to create a model using that same structure. We're using prisma. Don't worry much about the attributes. Right. But you've got the same data model, ID, name, image, URL. And let me show you how quickly we can move once we start up the Redwood dev server.
So now we're in local dev. Right. We're not working the storybook anymore because we're going to work across. And this is that coupling piece. Right. So typically your front end or you would work up everything you need for the front end UI. And then now you've got to figure out what that's going to look like in the backend or if you're a backend developer, you're going to start in the backend and you're going to think through what tables and models you might need. And then you can try to work forward in the front end. And it's just like when you're building a tunnel under a mountain. Like if you miss, you miss by a mile. Right. So trying to bring those two things together, you really want to have alignment. And that's what we're using cells and mock data and mock state to do.
14. Optimizing Workflow for Performant Teams
What happened there is with the Prisma Migrate command, we now have a local SQLite database with the same schema and table that's needed for the front end code. I want to talk about Thanos and the importance of performant teams building performant products. We need to optimize the process for the people to affect the whole.
What happened there is with the Prisma Migrate command, we now have a local SQLite database with the same schema and table that's needed for the front end code.
This will be quick. This is what I really wanted to talk to you about. I want to talk about Thanos. There's a lot of wonderful focus on tool performance. And rightfully so. Web performance, web performance. That's important. We need to do that. But Thanos, I'm talking about Thanos to a group of adults. Thanos, how do you beat Thanos, again? With a team. The Avengers. So, if you want to beat Thanos, it's gonna do it. You need a team. I love this. This was so great. It's a little bit ridiculous. Performant teams build performant products. There's a massive body of research talking about this, right? We can focus on technology, and we need to, but we also need to affect the whole, and we need to optimize the process for the people in order to affect the whole, alright? So, let me show you what that looks like in this scenario when you optimize a workflow for the people that we'll be working on the application, what that might look like.
15. Creating a Full Stack Application with CRUD Demo
In one command, we run scaffold for the UI and API, creating a full stack application. The generated files include SDL and service files for logic, as well as different cells for data handling. We showcase the admin UI with a quick CRUD demo, starting with an empty team page and adding a team member. We seamlessly transition from front end to back end. Lastly, we touch on the importance of security.
So, in one command, we're gonna run scaffold for the UI, and this will create everything we need in the API, all of our endpoints based on that data model that we have, and this will create everything we need in the UI so that we can run CRUD. And at this point, we now have a full stack application.
There was a lot of files that were generated, but again, it's the SDL files, it's the service files where our logic takes place, and the front end, it's the different cells, they're doing the data work for us so that we can do CRUD. And really quickly, this is the actual admin UI, quick CRUD demo. You'll see that there's a...there's nothing on the team page. We don't have any team members created yet. We're now inside of a dynamic application. Falcon, apparently, is who I chose to start with. Right? And boom, we have Falcon. If we go back, team members edit, we can edit that. That's pretty cool, right? All right. We just went from front end to back end in one flow. Oh, by the way, empty state, right? It's there for you. And I kind of...yeah, a little bit but if I made it too simple, then your mind might not be blown but it should be blown just a little bit. Just a little bit.
There's one last infinity stone and I'm going to make this really quick. What was the last infinity stone that we needed? I skipped that real fast, right? Security. So how can we do end security which I promise that would be our bonus that we get to. Oh, man. Spoiler. And I'll show this really quickly. But first, did we do it? Main display. It won't work if my displays don't work. Am I up? No. It's my thing, it's not going to go. I know. You know, it's a bummer when the grand finale doesn't go the way you want it to. But that's quite all right. I'm going to skip that part and go back to my slides.
16. Redwood Authentication and Workflow
Redwood has authentication by default and in one command. SDL files have directives that make them secure by default. You can easily change an SDL file from authenticated to skip off to create a public API. Adding private to a route in the routes file hides it and redirects to the homepage. These features were designed to create a user-friendly workflow.
I'm going to skip that part and go back to my slides. Redwood has authentication by default and in one command, and I'll just talk you through this really quickly. When you set up Redwood authentication, those SDL files that I showed you, it has directives on them and they're all secure by default. So that public page that you saw, for the team members, all of that would no longer be accessible, although the page itself would be, but your endpoint would be secure by default and then with one change of a directive on an SDL file, you would change it from authenticated to skip off. You would have a public API. And then inside of our routes file, if you add private to any route, that route now becomes hidden. You can redirect them to the homepage. And again, why did we do those kind of things with Redwood? We did those kind of things because we wanted to build a workflow for people.
17. Final Remarks and Redwood Community
If you are interested in Redwood, the best way to learn Redwood is to do the Redwood tutorial and join our community of over 400 contributors. Follow me on Twitter for more information. Thank you for your generosity.
All right, I have one final slide. Can we get it back up by a chance? I wanted this was, I was excited about this one too. Is it going to come? No. All right. I had Tony Stark at the end. You've got to end a presentation with Tony Stark if you're going to talk about Thanos, right? All right, the slides aren't coming. Okay. No, we get it. I mean, come on. Right? All right. Thank you. You were very gracious. That is not the way I wanted to end.
If you are interested in Redwood, the best way to learn Redwood is to do the Redwood tutorial, and the best way to be successful with the tutorial is to join our community. We have a wonderful community of contributors, over 400, and we love our contributors. We have a helpful dynamic on our forums and our Discord, and lastly, I promise I'll get this demo up online and you can take a look at it there. Follow me on Twitter if you want to find out more. Thank you. You're very generous. Thank you, David. Reminder, David, nobody moves. We'll be at the speaker's booth outside where maybe he'll do the demo for you. That's your time, not my time. All right, David, what about server side rendering with Redwood and TypeScript? Yeah. What about server side rendering and TypeScript? I meant submission, and I omitted. Reminder to leave quietly out of respect so we can all hear. Thank you. No, that's great. Yeah, fully typed. I meant to mention that when we started the project, but you can pass TypeScript.
Is that a promise or a threat? It'll be great. All right. Can Redwood generate TypeScript contracts based on the mocked API? Probably, Orda. It's not a problem you want, because you would need to support some forms of RLs. Yeah, so see, that's Orda, TypeScript guy. So what Orda said, and this is helpful, that's probably the right question. But in the context of Redwood, that's not something you would need. Cool.
Does it support other frameworks besides React? That's a fantastic question. Redwood, because of that workspaces that I mentioned, you can run Redwood as a standalone API, and why would we use GraphQL if we didn't intend it to be multi-client? Because today, what products are not multi-client? I mean, there's a lot of great sites that stay there, but we intend it to be multi-client from the start. So yes, there's a lot of great startups right now, using Redwood's API with Next, running multi-client across Electron, CLI, using different websites that they're deploying. So yes, is the answer. And is it free, or how is it monetized? Yeah, Redwood's an open-source project. It's sponsored, but it's open-source. If I didn't hit that grade at the beginning, that's my bad. Open-source and will-be open-source. Before I let you all go, you had something you wanted to share, I believe. Yeah, I did. Thank you, Ellie, for the time. So the last few years have been a little crazy for everybody, and what I wanted to leave you with is how important community and team is in my life. There's a really good friend here that I've never met before, and... Sorry, it's been a long week. Toby, I just wanted to say hi to you, because none of this was done by me alone. It was done with a wonderful community of people, and it's a shame that I'm the only person up here telling you about it. So I wanted to say Toby, come up here. I just wanted to meet you for the first time. Hey, man. That's a great hug. Thanks, buddy. I flubbed it at the end. Nice to meet you. It's really good to meet you. That was it.