[RFC] Important Discussion On Possible Blitz Pivot · blitz-js blitz · Discussion #3075

12 min read Original article ↗

Hello to all my friends :)

I have some important and weighty things to discuss.

TLDR: I’m proposing to pivot Blitz to a framework agnostic toolkit that preserves the world-class DX and features we adore but brings it to all Next.js users and optionally Remix, SvelteKit, Nuxt, etc.

I’ve been taking stock of where we are with Blitz and of what we’ve accomplished in the almost 2 year journey together. What we have done is incredible. We invented a fullstack developer experience that is unmatched, enabled by our zero-API RPC data layer. So many tell us it makes them 5-10x more productive 🤯 Nearly 100,000 Blitz projects have been created. No doubt many of those are throwaway tests, but still, that's a ton!! We recently passed the 10,000 Github Stars milestone that so few reach. Our community has grown to 2,500 Discord members and is absolutely incredible. We’ve had almost zero negative incidents. Everyone is so kind and generous.

We’ve also achieved incredible awareness. Tons of people are really impressed with what we’ve made. But at the end of the day, 99% go back to Next.js to build their production apps.

The reality is that we are, and always will, play second fiddle to Next.js. And so sadly, 99% of people never get to benefit from what we've built.

Growth has totally stagnated this year. Our weekly npm downloads is a perfectly flat line this year with around 5,000/week meanwhile Next.js downloads are growing exponentially and is now at 2.2 million per week. When we made the decision to fork Next.js, I expected that Blitz would start to carve out a small portion of Next.js users. But that absolutely hasn't happened. In fact, I think the fork has actually hurt adoption. That said, I still feel trying the fork was the right decision. I have a way better vision for what a Blitz toolkit should be now than I did then.

image

The Next.js fork has nearly killed our feature momentum. And we are no longer executing on the vision we set out to solve. Instead we spend so much time spinning our wheels fixing weird package manager things, keeping up with nextjs changes (we have no inside knowledge of nextjs plans), etc. Lack of momentum and lack of growth really hampers our motivation.

At the end of the day, I can't see anything that would significantly change our growth trajectory without a pivot.

If we’re always going to play second fiddle to Next.js, the best solution is to find another instrument no one is already playing :)

Proposal

First let me say that the thought of not having Blitz is terrifying. I’m building the Flightcontrol dashboard with it, and I can’t imagine having to do it all from scratch without the Blitz DX.

And I know I'm not alone in this. Many of you have told me the massive impact it has had on the productivity at your companies, your ability to hire and onboard developers, etc.

So whatever would come next, it absolutely must preserve the DX and huge productivity wins that current Blitz has.

Objectives

  • Preserve the DX and features we currently have
  • Make it easy for millions more of developers to benefit from Blitz
  • Decouple Blitz from any specific framework

Therefore I’m proposing to pivot Blitz to a framework agnostic toolkit that preserves the world-class DX and features we adore but brings it to all Next.js users and optionally Remix, SvelteKit, Nuxt, etc.

A toolkit for building anything with a database, from side projects to massive enterprise apps. A toolkit that both enhances fullstack DX and provides the missing backend components for JS that Rails and Laravel enjoy.

Keep reading, I flesh out the vision more below.

Trust & Transition

I really care about you all, the time and effort you’ve invested into Blitz, and your involvement in the community. It’s been a super fun journey, and we’ve come a long ways. But I truly feel we have reached the peak. I don’t see any possible way to go from here to the future we imagined and dreamed of, without a significant change. It’s time to face some realities and decide what is best for us all

You all have put incredible trust in me, building many thousands of production apps on an alpha and then beta status framework. So I feel a heavy responsibility to not damage that.

You have my word that, should we choose to pivot, I will do everything we can to make for a smooth transition from current Blitz to Next.js + Blitz Toolkit. This means preserving APIs as much as possible and heavy reliance on codemods to automate almost all changes including renaming imports from blitz to next , etc.

It would be very sad to lose Blitz as we currently know it (I’ve shed some tears). But many times in life we have to make difficult decisions to lose some things in order to gain even greater things.

I’ve already discussed this privately with a sampling of folks from our community, and all of them have basically been like “yeah it would suck to have some sort of migration, it would be sad to lose what we have, but also it makes sense that this would be the best for us.”

What The New Blitz Toolkit Could Look Like

The New Future, Features, and Possibilities

I originally set out to make the Ruby on Rails equivalent for Javascript and React. But I’m now of the opinion that the surface area for something like this is too massive for one framework to do well. Using a JS UI library like React or Vue adds so much more complexity to the problem. Complexity that Rails and Laravel knowing smirk :) However, I want to give huge props to RedwoodJS. They have done a way better job at executing on that very Rails-y flavor, at least in part because they've built everything from scratch. They also had ~1 year head start.

If we take a look at the entire JS ecosystem, most of the money and effort is going into frontend frameworks, but very little into backend functionality like queues, cron jobs, etc. There are Nestjs and Adonis, but they aren’t exactly designed to work with frameworks like Next.js/Nuxt/Remix.

This means there’s a huge opportunity for us to provide all of the backend features that Rails and Laravel enjoy, and do it in a way that all JS frameworks will love. It was always my intention to build these features for Blitz, but got bogged down too much to get to them.

My vision for the new thing is an enterprise-ready fullstack toolkit with an emphasis on backend components.

Blitz would be a DX booster pack you can add to anything to ship faster and have more fun.

Features

I believe the core value that Blitz currently provides is 1) the zero-api data layer, 2) authentication, 3) authorization, 4) conventions, 5) new app templates & code scaffolding.

A new toolkit would start with these as the foundation. I think almost everything Blitz currently adds would be migrated to the toolkit including recipes, typesafe Routes manifest, etc.

Then we’d start shipping a ton more awesome things. Here’s the features I want to build. And all them would be standalone modules that you can use independent of the others.

Current New Toolkit
Zero-API Data Layer
Authentication - cookie based
Authentication - JWT 🚀
Authorization - basic RBAC
Authorization - advanced, permission-based RBAC 🚀
New app templates
Code scaffolding
Recipes
Fullstack logging 🚀
Fullstack environment variable management 🚀
Websockets 🚀
Cron jobs 🚀
Scheduled jobs 🚀
Job queues 🚀
Data migrations 🚀
Mailers/Email integration 🚀
File uploads 🚀
Caching layer 🚀
Billing integration 🚀
Better support for service oriented architecture 🚀
GraphQL data layer 🚀 (if desired)

New Zero-API Data Layer Setup

  1. Query/Mutation resolver API would stay exactly the same.
  2. Queries/Mutations folders are no longer magic, so you import into a server setup file
  3. Instead of importing resolvers into the components for useQuery, you will specify the name of the query/mutation
  4. Everything will be explicit, so you’ll need to wrap everything for which you need Blitz functionality, like getServerSideProps, api routes, etc.

Server setup file

// blitz.server.ts
import { setupServer, sessionMiddleware } from "@blitzjs/next"

// queries/mutations folders are no longer "magic".
// You know have to explicitly import into this setup file
import createProject from "app/projects/mutations/createProject"
import updateProject from "app/projects/mutations/updateProject"
// You can now have multiple resolvers in a single file
import * as projectQueries from "app/projects/queries"

// api, gSSP, gSP will be used to wrap everything for auth and middleware
export const { api, gSSP, gSP, Server } = setupServer({
  middleware: [sessionMiddleware],

  queries: {
    ...projectQueries,
  },
  mutations: {
    createProject,
    updateProject,
    deleteProject,
  },
})

export type Server = typeof Server

Expose your server in an API route

// pages/api/rpc/[...path].ts
export { Server as default } from "blitz.server";

Client setup file

// blitz.client.ts
import { setupClient, clientSessionMiddleware } from "@blitzjs/next";
import type { Server } from "./blitz.server";

export const {
  useQuery,
  useQueries,
  useInfiniteQuery,
  useMutation,
  queryClient,
  BlitzProvider,
} = setupClient<Server>({
  middleware: [clientSessionMiddleware],
})

Using Queries/Mutations on Client

// some component or page
import {useQuery, useMutation} from 'blitz.client'

// Option 1 (strongly typed)
const [project] = useQuery.getProject({ id: 1 });
cosnt [createProjectMutation] = useMutation.createProject()

// Option 2 (also strongly typed)
const [project] = useQuery("getProject", { id: 1 });
cosnt [createProjectMutation] = useMutation('createProject')

Wrap getServerSideProps

// pages/ssr.tsx
import {gSSP} from 'blitz.server'
import getProject from 'app/project/queries/getProject'

// fully typed automatically
export const getServerSideProps = gSSP(async ({req, res, ctx}) => {
  // access session
  ctx.session.userId

  // Directly call query fn, pass `ctx` through
  const project = getProject({id: 1}, ctx)

  return {
    props: {}
  }
})

Wrap getStaticProps

// pages/static.tsx
import {gSP} from 'blitz.server'
import getProject from 'app/project/queries/getProject'

// fully typed automatically
export const getStaticProps = gSP(async ({queryClient}) => {

  // Easily prefetch query to fill client side cache
  await queryClient.prefetchQuery('getProject', {id: 1})

  return {props: {}}
})

Wrap API routes

// pages/api/hello.ts
import {api} from 'blitz.server'
import {trackEvent} from 'app/events/mutations'

const handler = api((req, res, ctx) => {
  // access ctx and session
  ctx.session.$authorize()

  // Call query/mutation directly, pass ctx through
  await trackEvent('api', ctx)

  res.statusCode = 200
  res.setHeader("Content-Type", "application/json")
  res.end(JSON.stringify({ name: "Secured Agent" }))
})
export default handler

Cons

  • Some migration work for anyone currently using Blitz (but again, we’ll make this as seamless as possible)
  • Lose ability to have multiple pages folders and pages folders inside app/
  • Lose local middleware for queries/mutations (very few, if anyone, uses this)
  • Can no longer fine-tune every aspect of DX since we wouldn’t be the entire framework (but we basically haven’t been able to do this anyways because of staying compatible with nextjs)

Pros

  • Almost everyone can now use Blitz without framework lock in
  • Reduce framework fatigue in the ecosystem
  • Less scope, so less bugs, less flakiness, less responsibility
  • Way more motivation for Blitz core team and maintainers
  • Will ship at a way faster pace
  • More modular and decoupled. For example other frameworks could use new standalone blitz-auth package
  • Mobile app/multi-client will work right away without custom compiler work!

Few Misc Notes

  • Next.js + Blitz Toolkit would still function as a monolith (if you want). So no major changes to your regular workflow or deployments.
  • Multi-framework support: we’d focus on Next.js initially but architect the new packages to easily other framework adapters if we want to.
  • Existing uses of things like Page.authenticate would continue to work.

So What Now?

Decision

First thing is to get all your feedback and make a decision on whether to move forward with this or not. Aiming to make a decision next week.

We can also spawn off other RFC discussions to discuss feature and API specifics.

Building The New Blitz Toolkit

If we move forward, there’s different paths to take, but I think the best is for Aleksandra and I to pause any new work on existing Blitz and focusing on shipping new Blitz Toolkit in January. At which point people could start building new apps with it or manually migrating existing Blitz apps. Then finish codemods etc that are needed to automate migrations as much as possible for everyone else.

I would petition the Next.js team with all my might to accept as many of the current Blitz-only features as possible like app env loading with next -e staging, onServerStart hook, fully typed Routes manifest, etc. Whatever they refuse, we can find a way to solve it with the new toolkit. But I trust they will be flexible and willing to work with us.

Status of Current Blitz

Your existing Blitz apps will continue to run as they always have, and we will fix any critical bugs the come up.

The current blitz codebase will remain. It may move to a different repo, but it will be preserved and we can continue to make releases as needed.

Your Feedback

I know this is a big deal and has a big effect on all of us. You are probably feeling a number of emotions right now. So I invite you to avoid a knee-jerk reaction, take a little time to process it, and then please share your perspective on what we should do. 💜