🪟 Windows TippsAffinity Ships a Major Update With Over 60 New Features(17.09.2026 um 19:13 Uhr)
🤖 Android TippsNutzer von Google Fotos dürfen sich auf großes Update freuen(17.09.2026 um 18:32 Uhr)
🕵️ HackingOnline-Täter locken Kinder in Chatgruppen - ooe.ORF.at(17.09.2026 um 17:22 Uhr)
🪟 Windows TippsAffinity Ships a Major Update With Over 60 New Features(17.09.2026 um 19:13 Uhr)
🤖 Android TippsNutzer von Google Fotos dürfen sich auf großes Update freuen(17.09.2026 um 18:32 Uhr)
🕵️ HackingOnline-Täter locken Kinder in Chatgruppen - ooe.ORF.at(17.09.2026 um 17:22 Uhr)
🔧 Programmierung 🕛 vor 2 Jahren 37 Min Lesezeit
0

Nestjs, Firebase, GCloud. How to Quickly Set Up an API Backend in TypeScript.

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

It's great that you decided to open this article. My name is Fedor, and I've been a full-stack developer on a permanent basis since the end of 2021. Just in case, here is my in advance.






Personal Experience



Although I only started working full-stack a few years ago, I have already had the chance to try my hand at cross-functional projects on different stacks and platforms, mainly JS/TS. I got acquainted with NestJS when I joined a company that chose the path of isomorphic full-stack development, and since then, this path has stuck with me. Over the years of working on various projects, I managed to create a simple and minimal boilerplate for the next MVP startup or pet project. I sincerely hope that this article and the final repo will make your life easier.






Before Initialization



I will be writing this boilerplate from my perspective and environment, so first things first: I work on the MacOS platform. All suggested actions should be cross-platform, but I do not rule out some difficulties. I welcome any questions and recommendations in the comments under this post.






My Setup



In the terminal, I use the ZSH shell instead of the standard BASH, so the first link is , a version manager for Node.js.



Lastly, since developers often have many projects, I use




  • Step 2: That's it for now; the main thing is to choose the package manager.



At this point, you should have .





Let's update the package.json by adding the following commands to the scripts section:




CODE
...,
scripts: {
...,
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
"lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
},
...






Be sure to install a new dependency (this is a plugin for the eslint configuration):




CODE
pnpm add -D eslint-plugin-simple-import-sort






Next, let's configure the prettier file.



. If we also run the command pnpm run lint:fix, we'll get the . If you've never worked with Firebase before, you'll have a mostly empty dashboard.





So far, so simple. Next, we need to enable the services: Firestore, Authentication, and Storage. These sections can be found on the left sidebar of the console, under the "Build" section.



.



Creating a database will require you to choose a server location; you can choose whichever is convenient for you depending on where your users are located. I usually choose Europe (Eur3). You can also select the launch mode; it's safe to leave the database in production mode. Note: I’ve tried us-central1 and eur3, and haven’t noticed any significant difference in runtime speed.



Now you have a fully set up Firebase project. By the way, you can easily generate multiple Firebase projects for different environments (Production/Stage/Local/Test); for most of my projects, this setup is sufficient.



Let's move on to the next step. We need to obtain the necessary data to run the project. To do this, go to your to the project is set up.



But that's not all! Next, we will cover:




  • The Firestore module

  • Examples of data models with controllers and services

  • Adding the gcloud bucket module for file operations






Firestore Module



Let's start with the module itself. Create a folder src/providers/firestore. In this folder, we will organize the necessary code for connecting to Firestore collections.



First, install the Firestore package: pnpm add @google-cloud/firestore.



In the firestore folder, add 4 files:





  • firestore.module.ts - for connecting to the project's AppModule


  • firestore.providers.ts - for listing Firestore entity documents


  • types.ts - for typing the module


  • index.ts - for cleanly re-exporting the Firestore module




CODE
// firestore.providers.ts
export const FirestoreDatabaseProvider = 'firestoredb'
export const FirestoreOptionsProvider = 'firestoreOptions'
export const FirestoreCollectionProviders: string[] = [/* Next, you will need to add classes for Firestore collection documents.
*/]









CODE
// types.ts
// Add a type for typing the module arguments
import { Settings } from '@google-cloud/firestore'

export type FirestoreModuleOptions = {
imports: any[]
useFactory: (...args: any[]) => Settings
inject: any[]
}









CODE
// firestore.module.ts
// Here, we are creating our own module provider for Firestore collections
import { Firestore } from '@google-cloud/firestore'
import { DynamicModule, Module } from '@nestjs/common'

import {
FirestoreCollectionProviders,
FirestoreDatabaseProvider,
FirestoreOptionsProvider,
} from './firestore.providers'
import { FirestoreModuleOptions } from './types'

@Module({})
export class FirestoreModule {
static forRoot(options: FirestoreModuleOptions): DynamicModule {
const collectionProviders = FirestoreCollectionProviders.map((providerName) => ({
provide: providerName,
useFactory: (db) => db.collection(providerName),
inject: [FirestoreDatabaseProvider],
}))

const optionsProvider = {
provide: FirestoreOptionsProvider,
useFactory: options.useFactory,
inject: options.inject,
}

const dbProvider = {
provide: FirestoreDatabaseProvider,
useFactory: (config) => new Firestore(config),
inject: [FirestoreOptionsProvider],
}

return {
global: true,
module: FirestoreModule,
imports: options.imports,
providers: [optionsProvider, dbProvider, ...collectionProviders],
exports: [dbProvider, ...collectionProviders],
}
}
}






Finally, connect the module in app.module.ts




CODE
...
import { ConfigModule, ConfigService } from '@nestjs/config'
...
import { FirestoreModule } from './providers'
...

@Module({
imports: [
FirestoreModule.forRoot({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
keyFilename: configService.get<string>('SA_KEY'),
}),
inject: [ConfigService],
}),
],
...
})






At this stage, we are generally ready to create modules with controllers, repositories, and so on. Here is the to create your Cloud Firestore database. This can be resolved by adding a billing account. If you run firebase init --debug, you can see the specific error.



Here is my error log for initialization due to an inactive billing account:




CODE
":{"code":403,"message":"Read access to project 'nestjs-boilerplate-example' was denied: please check billing account associated and retry","status":"PERMISSION_DENIED"}}
[2024-07-28T18:40:19.841Z] error getting database typeHTTP Error: 403, Read access to project 'nestjs-boilerplate-example' was denied: please check billing account associated and retry {"name":"FirebaseError","children":[],"context":{"body":{"error":{"code":403,"message":"Read access to project 'nestjs-boilerplate-example' was denied: please check billing account associated and retry","status":"PERMISSION_DENIED"}},"response":{"statusCode":403}},"exit":1,"message":"HTTP Error: 403, Read access to project 'nestjs-boilerplate-example' was denied: please check billing account associated and retry","status":403}
[2024-07-28T18:40:19.842Z] database_type: undefined






You can set up a billing account on the page:



After completing all the steps, the following files will be added to your project:




  • .firebaserc

  • firebase.json

  • firestore.indexes.json - list of current indexes, which we edit in this file

  • firestore.rules - contains the rules for working with Firestore. We do not change its value in the project dashboard.



Note: Instead of the .firebaserc file, we keep its example, .firebaserc.example, in the Git repository. How to set up CI/CD for all of this will be covered in the next article about deploying the backend on a VPS.



In the future, we will be mainly interested in .firebaserc (which will contain the Firebase project name) and firestore.indexes.json (where you can configure your indexes and deploy them to the project using the command firebase deploy --only firestore:indexes).



The final set of changes at this stage can be found in for detailed instructions.



In your terminal, run the command pnpm add -D husky, then initialize Husky by running npx husky init. This will add a .husky folder to your project, along with a pre-commit file that will run during the commit stage. Let’s modify the package.json to include a new command in the scripts section.




CODE
...
"scripts": {
...,
"prepare": "node .husky/install.mjs"
}






Next, add the . From now on, all future commits will be validated for JavaScript/TypeScript files using ESLint/Prettier, and fixes will be applied for future commits using the previously added command lint:fix. You can consider this pre-commit hook example as a foundation for your personal configurations. You can view the full list of available Git hooks in



Let's make a few more requests.






List of example documents.





So, we sent a request with a parameter, but we get the same error. In fact, this is normal, but there is a problem. At the moment, our API cannot read and work with boolean values, they are displayed in controllers and services as string values 'true' | 'false'.

If we log the incoming query argument in the GET v1/example controller, we will see the following picture




CODE
{ isPublished: 'false' }






There are several ways to solve this problem.




  1. At the repository level, in the findGenerator method, manually convert the boolean string to the Boolean type.




CODE
// Instead of
if (typeof filter?.isPublished === 'boolean') {
query = query.where('isPublished', '==', filter.isPublished)
}

// Something like this
if (filter?.isPublished) {
const isPublished = filter?.isPublished === 'true' ? true : false
query = query.where('isPublished', '==', isPublished)
}







  1. Add an additional step to transform them



Let's try the second step. Edit the controller method.




CODE
// example/controllers/example.controller.ts
import {
...,
ParseBoolPipe,
...,
} from '@nestjs/common'
...
@Get('/')
async getList(@Query('isPublished', ParseBoolPipe) isPublished?: boolean): Promise<ExampleDocument[]> {
const response = await this.exampleService.getList({ isPublished })
// ...
}
...






Thus, we transform the incoming isPublished parameter into a boolean value.






Creating a new example document





If you look closely, you can see that the list is being fetched correctly. However, for many collections, it's necessary to change directions or guarantee the return of a list, for example, taking into account the creation date by descending/ascending values.




CODE
// repositories/example.repository.ts

...
async find(filter: ExampleFilter): Promise<ExampleDocument[]> {
...
let query = this.findGenerator(filter)

query = query.orderBy('createdAt', 'desc')
...
}
...






After attempting to request the list method again, we will see that the response has changed to a 500 error. If we go to the console, we will see that Firestore has thrown an error about the absence of an index for such a list query.




CODE
9 FAILED_PRECONDITION: The query requires an index. You can create it here: <url>






Feel free to follow this URL to the project console, where you'll see a suggestion to add a new index. Don't rush to add it from there (you can initiate index creation from the console, but I'd prefer to do this through the firestore.indexes.json file)





If we request the list or document by id again, we will also see the changed data.






Changing the isPublished flag



In addition, let's change the value of isPublished in the document by making a request to another endpoint.



. You can download it and import it into your Postman workspace for quick deployment of the request environment.



Another

  • Free Storage provides 5GB of storage, beyond this volume you'll have to pay monthly for each byte of data.

  • The free Spark plan doesn't allow creating separate buckets, but we'll account for this case in the code. However, I recommend using the project's default bucket and resolving the files themselves by folders and subfolders in the code.



  • Use cases for Storage




    • Reading and writing a file to a temporary folder in the project root, /uploads in our case

    • Writing and deleting a file in Storage

    • Generating a public link to the file, if such a need exists (by default, we will always provide a public link, you can rewrite or supplement the necessary piece of code according to your use cases, I will provide a working example)



    Let's get started, we have N new files waiting for us.

    In the providers folder, next to firebase, let's add a new folder - bucket.

    In it, we will create, of course, the bucket.module.ts file and a bunch of auxiliary ones. By the way, I almost forgot, let's install the packages pnpm add @google-cloud/storage multer lodash, and necessarily pnpm add -D @types/multer.



    Proposed structure:




    CODE
    src/providers
    - bucket
    - providers
    default.bucket.ts
    index.ts
    bucket.constants.ts
    bucket.module.ts
    bucket.providers.ts
    bucket.shared.service.ts
    bucket.types.ts
    index.ts
    utils.ts






    Let's describe each file, there will be relatively little code. And we'll add uploading and deleting to a separate endpoint for working with images via api/example/:id/image. Let's start with the auxiliary files.




    CODE
    // default.bucket.ts
    export class DefaultBucketProvider {
    static bucketName = 'default'
    }

    // bucket.constants.ts
    export const getDefaultOptions = (role: string) => ({
    entity: 'allUsers',
    role: role,
    })






    bucket.providers.ts represents the same concept as the firestore.providers.ts file.




    CODE
    // bucket.providers.ts
    import { DefaultBucketProvider } from './providers'

    export const StorageBucketsProvider = 'StorageBucketsProvider'
    export const StorageOptionsProvider = 'StorageOptionsProvider'

    export const StorageBucketProviders: string[] = [DefaultBucketProvider.bucketName]

    // bucket.types.ts
    import { Bucket, Storage } from '@google-cloud/storage'

    export type StorageProps = {
    keyFilename: string
    }

    export type FirestoreModuleOptions = {
    imports: any[]
    useFactory: (...args: any[]) => StorageProps
    inject: any[]
    }

    export type BucketProvider = {
    bucket: Bucket
    storage: Storage
    }






    Our service for working with Bucket.




    CODE
    // bucket.shared.service.ts
    import { Bucket } from '@google-cloud/storage'
    import { Logger } from '@nestjs/common'
    import { extname } from 'path'

    export class BucketSharedService {
    private bucket: Bucket
    private logger: Logger

    constructor(bucket: Bucket, logName?: string) {
    this.bucket = bucket
    this.logger = new Logger(`${BucketSharedService.name}_${logName}`)
    }

    public async isFileExists(name: string) {
    try {
    const fileName = name
    const file = this.bucket.file(fileName)

    const [isExists] = await file.exists()

    return isExists
    } catch (error) {
    throw error
    }
    }

    public async deleteFileByName(path: string, folderPath: string) {
    return new Promise(async (resolve, reject) => {
    const pathes = path?.includes('%2F') ? path?.split('%2F') : path?.split('/')
    const fileName = pathes?.[pathes?.length - 1]
    const file = this.bucket.file(`${folderPath ? `${folderPath}/` : ''}${fileName}`)

    const [isExists] = await file.exists()

    if (!isExists) {
    reject(new Error('File does not exist'))
    }

    /**
    * There are no guarantees that it definitely deletes, but it seems that files become inaccessible for search by their links
    */
    file
    .delete()
    .then((res) => {
    resolve(res)
    })
    .catch((err) => {
    this.logger.error('Error with file bucket removing', err?.message)
    reject(err)
    })
    })
    }

    /**
    * Using with internal upload folders
    */
    public async saveFileByUploadsFolder(definedFile: Express.Multer.File, folderPath?: string): Promise<string> {
    const uniqueSuffix = `${folderPath || 'main'}/${Date.now()}-${Math.round(Math.random() * 1e9)}`
    const fileName = `${uniqueSuffix}${extname(definedFile.path)}`

    return new Promise((resolve, reject) => {
    this.bucket
    .upload(definedFile.path, {
    destination: fileName,
    })
    .then((response) => {
    const [uploadedFile] = response || []
    const file = this.bucket.file(uploadedFile?.metadata?.name)

    file.makePublic(async (err) => {
    if (err) {
    this.logger.error(`Error making file public: ${err}`)
    reject(err)
    } else {
    this.logger.log(`File ${file.name} is now public.`)
    const publicUrl = file.publicUrl()
    this.logger.log(`Public URL for ${file.name}: ${publicUrl}`)
    resolve(publicUrl)
    }
    })

    return true
    })
    .catch((err) => {
    reject(err)
    })
    })
    }

    /**
    * Using without multer storage option, only memory buffer
    */
    public async saveFileByUrlAndBuffer(path: string, folderPath: string, buffer: Buffer): Promise<string> {
    return new Promise(async (resolve, reject) => {
    const uniqueSuffix = `${folderPath || 'main'}/${Date.now()}-${Math.round(Math.random() * 1e9)}`
    const fileName = `${uniqueSuffix}${extname(path)}`
    const file = this.bucket.file(fileName)
    await file.save(buffer)

    file.makePublic(async (err) => {
    if (err) {
    this.logger.error(`Error making file public: ${err}`)
    reject(err)
    } else {
    this.logger.log(`File ${file.name} is now public.`)
    const publicUrl = file.publicUrl()
    this.logger.log(`Public URL for ${file.name}: ${publicUrl}`)
    resolve(publicUrl)
    }
    })
    })
    }
    }






    Adding a module with logic for connecting to Storage.




    CODE
    // bucket.module.ts
    import { Storage } from '@google-cloud/storage'
    import { DynamicModule, Module } from '@nestjs/common'
    import * as fs from 'fs'

    import { getDefaultOptions } from './bucket.constants'
    import { StorageBucketProviders, StorageBucketsProvider, StorageOptionsProvider } from './bucket.providers'
    import { FirestoreModuleOptions, StorageProps } from './bucket.types'

    @Module({})
    export class BucketModule {
    static forRoot(options: FirestoreModuleOptions): DynamicModule {
    const bucketProviders = StorageBucketProviders.map((providerName) => ({
    provide: providerName,
    useFactory: async (storage: Storage) => {
    console.log(storage, 'storage')
    /**
    * Use default bucket name
    */
    const bucket = storage.bucket(providerName === 'default' ? `${storage.projectId}.appspot.com` : providerName)

    const [isExist] = await bucket.exists()

    /**
    * Basic steps to create public bucket with writer rules, available only for BLAZE price
    */
    if (!isExist) {
    const options = getDefaultOptions(storage.acl.WRITER_ROLE)

    await bucket.create().catch((err) => console.error(`bucket ${providerName} creation get error`, err))

    console.info(`bucket ${providerName} created successfully`)

    bucket.acl.add(options, (err) => {
    if (!err) {
    console.info(`acl added successfully to ${providerName} bucket`)
    } else {
    console.error(`bucket ${providerName} error`, err)
    }
    })
    }

    return { bucket, storage }
    },
    inject: [StorageBucketsProvider],
    }))

    const optionsProvider = {
    provide: StorageOptionsProvider,
    useFactory: options.useFactory,
    inject: options.inject,
    }

    const provider = {
    provide: StorageBucketsProvider,
    useFactory: (config: StorageProps) => {
    const serviceAccount: { project_id?: string } = JSON.parse(fs.readFileSync(config.keyFilename, 'utf8'))

    return new Storage({ ...config, projectId: serviceAccount.project_id ?? '' })
    },
    inject: [StorageOptionsProvider],
    }

    return {
    global: true,
    module: BucketModule,
    imports: options.imports,
    providers: [optionsProvider, provider, ...bucketProviders],
    exports: [provider, ...bucketProviders],
    }
    }
    }






    Next, we need configuration for uploading files from the API to the temporary folder /uploads.




    CODE
    // utils.ts
    import { diskStorage } from 'multer'
    import { extname } from 'path'

    export const storage = diskStorage({
    destination: './uploads',
    filename: (_, file, cb) => {
    // Proposed file name regeneration
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`
    cb(null, `${uniqueSuffix}${extname(file.originalname)}`)
    },
    })






    The constant storage will be needed to avoid overloading memory when working with uploaded images. If diskStorage is not used, we may encounter a shortage of RAM as traffic grows.




    CODE
    // index.ts - for short imports
    export * from './bucket.module'
    export * from './bucket.shared.service'
    export * from './bucket.types'
    export * from './providers'
    export * from './utils'






    To successfully launch the bucket module, it needs to be imported into app.module.ts.




    CODE
    // app.module.ts
    ...

    @Module({
    imports: [
    ...
    BucketModule.forRoot({
    imports: [ConfigModule],
    useFactory: (configService: ConfigService) => ({
    keyFilename: configService.get<string>('SA_KEY'),
    }),
    inject: [ConfigService],
    }),
    ],
    ...
    })
    export class AppModule {}






    Let's add a helper for file validation. In it, we will describe the allowed file extensions and sizes.




    CODE
    - helpers
    ...
    - fileValidation
    constants.ts
    utils.ts
    index.ts









    CODE
    // constants.ts
    export const listDefaultImageExt = 'image/png,image/jpeg,image/webp'

    export const listPngAndJpegImageExt = 'image/png,image/jpeg'

    export const IMG_MAX_SIZE_IN_BYTE = 716800 // 700kb

    export const IMG_MAX_1MB_SIZE_IN_BYTE = 1048576 // 1mb

    export const IMG_MAX_5MB_SIZE_IN_BYTE = 1048576 * 5 // 1mb

    // utils.ts
    import { reduce } from 'lodash'

    export const getFileTypesRegexp = (ext: string): string => reduce(ext.split(','), (acc, key) => `${acc}|${key}`)






    Now let's focus on the controller and the image handling method, along the way, we will add a new parameter to example.document.ts and example.repository.ts, detailed changes can be viewed in the commit at the end of this section.




    CODE
    // example/controllers/example.controller.ts
    ...

    @Post('/:id/image')
    @UseInterceptors(FileInterceptor('file', { storage, limits: { files: 1 } }))
    async updateExampleImage(
    @UploadedFile(
    new ParseFilePipe({
    validators: [
    new MaxFileSizeValidator({ maxSize: IMG_MAX_1MB_SIZE_IN_BYTE }),
    new FileTypeValidator({ fileType: getFileTypesRegexp(listPngAndJpegImageExt) }),
    ],
    }),
    )
    file: Express.Multer.File,
    @Param('id') id: string,
    ): Promise<ExampleDocument> {
    return this.exampleService.updateImage(id, file)
    }
    ...









    CODE
    // example/services/example.service.ts
    ...
    export class ExampleService {
    private bucketService: BucketSharedService

    constructor(
    private readonly exampleRepository: ExampleRepository,
    @Inject(DefaultBucketProvider.bucketName)
    private readonly bucketProvider: BucketProvider,
    ) {
    this.bucketService = new BucketSharedService(this.bucketProvider.bucket, ExampleService.name)
    }
    ...
    public async updateImage(id: string, file: Express.Multer.File) {
    try {
    const { doc, data } = await this.exampleRepository.getUpdate(id)

    if (!doc || !data) {
    throw new NotFoundException('Example document does not exist')
    }

    const imageUrl = await this.bucketService.saveFileByUploadsFolder(file, `example/${data?.id}`)

    // It is possible to add deletion through an additional check if (data?.imageUrl, but in general this is not necessary, for the case when the file is missing, we definitely wrap the delete method call in a try-catch block)
    try {
    /**
    * Try to remove previously file
    */
    await this.bucketService.deleteFileByName(data?.imageUrl, `example/${data?.id}`)
    } catch {}

    const response = this.exampleRepository.getValidProperties({ ...data, imageUrl }, true)

    await doc.update({ imageUrl, updatedAt: response?.updatedAt })

    /**
    * Need for deletion uploads file by file.path
    */
    fs.unlinkSync(file.path)

    return response
    } catch (error) {
    /**
    * Need for deletion uploads/ file path
    */
    fs.unlinkSync(file.path)

    throw error
    }
    }
    ...
    }






    In general, everything is fine with file uploads. We have added a simple example of uploading an image to the example document. Let's make a request and check that everything works correctly.



    .






    Conclusion



    I hope that with this article we have achieved the intended goal of describing the minimum project configuration with an example of code structure, working with Firestore and GCloud Bucket. The full example of a NestJS project can be found on my GitHub. I hope that I have been able to clearly and with a sufficient amount of code describe the basic steps for generating a CRUD application. I would not like to stop at this article. I will be glad to receive feedback and criticism. The final repo can serve as a not bad example for your quick start in developing an MVP or pet project on Nest.js integrated with Firebase or any other PaaS solution.



    In the next articles, we will return to this boilerplate and try to do more:




    • Implement methods for working with authorization and authentication in Firebase in a Nest.js API (Based on this article and the current example project).

    • Add Swagger for convenient viewing of contracts and API testing.

    • Dive a little into deploying the resulting backend application and set up notifications in the team chat Telegram.

    • Try to write a Telegram Bot based on the nestjs-startup-boilerplate.

    • Create a Mini-App in conjunction with the resulting Telegram bot.

    Vollständiger Original-Artikel
    Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
    ↗ Original-Artikel auf dev.to lesen
    Wie bewertest du diesen Beitrag?
    1 Klick Feedback
    Teilen mit Netzwerk & Team:

    Community-Analysen & Experten-Meinungen 0

    Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
    Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
    Community Pulse: Relevanz-Einschätzung
    1 Klick Experten-Votum
    🔴 Akute Relevanz 0%
    🟡 In Evaluierung 0%
    🟢 Keine Auswirkung 0%
    Spannende Innovation 0%
    Verwandte Story-Cluster & Quellen (Vektor-KI)
    Port 8095 Engine
    1 Quelle
    Microsoft Brings Windows 11 Auto SR to Intel Core Ultra Series 3-Based PCs
    1 Quelle
    Affinity Ships a Major Update With Over 60 New Features
    1 Quelle
    Nutzer von Google Fotos dürfen sich auf großes Update freuen
    Ähnliche Beiträge
    🔍 Verwandte News

    Auch interessante Nachrichten Nestjs, Firebase, GCloud. How to Quickly Set Up an API Backend in TypeScript.

    Thematisch verwandte Begriffe: Nestjs, Firebase, GCloud, Quickly · 6 Treffer

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...