2 septembre 2020
Découverte de Blitz JS
6 minutes de lecture
BlitzJS est un framework React basé sur Next.js qui se veut totalement fullstack contrairement à Next qui est plus orienté front-end. Blitz ajoute notamment des fonctionnalités de connexion à une base de données, l'ajout de middlewares, mais aussi un système d'authentification. Blitz s'est inspiré de Ruby on Rails (framework Ruby pour créer des applications web fullstack), on retrouvera notamment certains aspects au niveau de la structure du projet.
Nous allons créer une petite application web à l'aide de Blitz où un utilisateur peut s'authentifier, puis accéder à une liste d'articles de blog qu'il pourra créer, éditer et supprimer.
Initialisons notre projet :
npx blitz new myapp
cd myapp && yarn start
Durant la phase de création, il vous sera demandé quelle librairie de formulaires vous souhaitez utiliser, nous allons utiliser react-hook-form, mais libre à vous de choisir celle avec laquelle vous êtes le plus à l'aise.
Avant de nous jeter sur le code, jetons un oeil sur la structure de notre projet :
myapp
├── app
│ ├── auth
│ │ └── components
│ │ | ├── LoginForm.tsx
│ │ ├── mutations
│ │ │ ├── login.ts
│ │ │ ├── logout.ts
│ │ │ └── signup.ts
│ │ ├── pages
│ │ │ ├── login.tsx # http://localhost:3000/login
│ │ │ └── signup.tsx # http://localhost:3000/signup
│ │ ├── auth-utils.ts
│ │ └── validations.ts
│ ├── components
│ │ ├── Form.tsx
│ │ └── LabeledTextField.tsx
│ ├── hooks
│ │ └── useCurrentUser.ts
│ ├── layouts
│ │ └── Layout.tsx
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── 404.tsx
│ │ └── index.tsx
│ └── users
│ └── queries
│ └── getCurrentUser.ts
├── db
│ ├── migrations
│ ├── db.sqlite
│ ├── index.ts
│ └── schema.prisma
├── integrations
├── node_modules
├── public
│ ├── favicon.ico
│ └── logo.png
├── utils
├── .eslintrc.js
├── .env
├── .prettierignore
├── babel.config.js
├── blitz.config.js
├── jest.config.js
├── package.json
├── README.md
├── tsconfig.json
└── yarn.lock
Comme on peut le voir, un des premiers avantages est que notre application est générée en TypeScript. En se rendant dans les composants app/components/Form.tsx
et app/components/LabeledTextField.tsx
, on se rend compte qu'ils ont été générés par rapport à la librairie de formulaire que nous avons choisie. Ce composant peut être modifié selon la librairie UI que vous souhaitez utiliser, ou selon votre CSS.
Nous avons aussi toute notre partie authentification qui a été générée, cela inclut nos routes API pour la connexion, l'inscription et la déconnexion. Le formulaire dans app/auth/components/LoginForm.tsx
a été généré selon les différents champs de notre modèle User.
Base de données
Blitz utilise par défaut Prisma 2 pour gérer la connexion à notre base de données, mais il est toutefois possible de se passer de Prisma au profit d'autres librairies, comme par exemple TypeORM. Un exemple d'utilisation de Blitz sans Prisma est disponible sur le GitHub.
Ouvrons notre fichier db/schema.prisma
. On peut constater que Blitz a généré automatiquement 2 modèles: User
et Session
. Nous allons maintenant devoir ajouter un modèle Articles
qui correspondra à nos articles de blog. Pour cela, le CLI de Blitz fournit une commande permettant à la fois de générer ce modèle, mais aussi de générer nos pages et routes API liées à ce modèle :
yarn blitz generate all article title content belongsTo:user
En se rendant dans notre fichier db/schema.prisma
, on peut voir notre modèle Article. Apportons-y des modifications en renommant nos champs user
et userId
respectivement en author
et authorId
. Voici ce à quoi devraient ressembler les modèles après ces modifications :
model User {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
email String @unique
hashedPassword String?
role String @default("user")
sessions Session[]
articles Article[]
}
model Session {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
handle String @unique
user User? @relation(fields: [userId], references: [id])
userId Int?
hashedSessionToken String?
antiCSRFToken String?
publicData String?
privateData String?
}
model Article {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
Enfin lançons la commande suivante pour lier ces modifications à notre base de données via une migration :
yarn blitz db migrate
Les commandes de génération de modèles et de migrations ne fonctionnent que si vous utilisez Prisma. À l'heure actuelle, le CLI ne permet pas de se brancher à un autre client tel que TypeORM.
Un nom de migration vous sera demandé, nous allons la nommer AddArticle
. À l'issue de cette migration, Prisma aura généré des types TypeScript liés à vos modèles. Néanmoins ces types seront incomplets, par exemple le champ author
de notre modèle Article
ne sera pas présent dans le type associé. Je vous déconseille fortement d'accéder au fichier de type de Prisma, car cela cassera l'autocompletion sur VSCode, et vous devrez alors totalement désinstaller puis réinstaller les node_modules
, suivi d'un yarn blitz db migrate
.
En plus du modèle Prisma, notre commande de migration a généré tout un dossier au sein de app
.
app
└── articles
├── components
│ └── ArticleForm.tsx
├── mutations
│ ├── createArticle.ts
│ ├── deleteArticle.ts
│ └── updateArticle.ts
├── pages
│ ├── [articleId]
│ │ └── edit.tsx # http://localhost:3000/articles/:id/edit
│ ├── [articleId].tsx # http://localhost:3000/articles/:id
│ ├── index.tsx # http://localhost:3000/articles
│ └── new.tsx # http://localhost:3000/articles/new
└── queries
├── getArticle.ts
└── getArticles.ts
À la manière de Ruby on Rails, on a ici un CLI qui nous génère tout le nécessaire pour pouvoir effectuer des actions CRUD sur une ressource donnée, le tout en y ajoutant les vues nécessaires.
Interaction avec la base de données
Contrairement à Next.js, Blitz nous permet d'interagir directement avec notre base de données sans avoir besoin de générer des routes API. C'est ce qu'on retrouvera au sein des fichiers situés dans les dossiers mutations
et queries
de nos ressources. Prenons par exemple le contenu de createArticle.ts
:
import { SessionContext } from 'blitz'
import db, { ArticleCreateArgs } from 'db'
type CreateArticleInput = {
data: Pick<ArticleCreateArgs['data'], 'content' | 'title'>
}
export default async function createArticle(
{ data }: CreateArticleInput,
ctx: { session?: SessionContext } = {}
) {
ctx.session!.authorize()
const article = await db.article.create({
data: {
content: data.content,
title: data.title,
author: {
connect: { id: ctx.session?.userId },
},
},
})
return article
}
- Dans un premier lieu nous vérifions si la requête est authentifiée grâce à
ctx.session!.authorize()
. Cette session est créée par nos mutations situées dansapp/auth/mutations/login.ts
etapp/auth/mutations.signup.ts
. - Nous effectuons ensuite la requête vers notre base de données pour créer un article dont l'auteur correspond à l'utilisateur courant.
Cette fonction createArticle
est appelée depuis notre page app/articles/pages/new.tsx
:
const onSubmit = async (values: ArticleInputType) => {
try {
const article = await createArticle({ data: values })
router.push('/articles/[articleId]', `/articles/${article.id}`)
} catch (error) {
toast({
title: 'Error',
description: 'Failed to create article, please retry',
status: 'error',
duration: 3000,
})
}
}
On interagit donc ici avec notre base de données directement depuis notre composant sans devoir passer par une route API comme sur Next.js.
Déploiement
Tout comme pour une application Next.js, il est possible de déployer une application Blitz sur Vercel. Il est toutefois nécessaire de posséder une base de données hébergée sur un autre serveur. Plus de détails ici.
En conclusion
Blitz.js est un framework très complet apportant son lot de fonctionnalités intéressantes manquants à Next.js. La partie authentification se veut assez complète avec notamment une gestion de rôles que nous n'avons pas abordée. Il est aussi possible d'ajouter d'autres types d'authentification (Twitter, Github, etc). L'outil de générations du CLI ainsi que le support de plusieurs librairies de formulaires populaires nous accordent un gain de temps non négligeable dans le développement. À l'heure actuelle, le framework est encore en alpha et des bugs peuvent subsister. Néanmoins, le développement est à un état assez avancé pour une mise en production. Je vous conseille en tout cas d'essayer ce framework si pour vous, avoir une application Next.js + un autre framework back-end est plus exhaustif qu'autre chose.
La documentation, très complète, se trouve ici.
Vous pourrez aussi retrouver le projet de démo ici.