Skip to content

n8n workflow

Nicolas Martinez edited this page Mar 24, 2025 · 12 revisions

We are using n8n open source workflow automation framework to power the integration to get a recipe from Google Gemini AI. We plan to extend it to integrate with different sources where users keep their fridge or pantry inventory, such as Google Keep.

We are using existing instance deployed on Docker container in a local Synology NAS, and building off learnings from that previous project.

Project Setup

Set up n8n workflow by creating a project Feed Me Now

Git setup

Set up Git source control as a single workflow instance.

image

Pushed initial commit. This created three folders:

image

Note: n8n commits the following to Git:

  • Workflows, including their tags and the email address of the workflow owner. You can choose which workflows to push.
  • Credential stubs (ID, name, type) After testing, sensitive information like API key does not get saved
  • Variable stubs (ID and name)
  • Projects

N8N Workflow Design

The System

Workflow

The workflow would potentially work with paid Generative AI's and therefore it would be critical to set up authentication in order to prevent others from connecting to the n8n for their personal needs. JWT signed token is passed into the webhook, where both the server and the workflow use the same JWT secret code. Then the decoded prompt that came with the token gets passed to the Gemini API through the POST method and an ugly json string gets returned. Note that most Generative AI nodes are followed by a Code node to fit the outputs into a JSON object, to be assembled into an even bigger JSON object, to be returned. The entire process is called "Prompt Chaining", which returns more consistent shape of the output, although suffers from making the process expensive. Without AI the system would simply not function.

Converting the Prompt into a Polished List

This is the first part of the workflow and it's responsible for converting the requested prompt of ingredients into a clean and distinguishable list of ingredients

image

If the POST request sent has the correct API key then the prompt gets decoded and passed into AI prompt to change it into a list of food items, removing the items that are not food items, checking to throw an error if the generated list is empty, and merged with the user preferences that were also passed to the webhook.

Generate Names of Dishes

Now that there is a definite list of ingredients the two branching out code nodes from a conditional node generate two types of prompts. One generates a prompt with a list of ingredients and applies all the user settings like Cuisines, Foods to avoid, Diets, number of servings. The other generates a prompt to ask AI to give silly, unrealistic and most importantly, dishes that can't really be made, because this node gets executes when the generative AI decides that the list of food items, taken from the user prompt, shall be empty.

image

Restricting the workload

The next two nodes limit the number of dishes to be used in generating recipes and other information for them, and map the names of dishes to the copies of the ingredients and settings for having the next big section of the workflow, process and generate full recipes content.

image

Making each Dish

This is perhaps the core section of the entire workflow and contains the current tactic to prompt chain the responses in the most optimal way. This method was created to generate small pieces of the bigger picture, which is the recipe object, separately and merge them together.

image

"Generate Measured Ingredients" Node takes the available list of of ingredients, if there are no item measurements, and the AI adds the required amount of each ingredient for the specific dish with the specific serving size, all passed from one node.

"Generate Cooking Instructions" Node takes the new list of ingredients along with the current dish name, the loop is is working with, and generates step by step guide on how to cook that specific dish.

"Generate Cousine, Serving Size, Cooking Time" Node takes the new list of ingredients along with all other available information and applies the user settings for selected cuisines, serving size, cooking time and returns the string that has 4 elements in the following order [cooking time in minutes, serving size of people, cuisine name, total calories]. The next node converts it into a JSON object that has these 4 keys.

Finalizing the Output

After the loop iterates through all the dish names, the following collection of nodes get executes. The workflow checks the condition of the JSON object that has been a collection of the previously merged JSON objects. It checks if the Generative AI has returned any errors along the way and whether any key is null. Then it finally returns the complete recipe JSON object array.

image

Webhook

webhook config

The Webhook needs to be a POST request as it accepts data with the method. It's set up to only accept connection with the API key that's passed in the header of the request from the server. Most importantly, it needs to have the "Using 'Respond to Webhook' Node" Respond setting as it would only respond when it executes through the entire workflow, including the wait time for Gemini API to generate the recipes.

JWT token

JWT secret is most commonly generated by the following command with npm:

node -e "console.log(require('crypto').randomBytes(32).toString('hex')) thanks to this article

Both n8n Workflow and Express server "know" the same JWT secret, where the server signs it and n8n workflow decodes it.

Gemini Flash/Pro API

Gemini has an issue where the json output it provides can be quite inconsistent in terms of generating the data that is later interpreted as either a string or an array. In order to deal with Gemini's outputs there are some strong prompt requests we must do to Gemini. Here is an example from the very first generative AI node:

{"contents":[{"parts":[{"text":"Put these items in a neat list separated by '|' symbol, and return ONLY the list. 
Remove items that can't be used in cooking for human consumption and remove items if they are in this list of 
\"Not Eating\" foods [{{ $json.payload.settings.notEating.join(" , ") }}] and fix possible misspellings. Here 
is the list of ingredients: [{{ $json.payload.prompt.replace(/\n/g, ' '); }}]. Return only the word 'nothing', 
if the user specifically asked for nothing"}]}], 
"generationConfig": {
    "temperature": 1,
    "topK": 40,
    "topP": 0.95,
    "maxOutputTokens": 8192,
    "responseMimeType": "text/plain"
  }
} 

The returned string will most of the time contain the ingredients separated by '|' symbol, so that the next node can easily create an array object from this prompt and efficiently work with it by passing it's values to be processed.

Note: N8N does not have Google Gemini's API connection node, therefore in order to connect to Gemini HTTP request method is possible.

Output Sanitization

This is the node that's being executed right after the first Generative AI node.

Here is it's the sourcecode:

//Take the object from generative AI node output
const rawtext = $input.all()[0].json.candidates[0].content.parts[0];

//Turn the output of polished list items that are separated by '|' and turn it into an array.
rawtext.ingredients = rawtext.text.replace(/(\W)(?<![| ])/gm, "").split("|").map(i => i.trim());

delete rawtext.text;

return rawtext;

The output becomes an object that contains the array of ingredients, which is easy to work with.

Reference

How to Git commit from n8n

  1. Click on Push option in context menu option in the workflow instance:
image

Or using in menu bar:

image
  1. Select workflow, enter commit message, and click Commit and push:
image
  1. Notice the changes show up as a new commit eg. https://github.com/nicmart-dev/feedmenow/commit/88a21fc8127bda7730b03a6ce980f4b366139cd8

Clone this wiki locally