Flame Blog
Como acionar manualmente Firebase Functions no emulador
Um guia completo para chamar funções HTTP, callable, Pub/Sub, Cloud Tasks e agendadas no emulador Firebase.
Publicado em 24 de abril de 2026
Por Val — Artigo original escrito em inglês
O emulador Firebase é capaz de executar suas Firebase Functions, sejam HTTP, callable, Pub/Sub, filas de tarefas ou funções agendadas.
Funciona out of the box quando você as chama programaticamente do seu app (exceto para funções agendadas que nunca rodam localmente), mas quando se trabalha em uma função, geralmente é mais fácil chamá-la diretamente, especialmente para as que são acionadas por ações não triviais no seu app.
A interface do emulador em localhost:4000 tem um painel admin básico para Firebase Auth, Firestore e Cloud Storage, mas não para Functions. Apenas este triste painel com logs:
Neste guia, veremos como acionar manualmente:
Chamando funções com cURL
Para resolver isso, cURL é nosso amigo.
Com o emulador Functions rodando na porta padrão 5001, sua chamada típica será assim:
curl -X POST 'http://localhost:5001/{project}/{region}/{function}' \
-H 'Content-Type: application/json' \
-d '{data}'
Onde region é por exemplo us-central1 (padrão), e function é o nome da sua função.
Então se seu projeto é my-project e sua função é definida como:
import * as functions from 'firebase-functions/v2'
export const httpFunction = functions.https.onRequest((request, response) => {
response.json({ ok: true })
}) import * as functions from 'firebase-functions/v1'
export const httpFunction = functions.https.onRequest((request, response) => {
response.json({ ok: true })
}) Sua URL será:
http://localhost:5001/my-project/us-central1/httpFunction
E com uma região customizada:
export const httpFunction = functions.https.onRequest(
{ region: 'europe-west1' },
(request, response) => {
response.json({ ok: true })
}
) export const httpFunction = functions
.region('europe-west1')
.https.onRequest((request, response) => {
response.json({ ok: true })
}) Obtemos:
http://localhost:5001/my-project/europe-west1/httpFunction
Agora o payload JSON será diferente dependendo do tipo de função, e em alguns casos, dependendo de se você usa funções 1st gen ou 2nd gen.
Funções HTTP
Esta é a mais fácil porque você controla o payload inteiramente, então apenas passe seu payload diretamente no body HTTP e você o receberá na função.
Exemplo de função:
export const httpFunction = functions.https.onRequest((request, response) => {
console.log({ url: request.url, body: request.body })
response.json({ ok: true })
})
Podemos chamá-la assim:
curl -X POST 'http://localhost:5001/my-project/us-central1/httpFunction' \
-H 'Content-Type: application/json' \
-d '{"foo": "bar"}'
Isso vai logar:
{
url: '/',
body: {
foo: 'bar'
}
}
Também podemos chamá-la com um caminho customizado:
curl -X POST 'http://localhost:5001/my-project/us-central1/httpFunction/hello' \
-H 'Content-Type: application/json' \
-d '{"foo": "bar"}'
O que vai logar:
{
url: '/hello',
body: {
foo: 'bar'
}
}
Isso é idêntico tanto para funções 1st gen quanto 2nd gen.
Funções callable
Elas são definidas ligeiramente diferente em 1st e 2nd gen, mas são chamadas da mesma forma.
export const callableFunction = functions.https.onCall(request => {
console.log({ data: request.data, auth: request.auth })
return { ok: true }
}) export const callableFunction = functions.https.onCall((data, context) => {
console.log({ data, auth: context.auth })
return { ok: true }
}) Chamada não autenticada
curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/callableFunction' \
-H 'Content-Type: application/json' \
-d '{"data": {"foo": "bar"}}'
Vai logar:
{
data: {
foo: 'bar'
},
auth: undefined
}
Chamada autenticada com ID token
Também podemos passar um header Authorization: Bearer. Ele recebe um Firebase Auth ID token que será automaticamente parseado e validado pelo SDK de functions.
Aqui está nossa chamada cURL com esse token:
curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/callableFunction' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {firebaseAuthIdToken}' \
-d '{"data": {"foo": "bar"}}'
O que vai logar:
{
data: {
foo: 'bar',
},
auth: {
uid: '...',
token: { name, email, ... },
rawToken: '...'
}
}
Chamada autenticada com token arbitrário
Se por alguma razão você quiser usar seu próprio token não-Firebase, isso também funciona:
curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/callableFunction' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer my-token' \
-d '{"data": {"foo": "bar"}}'
{
data: {
foo: 'bar',
},
auth: {
token: {},
rawToken: 'my-token'
}
}
Funções Pub/Sub
Aqui cobriremos a chamada direta de funções Pub/Sub, com um payload compatível com o que o emulador Pub/Sub enviaria de outra forma.
Se você quiser publicar manualmente mensagens no emulador Pub/Sub, confira nosso guia dedicado para chamar o emulador Pub/Sub.
export const pubsubFunction = functions.pubsub.onMessagePublished(
{ topic: 'my-topic' },
event => {
// `event.data.message.json` é uma propriedade computada então precisamos logá-la separadamente
console.log({ event, json: event.data.message.json })
}
) export const pubsubFunction = functions.pubsub
.topic('my-topic')
.onPublish(message => {
// `message.json` é uma propriedade computada então precisamos logá-la separadamente
console.log({ message, json: message.json })
}) Por alguma razão na URL do emulador, as funções Pub/Sub são sufixadas com -0, então neste caso a URL será:
http://127.0.0.1:5001/my-project/us-central1/pubsubFunction-0
Podemos chamá-la com:
curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/pubsubFunction-0' \
-H 'Content-Type: application/json' \
-d '{"data": {"message": {"data": "eyJmb28iOiJiYXIifQ=="}}}' curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/pubsubFunction-0' \
-H 'Content-Type: application/json' \
-d '{"data": {"data": "eyJmb28iOiJiYXIifQ==", "attributes": {}}}' O payload Base64 em data acima é o mesmo exemplo {"foo": "bar"} de antes:
Buffer.from(JSON.stringify({ foo: 'bar' })).toString('base64')
// 'eyJmb28iOiJiYXIifQ=='
Para conveniência, podemos embutir a codificação Base64 no mesmo comando:
curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/pubsubFunction-0' \
-H 'Content-Type: application/json' \
-d '{"data": {"message": {"data": "'$(echo '{"foo": "bar"}' | base64)'"}}}' curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/pubsubFunction-0' \
-H 'Content-Type: application/json' \
-d '{"data": {"data": "'$(echo '{"foo": "bar"}' | base64)'", "attributes": {}}}' A função vai logar:
{
event: {
data: {
message: {
data: 'eyJmb28iOiJiYXIifQ==',
publishTime: '2026-04-20T00:00:00.000Z'
}
}
},
json: {
foo: 'bar'
}
} {
message: {
data: 'eyJmb28iOiJiYXIifQ==',
attributes: {}
},
json: {
foo: 'bar'
}
} Funções de fila de tarefas
export const taskQueueFunction = functions.tasks.onTaskDispatched(
{
retryConfig: {
maxAttempts: 5,
minBackoffSeconds: 60,
},
rateLimits: {
maxConcurrentDispatches: 6,
},
},
async request => {
console.log({ data: request.data })
}
) export const taskQueueFunction = functions.tasks
.taskQueue({
retryConfig: {
maxAttempts: 5,
minBackoffSeconds: 60,
},
rateLimits: {
maxConcurrentDispatches: 6,
},
})
.onDispatch(async data => {
console.log({ data })
}) Tanto em 1st quanto em 2nd gen, precisamos chamar a função com uma propriedade data no body.
curl -X POST 'http://localhost:5001/my-project/us-central1/taskQueueFunction' \
-H 'Content-Type: application/json' \
-d '{"data": {"foo": "bar"}}'
O que vai logar:
{
data: {
foo: 'bar'
}
}
Isso chama a fila de tarefas diretamente. Se você estiver interessado em invocar o emulador Cloud Tasks, confira nosso guia do emulador Cloud Tasks.
Funções agendadas
export const scheduledFunction = functions.scheduler.onSchedule(
{
schedule: 'every 30 minutes',
timeZone: 'UTC',
},
event => {
console.log({ event })
}
) export const scheduledFunction = functions.pubsub
.schedule('every 30 minutes')
.timeZone('UTC')
.onRun(context => {
console.log({ context })
}) Assim como as funções Pub/Sub, estas são sufixadas com -0 por alguma razão (ainda tenho que descobrir em que condições vemos um sufixo -1).
Funções agendadas não recebem nenhum payload, apenas são acionadas.
curl -X POST 'http://127.0.0.1:5001/my-project/us-central1/scheduledFunction-0' \
-H 'Content-Type: application/json' \
-d '{}'
Isso vai logar:
{
event: {
scheduleTime: '2026-04-20T00:00:00.000Z',
}
} {
context: {
// Isso é encaminhado do body mas não tenho certeza do que representa em produção
params: {
}
}
} Outros triggers
Estou omitindo intencionalmente triggers de Auth, Firestore, Realtime Database, Cloud Storage e outros, devido ao seu acoplamento estreito com o serviço subjacente.
É melhor testá-los realizando a ação real que os aciona.
Se você tem um caso de uso para acionar manualmente alguns desses, tenho curiosidade de saber! Me avise em val@evetools.app.
Como Flame pode ajudar ✨
Tudo isso funciona, mas e se localhost:4000 tivesse uma interface de functions que lida com todos os detalhes subjacentes destacados neste guia?
É por isso que estamos construindo o Flame, uma interface de usuário para o emulador Firebase com tudo o que falta em localhost:4000.
Veja todas as suas funções e chame-as com um único clique:
Ajuste o body, copie como cURL com toda a codificação tratada, e chame quantas vezes quiser da interface.
Sem mais lutas com terminal, sem memorizar padrões de URL, sem codificação Base64 manual. Flame lida com funções HTTP, callable, Pub/Sub, filas de tarefas e funções agendadas, tudo da mesma interface.
Também vem com uma interface melhorada para Firebase Auth, Firestore, e mais.
Se isso parece interessante para você, experimente o Flame!