Blitz.js is an up-and-coming JavaScript framework built on React and Next.js. It is a full-stack, opinionated framework—meaning that it makes certain assumptions about how to structure your JavaScript applications. Perhaps the most interesting aspect of Blitz is its so-called zero-API approach, wherein the framework does the work of connecting the user interface to the back-end datastore.
Let’s get a hands-on tour of this interesting and unique take on JavaScript application development.
Set up the Blitz demo
To begin, add Blitz as a global NPM package with the command: npm i blitz@alpha -g. Now, you can use the tool to create a new project by typing: blitz new demo-app. Listing 1 shows my setup for the demo application.
Listing 1. Create a new Blitz application
$ blitz new demo-app Pick which language you’d like to use for your new blitz project › Javascript Pick which template you’d like to use for your new blitz project › full Install dependencies? … yes Pick which form you’d like to use for your new blitz project › React Hook Form Hang tight while we set up your new Blitz app!
After Blitz finishes installing all of its dependencies, you can move into the new directory that’s just been created, cd demo-app, and start the development server with the command blitz dev. The server is now running at localhost:3000, and you’ll get a welcome screen like the one shown here:
IDG
Figure 1. The Blitz welcome screen.
The first thing you may notice is that unlike most front-end frameworks, Blitz’s generator provides more elaborate full-stack scaffolding; in particular, there is support for user creation and logins. If you play around with these features, you’ll see that you can create a user and log them in and out.
Add a model
Now let’s do as the welcome page suggests and add a model via the command-line. Go to the command-line and hit CTRL-C to stop the development server. Enter blitz generate all project name:string. This command tells Blitz to add a new model object, called project, with a single string field called name.
Migrate the database via Prisma
When prompted, confirm that you want to run prisma migrate dev. Blitz has installed and is using the file-based SQLite database, which is mapped via Prisma, the object-relational mapping (ORM) layer. SQLite and Prisma are where and how data objects are persisted. The prisma migrate dev command informs Prisma to update itself to reflect changes in the dev database (the default database used for development).
Create a new project
When the generator completes, start the server again with the command blitz dev. Return to the browser and visit localhost:3000/projects. You’ll see a new user interface, which you can use to create a new project. (Hit Create Project.)
If you are not logged in when you attempt to create a new project, you’ll get the message: “Error: You are not authenticated.” This confirms that authorization is already working.
Now log in as a user and try the Create Project option again. This time, you’ll be presented with a form that contains a single name field. You can create some projects and you’ll observe that Blitz is scaffolding a reasonable RESTful schema for you.
The localhost:3000/projects page gives you a list of projects, which you can click on to get the project details at localhost:3000/projects/
Layout of a Blitz project
Let’s take a look at the project layout in Blitz. This gives us a sense of how Blitz manages to do so much for us.
- /project-root
- /db
- /db.sqlite: The SQLite engine and schema
- /schema.prisma: The Prisma ORM mappings
- /migrations: The directory showing the migrations (useful for rollbacks)
- /mailers: Contains stubs for configuring mailers like the Forgot Password mailer
- /jest.config.js: The JEST testing framework config
- /next.config.js: The Next.js config file
- /pages: The React.js and Next.js front-end files
- /api: Support for external (non-Blitz) API access
- /auth: The log in, log out, and signup pages
- /projects: Pages for the project entity. Other objects follow the same pattern.
- /test: Test files (JEST)
- /app: App infrastructure
- /blitz-client.js: The client-side config
- /blitz-server.js: The server-side config
- /core: The components, hooks, and layouts for the application
- /projects: The queries, mutations, and components for the project object
- /auth and /user: Auth related queries, mutations, and components
- /integrations: Third-party integrations like Auth0 and Sentry
- /package.json: The Node config file including Blitz scripts like dev
- /public: Static files like favicon
- /db
RPC in Blitz.js
The most unusual thing about Blitz is its use of remote procedure calls, or RPC. Instead of using a REST or GraphQL API, you import your server-side code directly into your client-side JavaScript. Blitz then transforms the server-side code into an RPC call. You can access the server-side code directly from your client-side JavaScript, and Blitz will wire the network interactions to make it all work.
Now, let’s open the file at blitz-demo/pages/projects/[projectId]/edit.js. Note that the square bracket syntax is how Next.js handles path slugs. (The projectID variable will be exposed to the page that handles the request, holding the value of the parameter at that position in the path.)
Listing 2 has the main significant parts of the Blitz demo.
Listing 2. Projects edit.js
import { Suspense } from “react”; import Head from “next/head”; import Link from “next/link”; import { useRouter } from “next/router”; import { useQuery, useMutation } from “@blitzjs/rpc”; import { useParam } from “@blitzjs/next”; import Layout from “app/core/layouts/Layout”; import getProject from “app/projects/queries/getProject”; import updateProject from “app/projects/mutations/updateProject”; import { ProjectForm, FORM_ERROR } from “app/projects/components/ProjectForm”; export const EditProject = () => { const router = useRouter(); const projectId = useParam(“projectId”, “number”); const [project, { setQueryData }] = useQuery( getProject, { id: projectId }, { staleTime: Infinity } ); const [updateProjectMutation] = useMutation(updateProject); return ( <>
Edit Project {project.id}
{JSON.stringify(project, null, 2)}
> ); }; const EditProjectPage = () => { return (
}>