Hey! Welcome to the first technical deep dive on my blog. I've been having a blast sharing life updates while simultaneously diving into NestJS and Next.js. I'd like to write about my experience developing with NestJS and some snippets of how I implemented certain things for my custom life-box-blog. Let's begin 😄
title
, content
, image
, and created_at
. All of which are defined as a type in my Next.js frontend, which I've already set up a while back.next/server
(kind of like a proxy). I enjoy using next/server
as it has a lot of nice caching features that I don't have to set up on my own. I also really enjoy hosting my projects on Vercel as it's super intuitive and easy to use.With the plan laid out, let's see how this is set up in code.
We first need to set up the Firebase module in order to instantiate and use the Firebase admin SDK from the Nest app:
I created a file within the src of my Nest app directory:
src/firebase/firebase.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as admin from 'firebase-admin';
const firebaseProvider = {
provide: 'FIREBASE_APP',
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
const privateKey = configService.get<string>('PRIVATE_KEY') || '';
const formattedPrivateKey = privateKey.replace(/\\n/g, '\n');
const firebaseConfig = {
type: configService.get<string>('TYPE'),
project_id: configService.get<string>('PROJECT_ID'),
private_key_id: configService.get<string>('PRIVATE_KEY_ID'),
private_key: formattedPrivateKey,
client_email: configService.get<string>('CLIENT_EMAIL'),
client_id: configService.get<string>('CLIENT_ID'),
auth_uri: configService.get<string>('AUTH_URI'),
token_uri: configService.get<string>('TOKEN_URI'),
auth_provider_x509_cert_url: configService.get<string>('AUTH_CERT_URL'),
client_x509_cert_url: configService.get<string>('CLIENT_CERT_URL'),
universe_domain: configService.get<string>('UNIVERSAL_DOMAIN'),
} as admin.ServiceAccount;
return admin.initializeApp({
credential: admin.credential.cert(firebaseConfig),
storageBucket: `${configService.get<string>('PROJECT_ID')}.firebasestorage.app`,
});
},
};
@Module({
imports: [ConfigModule],
providers: [firebaseProvider],
exports: ['FIREBASE_APP'],
})
export class FirebaseModule { }
The configuration is also set up as secrets from a local (and later deployed) environment.
Next, we have to create a basic module for containing the API logic of the blog. I'm calling it the blog module.
nest g resource blog
This automatically creates the controller, module, service, and some other building blocks like tests and even a DTO folder for defining your object structure.
I won't use any of the other blocks for the meantime; I want to focus on building the logic for the blog API.
Within the Blog module, we import the Firebase module:
src\blog\blog.service.ts
import { Injectable, Inject } from '@nestjs/common';
import * as admin from 'firebase-admin';
@Injectable()
export class BlogService {
constructor(
@Inject('FIREBASE_APP') private firebaseApp: admin.app.App
) { }
getFirestore() {
return this.firebaseApp.firestore();
}
async getCollection(collectionName: string) {
const snapshot = await this.getFirestore().collection(collectionName).get();
return snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
}
async getDocument(collectionName: string, documentId: string) {
const docRef = await this.getFirestore()
.collection(collectionName)
.doc(documentId)
.get();
if (!docRef.exists) {
return null;
}
return {
id: docRef.id,
...docRef.data(),
};
}
async createDocument(collectionName: string, data: any) {
const docRef = await this.getFirestore().collection(collectionName).add({
...data,
created_at: new Date()
});
return {
id: docRef.id,
...data
}
}
}
Then to utilize the service, I can make a super simple controller!
src\blog\blog.controller.ts
import { Controller, Get, Param } from "@nestjs/common";
import { BlogService } from "./blog.service";
@Controller('blog')
export class BlogController {
constructor(private readonly blogService: BlogService) { }
@Get(':collection')
async getCollection(@Param('collection') collection: string) {
return this.blogService.getCollection(collection);
}
@Get(':collection/:id')
async getDocument(@Param('collection') collection: string, @Param('id') id: string) {
return this.blogService.getDocument(collection, id)
}
}
There we go! A super simple blog API. From here, I simply deploy the NestJS server on a hosting service (Vercel) and allow my Next App to access the API there. My next implementation will be all about setting up some Auth and a basic CMS where I can post my entries without directly accessing the Firebase console.