Blitz.js is the Fullstack React Framework. It's heavily inspired Ru on Rails and is focused on making you as productive as possible. It's built on Next.js and adds all the missing pieces you need for building a fullstack app with a database. By far the biggest innovation of Blitz is the new "Zero-API" data layer that abstracts away the API so you don't have to mess with REST or GraphQL APIs!
Simon will introduce all the important parts & guide you through getting started with Blitz, so you'll know if you might want to use it or not.
Build Fullstack Apps in Record Time with Blitz.js
Workshop from:

React Advanced Conference 2021
Transcription
Yeah, this is me. My name is Simon Knott. I'm a developer tooling engineer, which means I work on tools that help you build better applications and build your applications better. Today I am the creator and developer of Quirrel. Quirrel is this wonderful tool, a job queuing solution for serverless deployments. I happen to really like it. I hope you like it too, and maybe we use it someday. I'm also a level two maintainer of Blitz.js, which is the reason I'm here today. Let's talk about Blitz.js, which is the reason you are here today. This is the website of Blitz. Blitz calls itself the full stack React framework. There's a couple of things in there. What does that mean? A React framework, Blitz is a framework that you can use to build applications using React. The full stack means that it's not only about the front end part of the thing, which you'd, for example, find in create React app or in Next.js, but it deals with the full stack from the front end layer to the back end layer, including the database. Blitz is really meant to build applications as opposed to websites or document heavy sites. We like to make this distinction from time to time where there's these kinds of websites that are more suited to static generators. They don't change a lot on things where you'd use Jekyll or Next.js or whatever. Then there's applications, obviously, that have a lot of interactive things, maybe even a database. Blitz is very much for that. Then on the website, it has this subtext here, which includes a lot of things that are helpful to know about Blitz. It says, Blitz is a better reason to do the framework that's inspired by Ruby on Rails. It's built on Next.js and features a zero API data layer abstraction that eliminates the need for REST or GraphQL. There's a lot in here to unpack. What does better reason to do the framework mean? That means that Blitz will give you most of the things you need to build an application by default. It brings everything with it. It's inspired by Ruby on Rails, not in the sense that we try to recreate Ruby on Rails, but we are very... Well, Blitz is very... It tries to have the same productiveness, the same productivity that Ruby on Rails enabled back in the days, but with a more modern feature set. It achieves that by being built on Next.js or it inherits a lot of the very nice features of Next.js. A couple of months ago, in the beginning, Blitz was really a compile to Next.js framework. We took your Blitz.js code and then we compiled that into the Next.js application. That's changed a bit. Blitz now has its own Next.js fork. But what still holds true is that Blitz really is an extension of Next.js, so to speak. And everything that works in Next.js will also work in Blitz. And then there's this last thing, which really is, I think, the main feature of Blitz, really, which is the zero API data layer abstraction. That's what we'll see in a bit. And that zero API data layer abstraction allows you to create applications and work with data from the database without having to think about building APIs, rather than the rest for GraphQL. And eliminating the need for GraphQL, obviously, is a shot against Redwood, which is similar to Blitz.js, but takes slightly different stance in using a lot of GraphQL or promoting the use of GraphQL. We have the zero API data layer, and I will show you what that is. All right. I think the easiest way for us to learn how to use Blitz is to actually build a Blitz project. And the rough plan for today is, so we have three hours together, right? And we'll roughly divide it into three. For the next 60 minutes, maybe 70, maybe 75, I will give you an introduction into Blitz.js. I will build, I will showcase how to build a prototype for a conference workshop signup page. And I will try to show, I will show you the most important features of Blitz that way. Then we'll take a short break, just so you can refresh, get some water, get some fresh air. And then we'll go into breakout sessions. And you will, in pairs of two or three or four people, we'll see, work on your own small Blitz applications. We'll do that for roughly an hour, 20 or so. And I will go around and answer any questions that might pop up, help you with any problems you have. So, the goal for that is for you to really get to grips with Blitz and just try to use it. And because a lot of the time, that is when you really learn what some tech feels like. And then afterwards, we'll come together again as a group and talk about what you've built, talk about experiences you had with Blitz, and answer further questions. Okay? Cool. So, let's start creating our application. I hope this is big enough for you to see. If not, let me know. So, let me just go to my temp directory. To use Blitz, you can install the Blitz CLI. You do that by running npm install, save global, to save it globally, and then Blitz. And I won't do that because I already did it, and it will take a while. But after you've done that, you have the Blitz CLI installed locally. And that says error, not in the Blitz project. But you will see the error on Blitz version, that I am currently on Blitz 0.41.1, which I think is the most current version. And now, to create a new Blitz project, you can run Blitz new, and then the name of your project. We'll call it React Advanced Workshops. And now, it will do some magic. It will ask you a couple of things. What package manager do you want to use? We will use npm. What form library do you want to use? You'll see why that matters in a bit. Let's just use the recommended one. And then it will generate a couple of files for us. And while it now installs npm dependencies, let's open this. No, let's not open that directory. Let's open the generated directory in VS Code. So, this is our newly created Blitz project. And it has a couple of files. Let's just quickly go through them, because I think you can see some things from here. Let's go from the bottom. We have a types.ts file and a tsconfig.json. But as you can see, Blitz by default comes with TypeScript preinstalled. Blitz really is TypeScript first. You can write everything with JavaScript if you want. But it will be a much better experience if you use TypeScript, because TypeScript just makes it a lot more fun to work on things. Then there's a readme with some Blitz specific things in there. We'll just ignore that. Some package.json things. And there's a jsconfig. We give you pre-configured jest testing out of the box. A Blitz configuration file. If you use Next.js, this might seem familiar. This is an extension of the next config file. There's the Blitz.env file, again, something for TypeScript. Babel, that is just used because we do have some Babel transformations. Prettier is presetup. ESLint is set up already. And then here's some environment files where you can play some environment variables, for example, for local development. And then what's interesting is these folders up top. Some test folder with some pre-generated utilities for you to test. Public folder, that is the same one that Next.js also have, just with static files. And then the mailers and integrations folders, we can ignore those. Those are just two conventions on how to build mailing things. And then the db folder and the app folder. And those are the really interesting ones. Let's look into them. The db folder contains a schema.prisma file. Prisma is a database client that is developed in Berlin, I think. Some of you said Berlin, if you want to go by the offices. And it's a really well done database client for JavaScript and TypeScript, and I think also Go and other languages nowadays. And we will get into how to use this database client later. But this is one of the best examples of how to use db folder. And I think that's one of the best things about Blitz, that it comes with Prisma by default. And then there's this app folder that contains a couple of things. And before we look into that, let's start our Blitz development server. We do that by running Blitz dev in the main directory, similar to how you would run Next.dev or something. This will start up our server. Open up Safari. We can go to localhost 3000. And we have this wonderful pre-generated page. This page lives in app slash pages slash index.tsx. If you've used Next before, you might recognize this. So this is file-based routing. If a file lives at index.tsx in the pages folder, that means it will be available at the index route. If it's called workshops, it will be available at slash workshops. And in that, you can see that it exports as default a React component. This is just some Blitz special type for React components, but this could just well be some React component. And if we want to change something about us, let's not put congrats here, but congratulations. Let's save on characters here. We see that this has auto reload or hot reloading or whatever that feature is called. So this is like the file that generates this page. And there's a couple of other files, the 4.4 page, which is shown when you go to not found page, obviously. And that app and document file, we don't need to talk about that. You probably won't need to look into that today. And then there's a couple other things here. There's this core thing, which contains some of the components you see as a lot of pre-generated ones. And then this user's directory, which we will look into later. Ravi wrote, it's fast refresh, I guess. Oh yeah, I think it's called fast refresh. Thank you. And this is like the general folder structure. What's important for now is that in the app folder, there's a pages folder. And in the pages folder, you can place new pages. Let's look into this database schema here. Blitz by default generates, comes with a schema for you that does some of the authentication things. So it has a user table, a session table and a token table. This just works with the pre-generated authentication logic that Blitz gives you. So you might've seen these buttons, sign up and log in. We'll just sign up. No, let's put in some email. React advanced at Simon.com.de password is test, test, test, create account. Nice. And now we have an account. And this logic is not something like that's built into Blitz, but we just generate it for you. So there's, he has like some password changing reset methods and you can just change it if you want. But this is useful for today. And we won't need to look into this, but what we want to do is we want to build an application that shows a list of upcoming workshops. For example, that's a React advanced conference and that allows people to sign up for these workshops. So we will add a new model called workshop. Give it an ID for now it's an integer. We'll mark that as an ID field and we'll auto increment that by default. We will give the workshop a name, which is a string and we'll maybe, oh, this is a GitHub copilot speaking. We will give it a date, which is a date time. You will give it a, let's give it a description, which is a string. And I think that's it for the moment. And then what we do is we open up a terminal and run Blitz Prisma generate. That's just something you need to run after you updated your schema. We'll talk about why you need to do that later. And just as a reminder here, Prisma comes pre-bundled with Blitz, but if you want, you can swap that out for some entirely different database client. You can use whatever you want, but we at Blitz, we're big fans of Prisma. So that's why we bundle it. All right. And we want to show a list of these workshops. You think the good idea is to just put in some dummy data first. To do that, we can use the Prisma Studio. If you run that, it will open up a browser tab and have this very nice, why doesn't it want that? Oh, I had that before. I think we need to run Blitz Prisma migrate, maybe. Why is that still a thing? DB push, is that a thing? Yes, that works better. Apparently in AT, I need to do some more digging into how Prisma actually works. We always forget what commands to run and what order. And this is like a very easy to use database UI. And we see in the user table, we have that user that we created, the React advanced assignment, knop.de user. Let's add a new workshop. Add record, give it a name. Blitz intro, the date is 2021, turn 28, add 16, no, add 15 UTC. The description is, let's learn everything there is to Blitz and save the record. And let's just add another one. I think there is one on editor experiences also today. Let's just have an hour later. This also seems interesting, just so we have two rows. Why didn't it put anything in? Okay. And now we want to display these values. And what we'll do for that is we'll create a new page in the pages folder. We'll call it workshops.ts.tsx, because we want to use JSX apparently, obviously. And we will define a function called workshops list. We'll export it as default. And in that workshops list, we'll return, let's call this, add a main thing, add a header, and then an unordered list, just put some dummy data in here. And then edit to experience. And now if we open up a localhost slash workshops, we will see a wonderful list of workshops. But obviously we want to get this, like we don't want to have this, we don't want to have this hard coded, but we want to get this from some database, from our database. And now think a second, what would you do to build this with, for example, Next.js or with anything else? Would most probably think about creating some kind of an API. Maybe not in this workshops case, this is the site that we're currently building could well be some statically generated document, but imagine we're building a more complicated application. So we'd most probably create some kind of an API. And there's a couple questions you need to answer when you do that. Do you want to have a REST API? Do you use GraphQL? Maybe is it RPC? If you use REST, how are your routes called? What HTTP methods did you use? How does authentication work? And all these kinds of things that are really hard questions to think about. But if you think about the grand scheme of things and like what that means for your application, it doesn't really matter a lot. For most applications, there will never be a customer that comes to you and says, Oh, I love that you built this using GraphQL. It makes my experience so much better. That just won't happen. And in the end, whether you use REST or GraphQL for the most cases is not a big difference, but still it's a big decision to make. And when Brandon, Brandon Bayer, who's like the original creator of Blitz, when he first built Blitz, he asked this question of how would this look like? How would data fetching look like if it was easy? What is the easiest developer API we can have for that? And well, to fetch data, what do you do? You just call code. That's what you do. You just call some function. That's what you can do with Blitz. So let's do that. We will go into this app folder and we will create a new magic folder called queries. And it's important that it has this name because this is a magic name. I'll talk about that more later. And the queries folder will create a file called get workshops. The name doesn't really matter. And in get workshops, we can define a nice function called get workshops. We'll export that as default. And in that function, we can just import a database from DB. This import from DB is just a shortcut to import db slash index dot TSX. So if I put a slash here, you see that it has all the files in here, but we'll just import from the DB. And then inside of get workshops, we can do mdb dot workshop dot find many. And we'll find, we'll just find all the workshops. We'll await the result and return it. The nice thing about Prisma is, and if you look at the result type of this, it will, does it say something nice? I think it says something nice. It will now return an array of workshops. That's the very nice thing about Prisma is that it gives you type safety really easily. And so now this is a function that gives you all the workshops. And now how do we call that from the front end? To be honest, it's really easy. You can just import get workshops from queries slash get workshops. And now you have a TypeScript function that you can just use. So what you could do is let's just do get workshops and like this returns a promise. And once we, once we have that, let's just console dot log the results, just to show you that this works. So I think our development server just crashed because we didn't restart it after updating the Prisma schema. That's something that we need to do because it needs to reload the Node.js files. And it can't do that for some reason because Prisma does some not super Node conventional things. But now that it's restarted, this should work. And if we look into the console, we can see that it contains the two arrays queried from the database. Okay. So this seems very magic. How does this work? We have this function in queries slash get workshops.ts that is just a very normal plain TypeScript function. Looks very normal. And we just import that in our React component, which can just call it. And what will happen is that this thing here only runs in the front end, while this thing here only runs in the back end. So how does this magic work? Like you can just import a back end function and use it, but with Blitz you can. So I told you that this query's name is magic. And the way this works is that Blitz will check your imports and whenever it finds an import from a folder named queries, it will replace this import and instead write just some pseudocode here, async function get workshops, return fetch slash API slash queries slash get workshops. And it will also automatically generate an API endpoint for this. So the way this works is that Blitz recognizes these imports and generates an API for you. It's an auto-generated RPC API because it's pretty much a remote procedure call. And that is what Blitz means when it says zero API data layer. This is not zero API because there is no API, but because you don't need to care about it. Similar to how serverless is not about not having servers, but about not caring for servers or not having to deal with them. All right. So we found that we have functions that we can just call. And the nice thing is because these are just normal TypeScript imports, our types work. That is the thing that I personally find to be the most beautiful thing about this zero API data layer design. Because in the code, this is just like normal function import. And that enables TypeScript to do all in one go. It enables TypeScript to do all its normal type checking work. Normally with a REST API, you need to do some serious work to get some notion of type safety. With that, not at all. Don't need to think about it. All right. So as we know with React, it can be a bit weird to work with promises inside of components. So we could do some standard use effect thing here and save that into some state. We won't do that, but instead we use the query hook from Blitz. This is built upon the React query, which you may have heard of. React query is a really great abstraction around querying APIs and Blitz just make use of it. That is a common theme you find at Blitz. If we find a really good existing library, we tend to integrate it well as opposed to rebuilding it ourselves. And now what we can do is we can write use query, get workshops. And I think we don't need to give it any parameters, but we put undefined in there. And then this will return an array of workshops. And as you can see, again, this has the perfect TypeScript type. Now if we want to show this, let's just map over the workshops. So we have a list item where we put in the workshop name. Now if I save this, you will see that this works and it should show us the right things. But once I reload this page, it will hopefully throw an error. Oh, it even catched the error. This threw an error. Okay. What is this? Let's read through that error because it's really interesting. It says a React component suspended while rendering, but no fallback UI was specified. Add a suspense fallback component high in the tree to provide a loading indicator or placeholder to display. What does this mean? Maybe some of you have heard of React suspense. That's a new upcoming feature. So there's been suspense for a while for loading components. But in an upcoming React version, there will be React suspense for data loading. And what is this suspense thing? It is one of the most beautiful things of React in my opinion. So if we look at this code, this code looks very synchronous. We call use query, give it get workshops, and it will immediately return an array of workshops. This seems like, or this code looks like this is there immediately. But in reality, calling the get workshops API, getting the data that takes a while, this is not a synchronous operation. You can see that by this function returning promise. So how does use query, use get workshops, and then get back a non-promise array of workshops? How does that work? That is where React suspense comes in. So the way this works is React will try to render this function. It will go through this hook, and this hook will kick off this query. It will call the API, the generated API, try to get some workshops. And then it will say, oh, shoot, I don't have data. I can't give anything back. I am not ready to render. And it will say, please stop React. I suspend. I am not ready yet. That is what use query says. This is implemented by use query, like throwing a special error, I think. But what you need to know is that use query can suspend rendering. It can say, please, React, do not render this component yet. I am not ready yet. But I will tell you when I'm ready. And until I'm ready, please suspend. Please show a fallback. And that is what React says. A React component suspended while rendering, but no fallback UI specified. So it is suspended because use query didn't have data. And then what React tries to do is it tries to find the nearest suspense boundary that is the suspense fallback component. And we can add that by not exporting the workshops display, but by wrapping it into a suspense boundary. So you can do that by importing suspense from React and then adding a suspense boundary that has like loading as the fallback. Why is this? Oh, we need to assign that to some value. Yeah, let's just ignore this. And now what you see is that this works and it will show loading really for a second here while it loads from the backend. Cool. So that is how you query data. I want to show you another very cool feature. So this is Prisma Studio. Is this still working? Yes, it is. I will now change something in here. I will change this to add it to experiences extravagance, just to have another word with me in there. And now when I press save on the left side, nothing happens, right? But when I go into this thing and now I will click into it, you will see that it immediately updates. And what does this do? So use query does not do like it doesn't have a live subscription to always get the newest value, but it tries to do some, some pseudo life value thing where it's, where it's has a couple of instances or a couple of moments where it tries to pull for new data. For example, when I reopened this tab, because we want to have the feeling of life data without actually having to deal with the web socket stuff. So whenever a user comes in and reopens the tab, we automatically update the values in here. And that is something that you don't need to deal with. Use query will do that on its own. Another thing you can do is you can pass this options and say, I want to re-fetch this every 200 milliseconds or something. And then what you see here in the background is that this will kick off new refresh and you call every 200 milliseconds, or you can say that I want to do some other things. It's not important right now. So this is how we do queries. Now let's build a details page for these workshops. So we will add a new page. What we want to have is we want to go to localize slash workshops, and then the idea of a workshop, and we want to see a details page for this workshop. And so what we do for this is we want to create like page at this route. So we add a directory here in pages called workshops, and then we add a new file and we call that ID.tsx. And we put the ID into square brackets. Those square brackets mean that this is a parameter. This is like a parameterized route. And here, anything could go in here. One, two, three, or four or bar or whatever. And in here again, we write a function called maybe workshop details that will return hello world from there. And we will export that as default. Now if we go to that site, we can see that we have hello world. Now what we want to do is we want to have access to what parameter this is. And for that, we can import a special hook. Let's call that, what that hook is called use param. And we give that the name of the parameter we want to refer to. And then we can also give it a very return type, which just makes sure that this has the right type. And let's just try if this works. Hello world ID. We go to some other ID. We see that we have a nice parameter here. And we maybe also want to show actual workshop details. So we'll need to build another query. So we need to fetch the workshop details, right? So that's what we are using another query for. We'll call that get workshop singular. And let's just copy this thing here. You could rename it. And now this gets one parameter called ID. And then instead of finding many, we find a unique workshop where the ID is the ID that's being put in. And we will now use that query from blitz again. So we'll import use query. We will also import the queries, the query, query slash get workshop. Hmm. I know we pause. I think I should turn off copilot. This is a bit disturbing. Copilot toggle off. Disabling global for now. And we will put in the ID parameter here. This will now say that the type doesn't work. Because type on the front is not assignable to type number. That happens because of some internal ways of how Next.js works. There may be instances where this component is rendered without having an ID available. And we'll just put some placeholder in there for now. And what we see is that this again throws the same error. It says we don't have a suspense boundary. And now what we'll do instead of wrapping every single component, that suspense boundary will just be lazy and go into this app file, which is the main wrapping component. And we'll add a central suspense boundary around here. That will now work for every single page. I don't think this is something you should do in an actual application code. Because you should have different suspense boundaries that show actually useful stuff. But for now, this is fine. And so we'll get back here a workshop. This could be a workshop or it could be null. So let's do if there's no workshop, let's return not found. And if there's a workshop, let's actually show some nice things. Let's add an H1 tag. Can we format this nicely? It has workshop.name. Let's add a paragraph that has a description. And let's add a date field. What's date time? Time. Workshop.date. To ISO string. So that will say not found for one, two, one, two, three. But if we go to number one, this works. And if we go to number two, this works too. And if we go to number three, that doesn't exist, it will say not found because it uses this here. It might happen a lot that you don't actually want to, that you have queries that could return nothing because these ideas could be nonsense. And because that is quite usual and you wouldn't really want to stay at the same page, but you'd expect people to redirect it to 404, we can do a nice thing here. Inside of a get workshop query, we can check if the workshop actually exists. And if there's no workshop, we will throw a new not found error. And if there's one, we'll return it. And this changes things because now the return type of get workshop is always a workshop. If there's no workshop, it will not return, but it will throw a not found error. And in here, we also know that this now will never be null. We can return this logic here. And if you reload this, it will automatically redirect you to the 404 page. And what I really like about this is that you can think in the happy path. This component isn't cluttered with a lot of conditionals about what happens if the workshop doesn't exist, all these things, but it only includes the path that you actually want to think about, the happy one. All right. And we have this works for number two. If we go to number three, we'll get a 404. Amazing. Whatever this was, that was interesting. Cool. So that is how we can build details page. And we'd likely also want to have links here, right? So let's go back to our workshops page and let's wrap that list item with an anchor and put an href to slash workshop, no workshops, plus workshop.id. And I think we also need to raise up the key for react here. And now we have links. Nice. What's interesting to see here, maybe you noticed that if I press on here, it will reload the full page. And if you've worked with Next.js before, you know that that is not necessary, at least with Next.js. And in Next.js, you use the link component, the same components available with Plits. So if you wrap this anchor in a link component and move up these properties again, then you will have instant writing here. We immediately show all the data. And what's nice about this is that not only it doesn't do a full HTML reload, but that it also will automatically prefetch any assets it needs as soon as this link is rendered. So if we go to the next page and then open up the network tab, let's go to slash workshops. And you see that, where is it? Oh, maybe it already had it in cache. But as soon as this link is rendered, it would immediately prefetch this subsequent page to just make navigation smoother, faster, and whatnot. Right. So this is linking. There is another feature to links that I'd like to show you later on. But just keep that in mind. We'll get back to that. And if I forget it, please remind me that I showed it to you. All right. So now we have a list of workshops. We can navigate into it. And maybe we also want people to be able to subscribe to these workshops. So we will go back into our schema here and we'll add a new relation. We'll see that there's a, we will add a participants array or participants relation, which is a list of users. For every workshop, there's a list of participants and for every participants and for every user, there's a list of workshops. So this is a many to many relationship. And we will stop our development server. We'll run blitz Prisma generate, no, blitz Prisma DB push to update the database. And we will restart our development server. And now what we can do is let's start by showing the participants. So I think we need to do here. Let's just check if this is true. We'll need to say include, and we want to include participants. Oh, it is automatically reloaded. Prisma, that's nice. I was just thinking about sometimes you need to restart the TypeScript development server if you make changes to your schema.prisma because TypeScript needs to re-index dead generated typings, but that wasn't the case now, which I am very fine with. And maybe we don't want to include everything about participants, but only will include selected fields, namely the, let's just keep it at that name. I think the name is totally fine for what we want to do. So what you can now see is that this returns a promise of an array of workshops and then an array of participant objects, which have name. I think there's no names in our database. So let's use emails instead. And in our workshops file, we can add a, oh, no, that's the workshops list. In our workshop details file, we'll add a header, participants. We'll add an unordered list in there. And for every, oh, wait, I added, I changed the wrong query. I didn't need to change this query, but I need to change the get workshop query. So for every participant, we show a list item that has the participants email. And also, it will just also use the participants email. Why doesn't this, oh, it's called key, not ID. This is now saying undefined is not an object. Oh, no, it's working. All right. All right. We have a list of participants. Question is, how do you actually subscribe to an event? And for that, let's add another button. That's not another button, but our first button. Let's give it the title subscribe. And when that button is clicked, we'll just for the moment alert. I want to subscribe. Subscribe. So this works. All right. We've talked about queries, right? Queries are for fetching data from the database. It's for reading. Now what we want to do is we want to write to the database. And for that, we can't use queries, because queries are for reading, but we want to use so-called mutations. And they work very similar to queries. So instead of using this magic folder called queries, we'll create another magic folder called mutations. Instead of mutations, we'll create a file called subscribe to workshop. Notice the workshop singular. And we will define a function called subscribe to workshop. We'll export it as default. It will get a workshop ID as its parameter. And then we will again, as we know it, import database from DB and call DB.workshop.update. We'll update workshop where the ID is the workshop ID we got. And we will write some data. We will update the participants and we'll connect the participant where the ID is. All right. What ID? We need to find a way to get the user ID of the requesting browser or the requesting user. If we look at the main page, I think there's a user ID written here. My user ID is one. But we need to get the user ID of the user that is calling in. And for that, this mutation is always passed a second parameter. We'll get a type from Blitz that shows what's in there. That parameter is called context. And on the context, there's the session object. And that session object contains information about the session of the client. For example, the user ID. And that user ID could be number or null. What we could now do is say if there's no user ID, we'll just throw a new error. Unauthorized get out. But as always, there is a neater way. What you can do is you can call context.session.authorize. And once you've done that, you see that the type of this, this returns, asserts this is authenticated session context. Once you've done that, you'll see that the type of user ID changes. The user ID after the call is a number. The user ID before that call is number or null. So, this authorize is pretty cool. That's a nifty feature in TypeScript. By calling authorize, you know that this will be number. Because if the user is not authorized, if they don't have a user ID, then this call here will immediately throw an error. And now what we can do is we can just put in the user ID here. And we'll await that. And that's everything we need to do to add some value to that workshop. So, now what we can do instead of calling window.alert, we can import from mutations our subscribe to workshop mutation. And we can, I think you need, nowadays you need to use the invoke helper function for this. You can say invoke the subscribe to workshop mutation with the parameter ID. And await that and then, oh, I guess that's it. Reload the page, we say subscribe. And nothing will change. But once we reload the page, we see that there now is a participant. So, why didn't anything change? Because the data that's shown here comes from this query. And Blitz has no way of knowing that calling this mutation changed something about the value of this query. So, what we need to do is we need to make sure that once this mutation was run, that we update this thing here. And so, we'll get the second parameter here, which we'll call workshop meta. And on that, we can say workshop meta.refetch, please. Please update this value. And now if we open up our studio, it's still running. Yes, it is running. Localized 5555. And we, can we, I think we need to reload studio because we changed something of the data schema. We can remove this participant, just check things again. And now if we open this press subscribe, we see that things update. All right. So, that is how you write to the database in Blitz. Similar to the use query helper, there's also a use mutation helper. You can use it if you want. It gives you some nice things. But it's not strictly necessary. All right. So, that's how we build a very, very basic workshop tracking application in 40 minutes, I guess. 45 minutes. I promised you I want to show you this one other feature about links. So, take a look at this line 14 here. Where we use the href, like a string based link to another page. What might happen is that you change the route of a page and now your links don't work anymore. And there's no way for you to or it's not super obvious that that broke something. And what we have in Blitz is we have the so called routes manifest. You can import from Blitz, Blitz the routes object. And on the routes object, there will be functions that help you for every single page you have. For example, for the workshop details page. So, what you can do is you can put into this href, instead of putting a string in here, you can say routes.workshop details. And now this will say to you, oh, you didn't put an ID into here. And you will pass it the workshop ID here. So, this is like a type safe way of building links to other pages. And now this still works the very same way. Yes. There is another thing that I'd like to show you about Blitz, which is the Blitz repl. I think you can let me check. I don't remember how to open it, to be honest. You open it using Blitz console. And what you can use this for, this is similar to the Ruby on Rails console. You can use this to test. Okay. Why doesn't this, why doesn't it like this? Property session does not exist. Okay. Let's just ignore it. You can use this to test your queries. You can, for example, do wait db.project.findmany. And then it will return, oh, not project, db.workshop. It will return all your projects, db.user, what we can do. We could do these things, or we could also call our queries. So, for example, await getworkshops. And then that will test our queries. And this can be really helpful to work in development. So, you can just use this to debug things. Another thing is the Blitz install command, which you can use to install some libraries or to set up some projects that we have recipes for. So, for example, if you run Blitz install Tailwind, that will take a while because it's cloning the recipe. And it takes a while, and it will set up some things so that we can use Tailwind. Tell you some things about what it will do is it will install all necessary dependencies and configure Tailwind. You could press enter. It will install dependencies. Then it will create some config files, write some style sheets. It will even update some of our existing files to add support for Tailwind. And then once we restart our development server, we will be able to style things using Tailwind. Let's reload this page. Cool. So, if you see this already looks a bit different because Tailwind reverts all existing styling, but we could know, for example, say that this, what file are we in? We're in the ID file. That this had a one, should have the class name, backgrounds, green. This is how Tailwind works. I always forget how to, what all these class names are called. Background color. So, BG minus green minus 500. And now we have a green background. Wonderful. So, you could use these recipes to really fast set up certain libraries or frameworks and tools that you might want to use. Cool. So, I think that is a rough overview of what Blitz can do. We spend the most time on the zero API data layer because that is really the main differentiating factor of Blitz. That's why Blitz is amazing and why I like it. All righty. It's 15 past my clock. So, ready to get back into the workshop. I don't know about you, it was good for me to have that short break. Before we get into record rooms, are there any questions you have right now about Blitz? Questions that I can answer? Is there any caching of the API database calls? I think there is some form of caching by default. Yes, Rackquery does some. But any complex caching would be done by yourself. I think what Rackquery, for example, does is it will store the result of the query inside the browser's local storage so that when the page is opened up again, then it will be able to use stale data to show something while it's loading. It's quite nice to give at least a bit of perceived performance. I think it's good to know. Any other questions? Simon, so you showed that when you changed the description in the database and added the word extravaganza and clicked on the browser window, it updated immediately. Is that a feature of Rackquery? Yes, that is a Rackquery feature. And does it also happen in production? Yes, you can turn it off if you want, but by default it will happen. I think that is one of the features that you need to turn on in Rackquery, but it's turned on by default in Blitz's Rackquery configuration. There's one form of caching that I forgot. So when you have multiple use query calls to the same query with the same parameters, those calls will also be cached. So that is kind of helpful. Imagine you have a tree of React components and you need to use the same data in two components, maybe two sibling components. Then normally what you'd need to do is you'd need to lift that state up or lift that data up in the parent component so that you don't need to fetch it twice. And because use query does that for you, you don't need to do that. You don't need to think about lifting state up only to avoid overfetching. Cool, thanks. Cool. I guess we go into breakout rooms then. We are six people, so I would create three breakout rooms. So you'll get assigned some pair that you work with. Oh, now we're seven. So maybe I should... that we have one pair of three or one triplet, not a pair. Do you have ideas of what you want to build? Are there any ideas in your head that you want to try to ship away as? Just a blog maybe. Blog. Some other ideas. Well, I might as well go for the demo you just did. Do the same thing, probably. Maybe that. Okay, cool. I think some other thing that might be interesting is building a ticketing service where people can open support tickets and then you have some form of a chat beneath that or something. At least that's a use case that I really love using Query for internal tools because... and not Query, Blitz. I should not mix up products because Blitz allows you to be really fast at prototyping things.