Why Do You Need This? What Is The Problem FireCall Trying To Solve?
Read Here
Read Here
npm i firecaller firebase zod
and of course you need typescript
.
Normally this file is created on backend and share to frontend.
Tips: You can also use these schemas to validate your form, learn more at zod!
import { z } from 'zod'
export const updateUserSchema = {
//request data schema
req: z.object({
name: z.string(),
age: z.number(),
address: z.string(),
}),
// response data schema
res: z.undefined(),
// function name
name: 'updateUser',
}
export const getUserSchema = {
res: z.string(), // userId
res: z.object({
name: z.string(),
age: z.number(),
}),
name: 'getUser',
}
import { initializeApp } from 'firebase/app'
import { callable } from 'firecaller'
import { updateUserSchema, getUserSchema } from './someFile'
export const app = initializeApp(yourConfig) // must initialize app before using firecaller
const funRef = getFunctions(app)
// now create the specific callable
export const updateUser = callable(updateUserSchema) // or callable(updateUserSchema, funRef)
export const getUser = callable(getUserSchema) // or callable(getUserSchema, funRef)
FireCaller never throw, all errors are caught and returned as object. We choose this pattern because it is impossible to type-safe rejected promise.
By checking the value of the code
, you know how to deal with them:
code | meaning |
---|---|
ok | success, you can access the data value |
schema-out-of-sync | Incorrect response data shape, your schema is out of sync, you can access the message |
'functions/cancelled', 'functions/unknown', 'functions/invalid-argument', 'functions/deadline-exceeded', 'functions/not-found', 'functions/already-exists', 'functions/permission-denied', 'functions/resource-exhausted', 'functions/failed-precondition', 'functions/aborted', 'functions/out-of-range', 'functions/unimplemented', 'functions/internal', 'functions/unavailable', 'functions/data-loss', 'functions/unauthenticated' | the error source is FireCall in NodeJS, you can access the message . |
import { updateUser, getUser } from './someOtherFile'
const { name, age, address } = someFormData()
updateUser(
// input type depends on schema.req
{ name, age, address } // { name: string, age: number, address: string }
).then(res => {
const { code } = res
if (code === 'ok') {
const data = res.data // data type depends on what you define in schema.res
} else {
const { code, message } = res
// message is string
}
})
import { initializeApp } from 'firebase/app'
import { getFunctions, connectFunctionsEmulator } from 'firebase/functions'
import { callable } from 'firecaller'
import { z } from 'zod'
const app = initializeApp({ projectId: `### YOUR_PROJECT_ID` })
const functions = getFunctions(app)
connectFunctionsEmulator(functions, 'localhost', f.emulators.functions.port)
const schema = {
req: z.string(),
res: z.string(),
name: 'hello',
}
const helloCallable = callable(schema, functions)
describe('test callable', () => {
it('success', async () => {
const result = await helloCallable('hello')
expect(result.code).toBe('ok')
expect(result.data).toEqual('hello')
})
it('invalid arguments', async () => {
// @ts-expect-error
const result = await helloCallable(123) // wrong input type
expect(result.code).toBe('functions/invalid-argument')
expect(result.message).toEqual('invalid-argument')
})
})