Video details

Fully-typed fullstack development using Blitz.js | Simon Knott

TypeScript
11.11.2020
English

Simon Knott

TypeScript is awesome, but only works in the boundaries of its codebase. In web applications though, type-soundness is often lost in transit between frontend and backend. Blitz.js, a Next.js-based Framework for full stack web applications, overcomes this boundary with its “Zero-API” Data Layer: Frontend code *looks* like it’s directly calling backend code, enabling you to fully leverage TypeScript. In the talk, we’ll take a look at how this works, where we can find similar trends (psst: Prisma! :D) and why that’s interesting.
Simon Knott is a maintainer at Blitz.js, implemented SuperJSON and currently builds Quirrel, a task queueing solution for serverless deployments. He also created EntE, a digital absence accounting solutions for German schools.

Transcript

- Okay. We are now coming up with the third talk of today's meetup and we have a very, very special guest who's joining us from Potsdam in Berlin. And so we have Simon Knott joining us and I'll bring him onto the stage. - Hello. - Hello, welcome. Lemme just get the labels working. Okay. So Simon, welcome. - It's nice to have you here. - Thanks for having me. Yeah. - So I'll give a short introduction and then hopefully we can get started with your talk. So, I'll also bring your slides onto the screen. So, Simon Knott is a maintainer at Blitz.js. He implemented SuperJSON and currently builds Quirrel, a task queuing solution for serverless deployments. He also created EntE, a digital absence accounting solution for German schools. And when he's not working on development tooling he loves riding his bike and DJing. And sometimes, he's also found studying at Hasso-Plattner-Institute in Potsdam. - That's it. - So, Simon your talk for today is Fully-typed Fullstack Development using Blitz.js. And without any further delay, I'm gonna give you the stage. - Alright? Thanks Daniel. Yeah. As Daniel said, in my talk we're gonna have a look at Fully-type Fullstack Development. And it's gonna be two parts. So, at first we're gonna take a look at Blitz.js and especially one aspect of it. Then we're gonna take a step back and have a look at whether we can spot a trend in the web development ecosystem from what we've seen in Blitz.js. So, I've got a slide that tells you who I am but Daniel already did that. So, we gonna skip it. Let's just get right into what's Blitz.js. I guess most of you haven't heard of it. But Blitz.js describes itself as the Fullstack React Framework. So, it's a framework used to create React web applications as opposed to say web pages. And it's built on Next.js, which is that awesome React Framework by the team at Vercel and takes heavy inspiration by the mother of productivity, by Ruby on Rails. The main like value proposition of Blitz is that it makes you hella productive. Like it really supercharges your productivity. And I can personally vouch for that. And one of the ways it does that is by the so-called zero API Data Layer. And that's what we're gonna take a look at today. So, first thing we're gonna need to install Blitz. Similar, as like easy just run NPM, installed Global Blitz and then you got the Blitz command line interface and then you can use Blitz new and then the name of a project to scaffold a new baby Blitz project. In this case, we're gonna build the TS meetup page. So this is a page for this meetup here, where the upcoming meetups can be seen and people can like say "well, I'll participate there." And just like a simple demo app. So, run Blitz new TS-meetup-page to generate that and then change into the directory and ramp it start to start your development environments. And now, again what we wanna build is that meetup page and the first thing we're gonna need is a page or a route called /meetups where you can see the upcoming ones. And to implement that, we gonna create a new file which is at pages/meetups.tsx. That is just like a Next.js convention. Whatever file is placed in the pages folder will become a U Route in your application. So that file will be accessible at /meetups. And it's a very simply React component. You don't need to understand React for this talk at all. Just know that this function it like, it fetches the upcoming meetups from say your database and then it renders them in some list. But the question is, how does it fetch the upcoming meetups? How does it do that? And back in the olden days, when we had like Server-side rendering maybe with PHP or like anything that's older than a couple of years, that was easy. Everything's rendered on the server. So, just call your database. You got access there anyways. But now, recently Client-side rendering has become quite a rage and you'd also want to get the new meetups or get your upcoming meetups from the Client-side. And so now, like it's kinda obvious that you don't want to actually access the full database from the Client-side. So, how do you get the data? And you may like... The obvious solution to that is, well, just create some API that the Front End can call but just create some API like there are so many choices. There, you can create a REST API, you can use JSON but you can also have like an GraphQL and points. You can use good old RPC but like if you use GraphQL, do you use Apollo or Relay? Maybe, you don't have an API at all but put your data into like some user accessible database like Faunadb or Firestore. And even when you have that, like you just use the rest. How do you fetch the data on the Front End? Do you just use like window.fetch? Do you use some accompanying tools like React Query or something? Like there is a lot of different choices available. And to be honest, it doesn't really matter what you decide to use. Whether you use REST or GraphQL or something like this. Well, it matters from a development kind of standpoint because it impacts the way you work. On a big end it impacts the development speed you can work at. But it won't... there's no user coming to you and saying, "I love that you use GraphQL at Facebook "because it makes my pages super fast." Like your users won't worry about or won't care, about the underlying technologies. And to be honest, this is just a lot of different choices. And a lot of headache caused by what kind of transport you use. And this is also not easy. You still have to do a lot of thinking about what would be the right solution. And even when you decided for something, let's say you you're gonna build a REST API, You always have to decide what's the right name for the routes. Do I use get, put, post, patch, options, hats, make call or what do you use as HTTP headers? How do my HTTP headers work? And so, how would this look like if it were easy? That's the question that Brandon Bayer who is the original author or the original creator of Blitz.js, that's the question he asked himself. Like if this were as easy as possible, how would this look like? And to be honest, if this were as easy as possible, you just import your Back End function from the Front End and call it like any other function. You wouldn't want to worry about transferring the data over the network. And that's actually what's possible with Blitz.js. That's the so-called zero API data layer. So, let's get back to our page. And as you can see in the second line we import a function called getMeetups from queries/getMeetups. And that is actually a function that when run or when called will execute on the Back End. It doesn't matter if you call it from the Back End or from the Front End it will execute on the Back End. So, it has full access to the database and like to the full Node.js APIs. And you import that and then in the first row on the first line of the component, you can just call getMeetups. And that's how it works. At least conceptually, like in reality you still have to import like some helpers from Blitz like use Query and then you gonna have to wrap in use Query. But that's just to make TypeScript types work, and all the rest it's like it works. So yeah, you get the concept. And to show you like this is not some real trickery with the Queries getMeetups thing, If we have a look at that, it get meetups file, this is queries/getMeetups, this is just normal TypeScript code, right? There's this function called Get Meetups that like uses the database clients to find like the upcoming meetups and then it just returns it and then that function is exported as default. And that's all. That's the function that's important here. And it works. And actually that import db from db that you see there. db is actually just an alias on Blitz, it's the default alias on Blitz.js for a Prisma client. So we're gonna take a look at Prisma, who you may know because they are the organizers of this meetup. We're gonna take a look at that later. Alright and because this is just normal TypeScript code, there is full type TypeScript support. Here's a screenshot from VS Code where can see how this fully supports, like full editor integration and a full auto complete because the code is just like, you just import the function and you call it. And actually those two auto suggestions you can see there. These come from the database clients. So they're fully passed through. And that is something that doesn't happen with most or with traditional Back Ends of traditional API styles of things. And that's because like normally the type soundness of even of types of projects it's lost in transit when transiting over the network. So, when I first saw this, I was like, wow! This is amazing. It's just so easy and you don't have to worry about all those things. And now if you're like her, if you're like me and you're like, wow! I need to check this out. Like really, go check it out. Blitz started at the beginning of this year and it's been around 145 contributors since then. We started at the beginning of the year and we're about to hit beta in the coming days and plan on moving to version of 1.0 by the end of the year. There's already some production Blitz apps out in the world, for example, the current landing page is was built in Blitz and I can vouch for it and it makes you very productive. Okay, so lets get back to that magic trickery with importing the Back End. And I can see that in the chats, you're already debating about how this works technically. And I think let's see, Yup! It seems like you're kinda correct. So let me tell you how that works. You can see here that there is this import from query/get Meetups. And whenever you import a file from a folder that is called Queries. So Queries is a magical name in this context. Whenever you import a file from folder called Queries or mutation for that matter, we will take that import and replace it with a function that calls the Back End to execute that import. Again, So whenever you import from there, we're gonna replace it in a compile step. We're gonna replace it with a function that calls the Backend. So we generate the API for you. That's why it's called the Zero API data layer. For you as a user, you don't have to worry about APIs or for you as a developer, API is none of your business or at least nothing you need to worry about or think about. And then the API is auto-generated for you. And depending on Queries or Mutations we do a lot of some things differently to have it work well with caching and stuff but the concept is the same. And that concept can be called code generation. Code generation is the process of taking some artifacts for example, code and transforming it into code. And that is actually a thing that Blitz uses to make developer experience so good. And Blitz is not the only thing to use code generation. Another tool that uses code generation heavily is Prisma. I promise we're gonna take a look at Prisma and that's what we're gonna do now. So Prisma is that database client by that nice company who who organized this meeting. And it works like the following. So you specify your schema, your Prisma schema in Blitz apps, it says db/schema.prisma and the Prisma schema is a file where you put in your data model for your application. This is in like a Prisma specific language. And you can see like there's two models one for meetup and one for participants and both have like an ID and a name, Meetup set for date. And then there's like a many-to-many relationship between those two. And you take that model and then you run Prisma generate or Blitz db migrate for that matter. Blitz encapsulates it. And when you run that, Prisma takes your artifact, which is the Prisma schema. It does some transformations and then generates this code for you. So this is the generated code. Again code generation. So it takes the schema and for example it makes this type artifact the meetup type of like an ID and the name and some other properties. And it also generates the meetup delegate which is an interface that contains like data access or database access functions that it has like find one function but you can find exactly one or I think now it's called FindFirst, we can find one row in the database. Has like an update, a delete function, all that good stuff and it even has like custom made comments. So, this really like next level develop experience. Because this is normal TypeScript code, your VS Code can fully give you editor support. That's what we've seen a couple slides back. And the interesting thing about Prisma is, it generates into your Node Modules folder. So this file lies at nodemodules/.prisma/clients. So this is also code generation. And turns out code generation is used to Blitz and in Prisma, but not only there, but also in these well-known things. Let's start from the top. Babel, I guess you haven't heard about Babel. Babel is like the mother of code generation or the mother of code transformation. Babel takes modern JavaScript like ESNext, ES7 Or are we at ES9 yet? I don't know. And so you take like a modern JavaScript version and you do some transformations to transform it into an old JavaScript version like ES3 that even internet Explorer can understand. And you can also do like, custom modifications. Which is the very interesting thing about Babel. It's pluggable, So you can have, for example, your own Babel plug in that as an example, I did like, I created a Babel plug in, that for Next.js and SuperJSON users that wraps all of your Next.js routes to use SuperJSON by default, without your developers to do anything. That's what you can use Babel for. So take modern JavaScript, make old JavaScript out of it. Then there's Tailwindcss. Tailwind is a lot more icon to something like Prisma. You take your table and config where there's colors in there and like custom gaping sizes and paddings and all that stuff. And then it generates your own tailor made custom CSS framework for you. There's stuff like Sass, which is basically the TypeScript of CSS, which lets you write CSS at a more, like at a higher level and then Sass comes in and compiles that back to CSS. So, take Sass code and generate CSS code out of it. Again, code generation. And next up lets have Next.js. In Next.js your artifact that you put in is the flow based routing structure. So any file and the pages director is a Route, then Next.js does some transformations and some generation steps to generate an arguably more complex version of your app that fully support Server-side routing server-side rendering clients that routing and of the data fetching methods that Next.js offers.That is also code generation. And there's lot more examples, like, Webpack or even the Jay's house called JSON schema to TypeScript package that even talked about, which is also great. Which takes JSON schema files and then transforms it to TypeScript also code generation. Alright, that's like... It's awesome, but it has its problems. And the biggest problem, like at least for me, is fragmentation, which seems to be kind of like an overarching problem of the JavaScript ecosystem, I guess. But the thing is that, there is no agreed upon a way to do code generation. Everybody builds their own tool chain. And so, I mean like TypeScript has their fully owned passer like let alone their passer is 8,000 lines. Then there's like a Babel which uses a modification of Econ for the passer or Webpack, which gives us normal econ and then a lot of other things. Like Blitz has its own NEXT has its own. They all do their own kinda tool chain. And this comes with performance issues because building a coach and ration tool chain is in a lot of race, very akin to building a compiler. And we all know building a compiler is hard. So yeah, performance issues.Also limited possibility. So you can't just take your Babel plugin and move it to say Webpack. Which yeah, makes it hard to move to a new project move move to new, to move to new tools in your tool chain. Which brings me to the next problem. Like there's no agreed upon standard to do code generation anyway. So, some projects generate into Node modules like Prisma does. Then there stuff like JSON schema to TypeScript that generates into your source directory and then kindly ask you to at their files to the get ignore. There is stuff like Tailwinds, which generates its output code into some hidden the way, pipeline thingy so, that the Bandler can use it And a lot of different ways. And this also results in poor integration because there's no agreed upon standard, no editor can accompany to that. And the only way that like the awesome editor support of Prisma can work is because it generates like real files, like files in your file system that has its problems to you. Like apart from that there's user generated files in Node modules which to be honest, shouldn't belong there. There's the problem of people forgetting about regenerating the TypeScript type, regenerating the Prisma clients. Which I am suspect too as well. Like, I do it all the time. I always change my module and forget to regenerate the client. And yeah. Also not having those standards just makes it hard to build an additional tooling on top of it. So it would be better to have like an official JavaScript standard or at least something like the industry standards. And in the Java ecosystem, this is something that's been there for quite a time, right? 16 years now and since the annotation processing API has been introduced. And annotation processing is actually something where Java code can hook into the compilation process. And it's used in it's in a wide use across the Java ecosystem. So you may have heard of project Lombok or of Spring Boot. Both of them make heavy use of annotation processing. And I'd say that most JavaScript framebooks use it. also C# has introduced support for source generators earlier this year. So yeah, that's also something where people are taking a look at code generation. So, let's see how we could fix this problem in the JavaScript ecosystem. And to see how this could work. Let's have a look at, the conceptional view of our current tooling. So, this is a conceptional view of Babel. Remember the tool that takes like modern JavaScript code and translate it to old JavaScript code. So it takes some source code like modern JavaScript code that then it Lexus and parse it. So Lexing and parsing is like the technical term for making sense of your code and building like a so-called abstract syntax tree which is a, like the representation of your program. And then after Lex and parsed it, It We'll put that into different form step. And transform step in Babel is called Plugins. And so a Plugin is a function that takes the abstracts syntax it de-codes does some transformations and returns it. So, these Plugins are what allow to take modern JavaScript code and transform it. And there can be potentially like a lot of Plugins you can even use your own custom ones. And then after you transformed it, and you have like your your final output you take that abstract syntax and use it to generate output. Alright. Then there is this conceptual view of TypeScript. So TypeScript, you also take some source code and in this example you take TypeScript code, then your next parse it. And then that's the interesting thing about TypeScript you check types. You've got that type system and you do some types on this checking. And so it's stuff like that. And then conceptually that's the point where the editor integration happens after checking types so that your editor knows whether the types are correct or not. And then after we check types we're gonna again, transform it. This time this transformation step is not user pluggable but it depends on what your TypeScript config says. So, when your deployment target it's like ES3 or ES5 or something like that, it will transform a lot like to ES3, ES5 but if it's like ESNext to it, it will only like strip away the types. And then again, it takes and generates output, very similar. So what would it look like if Babel and TypeScript joined forces. If TypeScript added like would become better when it comes to code generation. Because like we've seen Babel does code generation well and TypeScript does not. So, Okay. So this is how it could look like if they joined forces. Can take source code, this time not only JavaScript and TypeScript but also JSON Prisma schema files, table and configs and potentially even like images or something. Any file, you again Lex and Parse it and then you pass it to this pre type checking transformation step. So this is similar to the transform step in Babel. As in it's user configurable it's pluggable. And in this step, that's where our code generation happens. This is where for example, Prisma is and Prisma picks the incoming Prisma schema file and then generates outgoing like it's Prisma client and gives that back into the pipeline. So that when the transforming is done check types will have access to all of the generated files that have been generated by the Prisma compiler plugging. And for JSON schema to TypeScript, this would be even easier. So what you could do is, you could take some JSON file and transform it to TypeScript files, and then in check types you could just like, you could not have to import from a different file, but in your TypeScript code you could just import a JSON and it would export some type. And because the editor integrations and to enter to integration what's happening here it would work wonderful for developers. Alright. And then after checking types, we again transform this time like similar in the TypeScript step, transform to the target platform ES3, ES5. anything you'd like Web Assembly potentially then generate the output. Alright. So, this is nothing that like, I know the TypeScript team to be working off. This is just my own weird thoughts. I'd love to see something like this. And I personally, I'd think it would be useful to the community because it would greatly simplify the process of code generation and does make developer experience a lot better in the process. But this is not the only idea. I've got an excerpt from the blog post on introducing Rome here, by Sebastian Mackenzie. And Sebastian Mackenzie is kinda well-known name because he's the one that developed original version of Babel. So the Babel plugin or the Babel project, that pretty much powers all of our JavaScript tool chains at the moment, except when it's using TypeScript, that's the guy who worked on it. And in this blog post, which is a couple of days old I think or like a couple of weeks, he says, "language tooling "maintainers spend so much time working on the same things "but processing codes or processing source code "whether it's a transpiler like Babel, linting it in ESLint, "or bundling it in a Webpack is fundamentally "the same problem with overlapping responsibilities "and technical implementation. "A linter in the JavaScript ecosystem is exactly the same "as a compiler. They both consume code, "then produce compiled code and errors." So, that's what he says about like the current state of JavaScript tooling. Basically he says, "we all do the same and we work on our own projects "why not join forces and all work on one project instead." And that's what he's doing. So, Rome is the project he currently works at, which is meant as kind of like a spiritual successor to Babel. And it's kind of a unified tool. It does LinkedIn, bundling, compiling, type checking and all of that stuff for you in one package. And thus it wants to be like more efficient and like just a better experience. And that what he's currently working on. I think it's financed by Facebook. Not sure. Yeah. But that could also be the solution to the code generation problem. Like if Rome had a user pluggable code generation step that would solve the problem. Which only needs Rome to efflux support for the language server protocol then, that would be it. Alright, So that's what SAP has to say. Alright, we're coming towards the end of the talk. Let's do a quick recap on this. So, on the first half of the talk where we've seen the Blitz.js or at least parts of Blitz.js. Blitz.js is a fullstack React framework. It has awesome developer experience, at least from what I'm concerned and it's a community maintained project. You should definitely check it out. Like, I say that really like in deep love for Blitz.js it's awesome. And really come buy it us, like we are very welcoming, very inclusive and overall nice community of people helping each other and like sharing a common goal of goods JavaScript or goods on React future, good React tooling. Alright, Blitz.js uses code generation under the hood just as a lot of other tools. code generation has become more and more relevant in the recent years especially in the troublesome ecosystem. It has been kind of a normal thing in other ecosystems but in JavaScript has become more and more relevant. It can be used to supercharge develop experience for example, in Blitz or in Prisma. And it's a huge driver behind the really recent tooling improvements that we've seen. Still, there's some room for improvement in code generation and I'm looking forward to see what will be there in the future. Okay. I originally had like a Shameless plug for Quirrel in the slide, but given how often Daniel has talked about Quirrel, I think we'll just skip this now. And now we're gonna say like, that's all fixed. That's been my talk. And now I'm looking forward to questions from you. - Sorry. My microphone was on mute. So, thank you so much for that talk. That was really insightful. - Yeah. You welcome, Thank you. - I think that was a really good introduction to Blitz and also to a lot of the shifts that we're seeing in the JavaScript and TypeScript ecosystem at large. So, exciting things. And indeed now is the time for questions. So, if you're listening and if you viewed the talk and you have questions now is the time to drop your questions in the chat. Thank you for the comment. And if there are no questions, I think we can go on to the quiz before we wrap up for today. Okay. Let's get on with the quiz. So, we have some really positive feedback. - Thank you. - I think one of the nice things that I've noticed is that... Oh yeah, we have one from Ema. "Amazing framework. Haven't heard about it before." "Looks promising. Is it production ready?" - Yeah. That's an interesting question actually. Because what I would tell you, like from an emotional kind of standpoint is different what like the rational Simon would tell you. So if you're gonna... If production-ready really means for like your your big project where you've tons of tons of money. No, it's not. It is currently still an alpha. I think I forgot to mention that. Currently it's an alpha and we are moving beta in the next couple of days actually and aiming to go to 1.0 by the end of the year. However, I am currently using Blitz and I know a lot of other people are already using Blitz in their own projects, also in production. So, for example, the Quirrel Landing page and the Quirrel dashboard which is the place where you can like put in your payment details and get like Quirrel API keys, and then see like statistics. That is fully developed using Blitz and like it really makes me productive. And then there's also some other like smaller tools that have been developed using it Blitz. So for example, a couple of days ago, there was the React Summit Conference. You may have heard of that. And they had like some Conference Badge Application that we actually found out was built using Blitz. And at the moment that's like the prime thing, because it's.... It's not that important, but the cool thing about Blitz is makes you super productive also on these more fun projects. So yeah. Is the production ready? Kinda depends. So for me, it is for my own projects, but I'm also level two maintainer, and whenever there's a problem, I mostly can get it fixed myself and provide a Buck for other things I don't know. But yeah, if you interested definitely check by at the Slack and people will answer you all your questions you have. - So we had another question coming in from George. And I guess this is also touching on probably one of the biggest value propositions of Blitz. And that is, that there is no sort of like abstracts the API layer for you. - Yeah. - And so George is asking, "how flexible, configurable "or pluggable is the transport layer?" - So, I'm not sure if I fully understood that question because the way I see it, that the developer position of Blitz is that it abstracts array that transport layer. So, making it configurable is not really something we're interested to do. We're interested in providing like, the one solution that does everything. There may be things that like I'm not really sure what you mean by that. If it's stuff like, I have my own data types that I want to be transferred correctly. That's something we've got figured out. So, Dan has mentioned that I am the code owner for SuperJSON which is Blitz's own JSON civilization framework. So, it basically takes some JSON and generates another JSON out of it. So, again code generation basically. But this time in runtime. Generates another JSON out of it. And I add some annotations on what the original types were so, that when we transferred over the wire we can then like reapply those annotations to preserve the types of things. Because so, maybe you know that when you, JSON.stringify a date object, it will be downgraded to a string, but with SuperJSON, it won't. So, it will be downgraded to a string and then get the annotation attached and then afterwards we just reapplied the annotation to make a string out of it. That's something we do in a transport layer. That may be interesting to you. - Alright. And now I think is the time we had one last question. Maybe you can sort of give a short answer to that. "Is there an example of a mid-size or large size application "built on Blitz?" - Well, large size, I don't think so. Midsize, maybe check out the Quirrel webpage which is open-sourced or take a look at the Blitz Wiki. There is a list of apps that are in production running Blitz and there are something like a Blitz Space Job Board and some like Startup Landing Pages and Start up applications that could be considered a mid-size, I think.